3.IOC容器之BeanFactory
Spring提供了2种容器类型:BeanFactory和ApplicationContext。
BeanFactory:基础类型IOC容器,提供完整的IOC服务支持。如果没有特殊指定,默认采用懒加载。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。
ApplicationContext:在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,还支持事件发布、国际化信息支持等。它所管理的对象,在启动之后,默认全部初始化并绑定完成。在哪些系统资源充足,并且要求更多功能的场景中,它更合适。
1.BeanFactory的对象注册与依赖绑定方式
- 直接编码方式:
1、BeanFactory只是一个接口,我们最终需要一个该接口的实现来进行实际的Bean的管理,DefaultListableBeanFactory就是一个比较通用的实现类。它除了间接地实现了BeanFactory接口,还实现了BeanDefinitionRegistry接口,该接口超时在BeanFactory的实现中担当Bean注册管理的角色。
2、基本上,BeanFactory接口只定义如何访问容器内管理的Bean的方法,各个BeanFactory的具体实现类负责具体Bean的注册以及管理工作。BeanDefinitionRegistry接口定义抽象了Bean的注册逻辑。
3、每一个受管的对象,在容器中都会有一个BeanDefinition的实例与之相对应,该BeanDefinition的实例负责保存对象的所有必要信息,包括其对应的对象的class类型、是否是抽象类、构造方法参数以及其他属性等。当客户端向BeanFactory请求相应对象的时候,BeanFactory会通过这些信息为客户端返回一个完备可用的对象实例。 - 外部配置文件方式:
1、Spring的IOC容器支持2种配置文件格式:Properties和XML文件格式。
2、采用外部配置文件时,Spring的IOC容器有一个统一的处理方式。通常,需要根据不同的外部配置文件格式,给出相应的BeanDefinitionReader实现类,由BeanDefinitionReader的相应实现类负责将相应的配置文件内容读取并映射到BeanDefinition,然后将其注册到一个BeanDefinitionRegistry大部分工作,包括解析文件格式、装配之类的工作,都是由BeanDefinitionReader的相应实现类来做的,BeanDefinitionRegistry只不过负责保管。
3、XMLBeanDefinitionReader负责服务Spring指定格式的XML配置文件并解析,之后将解析的文件内容映射到相应的BeanDefinition,并加载到相应的BeanDefinitionRegistry中,这时,整个BeanFactory就可以放给客户端使用了。
4、除了提供XMLBeanDefinitionReader用于XML格式配置文件的加载,Spring还在DefaultListableFactory的基础上构建了简化XML格式配置加载的XMLBeanFactory实现。 - 注解方式:
1、如果要通过注解标注的方式注入所需要的依赖,现在可以使用@Autowired和@Component对相关类进行标记。Autowired将告知Spring容器需要为当前对象注入哪些依赖对象。而Component则是配合classpath-scanning功能使用的。
2.BeanFactory的XML之旅
-
和
1、所有使用XML文件进行配置信息加载的Spring IOC容器,都使用统一的XML格式。
2、与构造方法注入可以使用注入配置相对应,Spring为setter方法注入提供了元素。
3、local只能指定与当前配置的对象在同一个配置文件的对象定义的名称。
parent只能指定位于当前容器的父容器中定义的对象引用。
4、,如果要为当前对象注入所依赖的对象的名称,而不是引用时使用。容器在解析配置的时候就可以帮你检查beanName到底是否存在。
5、内部,使用ref可以引用容器中独立定义的对象定义。而内部bean可以将这个私有的对象定义仅局限在当前对象,其他对象无法取得该对象的引用。
6、通常,可以直接通过之前提到的所有元素,来显式地指定bean之间的依赖关系。但如果需要非显式依赖关系,就可以使用depends on。
7、autodetect(发现)是byType和constructor模式的结合体,如果对象拥有默认无参数的构造方法,容器会优先考虑byType的自动绑定模式,否则,会使用constructor模式。
8、自动绑定只应用于原生类型、String类型以及Classes类型以外的对象类型。
9、可以使用parent继承的方式将特定的属性进行更改,而不要全部又重新定义一遍。
parent属性还可以与abstract属性结合使用,达到将相应bean定义模板化的目的。
容器在初始化对象实例的时候,不会关注将abstract属性声明为true的bean定义。 -
bean的scope:
1、scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。
2、标记为拥有singleton scope的对象定义,在容器中只存在一个实例,将一直存活到容器退出。
PS:标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例;而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例。
singleton是容器默认的scope。
3、针对声明为prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继声明周期的管理工作。 -
工厂方法与FactoryBean
1、可以使用静态工厂方法实现类的工厂方法调用结果作为bean注册到容器中。2、
3、FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,当某些对象的实例化过程过于烦琐或某些第三方库不能直接注册到容器时,就可以使用FactoryBean接口,给出自己的对象实例化逻辑代码。
4、FactoryBean类型的bean定义,通过正常的id引用,容器返回的是FactoryBean所生产的对象类型,而非FactoryBean实现本身。如果一定要取得FactoryBean本身的话,可以通过在bean定义的id之前加前缀&来达到目的。 -
方法注入:该方法必须能够被子类实现或者重写,因为容器会为我们要进行方法注入的对象使用Cglib动态生成一个子类实现,从而替代当前对象。通过的name属性指定需要注入的方法名,bean属性指定需要注入的对象,当方法被调用的时候,容器可以每次返回一个新的类型的实例。
Spring提供了一个BeanFactoryAware接口,容器在实例化实现了该接口的bean定义的过程中,会自动将容器本身注入该bean。这样,该bean就持有了它所处的BeanFactory的引用。
ObjectFactoryCreatingFactoryBean是Spring提供的一个FactoryBean实现,它返回一个ObjectFactory实例。它可以为我们返回容器管理的相关对象。实际上,它实现了BeanFactoryAware接口,它返回的ObjectFactory实例只是特定于与容器进行交互的一个实现而已。使用它的好处是,隔离了客户端对象对BeanFactory的直接引用。 -
可以使用进行方法替换
3.容器背后的秘密
- 容器启动阶段一开始,首先会通过某种途径加载XML里的配置元信息,除了代码方式比较直接,在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的配置元信息进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。
该阶段所做的工作可以认为是准备性的,重点更加侧重于对象管理信息的收集。一些验证性或辅助性的工作也可以在这个阶段完成。 - 当某个请求方通过容器的getBean方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用getBean方法时,就会触发第二阶段的活动。
该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它。当该对象装配完毕后,容器会立即将其返回请求方使用。 - Spring提供了一种叫BeanFactoryPostProcessor的容器扩展机制。该机制允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。
对于ApplicationContext会自动识别配置文件中的BeanFactoryPostProcessor并应用它。 - BeanFactoryPostProcessor:
1、PropertyPlaceholderConfigurer:通常情况下,我们不想将类似于系统管理相关的信息同业务对象相关的配置信息混杂到XML配置文件中,所以,我们会将一些信息单独配置到一个properties文件中。该实现允许我们在XML配置文件中使用占位符,并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。
当BeanFactory在第一阶段加载完成所有配置信息时,BeanFactory中保存的对象的属性信息还只是以占位符的形式存在。当PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor被应用时,它会使用properties配置文件中的配置信息来替换占位符所表示的属性值。这样,当进入容器实现的第二阶段实例化bean时,bean中的属性值就是最终替换完成的了。
2、PropertyOverrideConfigurer:可以通过该实现类对容器中配置的任何想处理的bean定义的property信息进行覆盖替换。如果要对容器中的某些bean定义的property信息进行覆盖,需要按照如下规则提供一个配置文件。
3、CustomEditorConfigurer:它只是辅助性地将后期会用到的信息注册到容器,对BeanDefinition没有做任何变动。要完成由XML记载的字符串类型到具体对象的转换,都需要这种转换规则相关的信息,而CustomEditorConfigurer就是帮助我们传达类似信息的。
Spirng内部通过JavaBean的PropertyEditor来帮助进行String类型到其他类型的转换工作,只要为每种对象类型提供一个PropertyEditor,就可以根据该对象类型取得与其相对应的PropertyEditor来做具体的类型转换。 - 了解bean的一生:
1、容器启动之后,并不会马上就实例化相应的bean定义。
2、隐式调用有如下2种情况:
(1)对于BeanFactory来说,对象实例化默认采用延迟初始化。当A要进行第一次实例化的时候,如果依赖的B还没有进行实例化,那么会先内部调用getBean()对B进行实例化。
(2)ApplicationContext启动之后会实例化所有的bean定义。
3、
4、Spring容器将对其所管理的对象全部给予统一的生命周期管理,这些被管理的对象完全摆脱了原来那种new完后备使用,脱离作用域即被回收的命运。
(1)Bean的实例化与BeanWrapper
容器在内部实现的时候,采用策略模式来决定采用何种方式初始化bean实例。通常,可以通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。
容器只要根据相应bean定义的BeanDefinition取得实例化信息,结合策略实现类以及不同的bean定义类型,就可以返回实例化完成的对象实例。但是,不是返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。这是为了第二步设置对象属性。
BeanWrapper定义继承了PropertyAccess接口,可以统一对对象属性进行访问。在第一步构造完成对象之后,Spring会根据对象实例构造一个BeanWrapperImpl实例,然后将之前的CustomEditorConfigurer注册的PropertyEditor复制一份给该实例(这就是BeanWrapper同时又是PropertyEditorRegistry的原因)。这样,当BeanWrapper转换类型、设置对象属性值时,就不会无从下手了。
使用BeanWrapper对bean实例操作很方便,可以免去直接使用Java反射API操作对象实例的烦琐。
(2)各色的Aware接口
当对象实例化完成并且相关属性以及依赖设置完成之后,容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。如果是,则将这些Aware接口定义中规定的依赖注入给当前对象实例。如BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
如果对象声明实现了BeanFactoryAware接口,BeanFactory容器会将自身设置到当前对象实例。这样,当前对象实例就拥有了一个BeanFactory容器的引用,并且可以对这个容器内允许访问的对象按照需要进行访问。
对于ApplicationContext类型容器,使用的是下面的BeanPostProcessor方式。会检查以下几个Aware接口并根据接口定义设置相关依赖。
ResourceLoaderAware:会将当前容器自身设置到对象实例。
ApplicationEventPublisherAware:ApplicationContext作为一个容器,同时还实现了ApplicationEventPublisher接口,这样,它就可以作为ApplicationEventPublisher来使用。所以,该接口也会将自身注入当前对象。
MessageSourceAware:自身就实现了MessageSource接口,提供国际化的信息支持。
ApplicationContextAware:将自身注入当前对象实例。
(3)BeanPostProcessor
BeanPostProcessor是存在于对象实例化阶段,而BeanFactoryPostProcessor则是存在于容器启动阶段。
BeanPostProcessor会处理容器内所有符合条件的实例化后的对象实例。该接口定义了2个方法:postProcessBeforeInitialization是前置处理这一步会执行的方法,而postProcessAfterInitialization是后置处理那一步将会执行的方法。BeanPostProcessor的2个方法中都传入了原来的对象实例的引用,这为扩展容器的对象实例化过程中的行为提供了极大的便利。
场景的使用BeanPostProcessor的场景,是处理标记接口实现类,或者为当前对象提供代理实现。
ApplicationContext对应的那些Aware接口实际上就是通过BeanPostProcessor的方式进行处理的。当ApplicationContext中每个对象的实例化过程走到BeanPostProcessor前置处理这一步,容器会检测到之前注册到容器的BeanPostProcessor的实现类,然后检查并设置Aware相关依赖。
除了检查标记接口以便应用自定义逻辑,还可以通过BeanPostProcessor对当前对象实例做更多的处理。比如替换当前对象实例或者字节码增强当前对象实例等。Spring的AOP则更多地使用BeanPostProcessor来为对象生成相应的代理对象。
(4)InitializingBean和init-method
InitializingBean是容器内部广泛使用的一个对象生命周期标识接口,其作用在于,在对象实例化过程调用过BeanPostProcessor的前置处理之后,会接着检测当前对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。
虽然该接口在Spring容器内部广泛使用,但如果真的让我们的业务对象实现这个接口,则显得容器比较具有侵入性。所以,还提供了XML配置的时候,使用bean的init-method属性。
通过init-method,系统中业务对象的自定义初始化操作可以以任何方式命名,而不再受制于InitializingBean的afterPropertiesSet().
(5)Disposable与destroy-method
当所有的一切完成后,容器将检查singleton类型的bean实例,看是否实现了DisposableBean接口。或其对应的bean定义是否通过bean的destroy-method属性指定了自定义的对象销毁方法。如果是,就会为该实例注册一个用于对象销毁的回调,以便在这些singleton类型的对象实例销毁之前,执行自定义销毁逻辑。
需要我们告知容器,在哪个时间点来执行对象的自定义销毁方法。
对于ApplicationContext来说,道理是一样的。但AbstractApplicationContext为我们提供了registerShutdownHook方法,该方法底层使用标准的Runtime类的addShutdownHook方法来调用相应bean对象的销毁逻辑,从而保证在Java虚拟机退出之前,这些singleton类型的bean对象实例的自定义销毁逻辑会被执行。
本文深入探讨Spring框架中的BeanFactory与ApplicationContext的区别,涉及注册与依赖绑定、配置方式、BeanScope、工厂方法与FactoryBean,以及它们在生命周期管理中的角色。
3万+

被折叠的 条评论
为什么被折叠?



