当前位置: 首页 > Linux

老同学谈IoC

时间:2023-04-06 21:46:56 Linux

??IoC,Spring的核心概念之一,确实是家常便饭。但今天!重温一遍后,想谈谈我对IOC的一些看法。IoC-InversionofControl,控制反转。想要了解IoC,还是要从它本身开始。首先,在控制方面,控制就是对谁的控制——是对对象的控制。其次,什么是inversion的inversion或者为什么叫inversion——就是objectcontrol的inversion。对象控制,传统的方式是程序员通过new关键字生成一个对象,然后程序员根据程序逻辑人为控制对象的使用。从这里,您可以很好地理解什么是控制反转。所谓控制反转,就是将原本掌握在程序员手中的创建和管理对象的权限交给了SpringIoC容器。也就是说,控制反转就是让程序员对对象的控制权转移,而在Spring中的实现是SpringIoC容器通过Xml或注解的描述来生成或获取对象,然后由IoC容器管理这些bean。因此,要理解IoC(InversionofControl),你只需要记住谁在将控制权反转给谁。IoC容器顶级IoC容器接口——BeanFactory对于BeanFactory来说,它的重要性来自于所有的IoC容器都直接或间接地派生自它。虽然它的功能不是很强大,但是从它的源码中还是可以看出很多端倪的。publicinterfaceBeanFactory{/**工厂bean的前缀,用于判断是获取到的FactoryBean还是FactoryBean生成的实例,下面会详细说明**/StringFACTORY_BEAN_PREFIX="&";/**通过名字获取bean**/ObjectgetBean(Stringname)throwsBeansException;/**通过名称和类类型获取Bean**/TgetBean(Stringname,@NullableClassrequiredType)throwsBeansException;/**通过名称和构造参数,即可以指定调用某个构造函数来获取Bean**/ObjectgetBean(Stringname,Object...args)throwsBeansException;/**通过类类型获取Bean**/TgetBean(ClassrequiredType)throwsBeansException;/**通过Class类型和构造参数,还可以指定调用某种构造方法获取Bean**/TgetBean(ClassrequiredType,Object...args)throwsBeansException;/**返回一个被ObjectProvider包裹的Bean**/ObjectProvidergetBeanProvider(ClassrequiredType);ObjectProvidergetBeanProvider(ResolvableTyperequiredType);/**通过名称判断是否在容器中有这个Bean**/booleancontainsBean(字符串名称);/**是否是单例**/booleanisSingleton(Stringname)throwsNoSuchBeanDefinitionException;/**是否是原型**/booleanisPrototype(Stringname)throwsNoSuchBeanDefinitionException;/**类型匹配否**/booleanisTypeMatch(Stringname,ResolvableTypetypeToMatch)throwsNoSuchBeanDefinitionException;booleanisTypeMatch(Stringname,@NullableClasstypeToMatch)throwsNoSuchBeanDefinitionException;/**通过名称查找Bean的类类型**/@NullableClassgetType(字符串名称)抛出NoSuchBeanDefinitionException;/**获取这个Bean以外的别名**/String[]getAliases(Stringname);}FACTORY_BEAN_PREFIX在Spring中,有一个接口叫FactoryBean,这个类有一个TgetObject()throwsException;这样的一个方法,这个方法会返回一个对象实例对于这个接口的实现类,BeanFactory的getBean()返回的Bean是实现类本身的实例,还是getObject()返回的实例取决于没有字首。是的,返回FactoryBean;否,返回getObject()返回的实例。/**举一个简单的例子来实现这样一个FactoryBean,并使用这样一个FactoryBean来创建我们需要的User**/@Component("user")publicclassUserFactoryBeanimplementsFactoryBean{@AutowiredprivateUseruser;@OverridepublicUsergetObject()throwsException{返回用户;}@OverridepublicClassgetObjectType(){returnuser.getClass();}}//测试方法publicstaticvoidtest1(){ApplicationContextctx=newAnnotationConfigApplicationContext(UserConfig.class);//不带前缀//获取User的返回值getObject()throwsExceptionUseruser=(User)ctx.getBean("user");System.out.println(用户);//带前缀//获取UserFactoryBean实例UserFactoryBeanuserFactoryBean=(UserFactoryBean)ctx.getBean("&user");System.out.println(userFactoryBean);}这里只是一个简单的例子来说明FACTORY_Bean_PREFIX的作用,FactoryBean更具体的用法可以参考工厂模式中工厂的作用。ObjectProvider这是spring4.3之后才出现的接口。它的主要作用是解决注入时Bean不存在或者存在多个Bean时出现的异常情况。//getIfAvailable()可以解决容器中没有userDao时的异常publicclassUserService{privateUserDaouserDao;publicUserService(ObjectProviderdao){userDao=dao.getIfAvailable();}}//5.1之后可以流式处理Handle,解决容器中存在多个userDao的情况publicclassUserService{privateUserDaouserDao;publicUserService(ObjectProviderdao){userDao=dao.orderedStream().findFirst().orElse(null)}}核心容器——ApplicationContext学过Spring的人对ApplicationContext都不陌生。它是BeanFactory的子(准确的说应该是孙子)接口之一,而我们使用的SpringIoC容器大多是ApplicationContext的实现类。Spring的源码庞大复杂,所以建议当学习,从几个关键的类入手,分析它们的继承和扩展关系,横向扩展你对Spring的理解。这里,我们不再一一解释ApplicationContext的各个继承接口。API文档里都有:ApplicationContext。对于ApplicationContext容器,更侧重于它的应用介绍,即如何通过这个容器获取bean。我们通过一个简单的例子来看一下://OrdinaryJavaBeanpublicclassUser{privateLongid;私有字符串名称;私人年龄;/**getter,setter,toString**//配置类,以注解的形式配置Bean@ConfigurationpublicclassUserConfig{@Bean(name="user")publicUsergetBeanUser(){Useruser=new用户();user.setId(1L);user.setName("klasdq1");user.setAge(18);返回用户;}}//测试类publicclassIocTest{publicstaticvoidmain(String[]args){ApplicationContextctx=newAnnotationConfigApplicationContext(UserConfig.class);Useruser=ctx.getBean("user");//System.out.println(user);}}@Configuration@Configuration注解的作用是它标记的类有一个或多个@Bean修饰的方法,这些方法会被Spring容器处理,然后用来GenerateBean或者服务请求。//@Configuration源代码@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic@interfaceConfiguration{@AliasFor(annotation=Component.class)Stringvalue()default"";booleanproxyBeanMethods()defaulttrue;}从注解的源码可以看出,有两个;一个是value,用于为配置类声明一个具体的Bean名称。第二个是proxyBeanMethods,用来指定@Bean修饰的方法是否可以被代理。@Bean这个注解只用在Spring容器管理生成bean的方法上。@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceBean{@AliasFor("name")String[]value()default{};@AliasFor("value")String[]name()default{};/**@deprecated*/@DeprecatedAutowireautowire()defaultAutowire.NO;布尔autowireCandidate()默认为真;StringinitMethod()默认"";StringdestroyMethod()default"(inferred)";}@Bean最重要的参数是名称(value),意思是为Bean声明一个具体的名称。一个Bean可以有多个名称,这就是BeanFactory中有getAliases()方法的原因。其他的参数,看名字就知道用意了,就不多解释了。AnnotationConfigApplicationContext这是ApplicationContext类的具体实现类之一,用于以注解形式生成bean。相应的,ClassPathXmlApplicationContext从XML文件中获取bean。Spring中的Bean组装允许我们通过XML或配置文件来组装Bean,但是在SpringBoot中通常使用注解。为了方便SpringBoot的开发,不再使用XML的形式。直接看例子://ConfigureJavaBean@Component("klasdq2")publicclassUser{@Value("2")privateLongid;@Value("klasdq2")私有字符串名称;@Value("19")私人年龄;/**getter,setter,toString**//Configuration类扫描程序集Bean@Configuration@ComponentScanpublicclassUserConfig{}//测试类publicclassIocTest{publicstaticvoidmain(String[]args){ApplicationContextctx=新的AnnotationConfigApplicationContext(UserConfig.class);用户user=(User)ctx.getBean("klasdq2");System.out.println(user);}@Component@Component的源码很简单:@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Indexedpublic@interfaceComponent{Stringvalue()default"";}参数中只有一个值,用于声明Bean的名称(身份)。这里有一个新的注解@Indexed。顾名思义,这个注解就是添加一个索引。这是因为在SpringBoot中大量的bean以扫描的形式组装后,扫描的bean越多,解析的时间就越长。为了提高性能,5.0版本引入了这样的注解。@Value@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceValue{Stringvalue();}这个注解可以用在字段和methods,方法参数和注释,将表达式或特定字符串传递到相应的值中。@Value是一个非常强大的注解,建议多了解一下。它的功能主要有以下几个:注入普通字符串,编写SpEL表达式,如:@Value("#{person.name}"),可以从配置文件、Bean属性、调用方法等获取数据。注入Resource,如:@Value("classpath:com/demo/config.txt")使用Resource类型接收注入的URL资源,如:@Value("http://www.baidu.com")使用Resourcetypetoreceive@ComponentScan@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Repeatable(ComponentScans.class)public@interfaceComponentScan{/***这个参数是最常用的ComponentScan注解,和它的作用是声明要扫描什么Package,*通过扫描,将带有@Componet注解的Bean加载到Spring容器中。*值与basePackages作用相同,其默认值为配置类所在的包及其子包。**/@AliasFor("basePackages")String[]value()default{};@AliasFor("value")String[]basePackages()默认{};/**要扫描哪些类**/Class[]basePackageClasses()default{};/**BeanNamegenerator:自定义bean命名生成规则**/ClassnameGenerator()defaultBeanNameGenerator.class;/**作用域解析器**/类scopeResolver()defaultAnnotationScopeMetadataResolver.class;/**范围代理**/ScopedProxyModescopedProxy()defaultScopedProxyMode.DEFAULT;/**资源匹配方式,默认为.Class**/StringresourcePattern()default"**/*.class";/**是否开启默认过滤器(源码下方的自定义过滤器)**/booleanuseDefaultFilters()defaulttrue;/**满足过滤条件的组件只会Scan**/ComponentScan.Filter[]includeFilters()default{};/**满足过滤条件的组件将不会被扫描**/ComponentScan.Filter[]excludeFilters()default{};/**是否开启延迟加载**/booleanlazyInit()defaultfalse;/**过滤器**/@Retention(RetentionPolicy.RUNTIME)@Target({})public@interfaceFilter{/**可以根据注解类型或者正则表达式进行过滤**/FilterTypetype()defaultFilterType.ANNOTATION;/**过滤哪些类**/@AliasFor("classes")Class[]value()default{};@AliasFor("value")Class[]classes()default{};/**匹配方式**/String[]pattern()default{};}}例如:@ComponetScan(basePackages="com.klasdq.sb.service.*",excludeFilters=(@Filter(classes="UtilService.Class")))在这样的例子中,basePcakages指定扫描服务包All具体的@Component注解的ServiceBeans(@Service包含@Component)和excludeFilters定义使用@Filter过滤掉UtilService.Class。其他参数可以按照参数API文档中的说明使用,类似但略有不同。@ComponentScans这个注解也可以用来扫描组件,可以定义@ComponentScan,比如:@ComponentScans(value={@ComponentScan(value="com.klasdq.sb.service.*"),@ComponentScan(value="com.klasdq.sb.dao.*",excludeFilters=(@Filter(classes="UtilDao.Class"))})这样定义多个扫描组件,让扫描更准确。因为@ComponentScan(value="com.klasdq.sb.*")包罗万象的扫描方式,写起来简单,但耗费时间。最后,最近有很多朋友找我要一份Linux学习路线图,所以我结合自己的经验,利用业余时间熬夜一个月,整理了一本电子书。无论你是面试还是自我提升,相信都会对你有所帮助!免费送给大家,只求大家给我点个赞!电子书|LinuxDevelopmentLearningRoadmap也希望有小伙伴可以加入我的行列,把这本电子书做得更加完美!获得?希望老铁们来个三连击,让更多人看到这篇文章。推荐阅读:干货|程序员和高级架构师免费发送工件的必备资源|支持搜索的资源网站