Spring多种加载Bean方式解析
1 定义bean的方式
常见的定义bean的方式有:
通过xml的方式,例如:
<bean id="dictionaryrelmap" class="java.util.hashmap"/>
通过注解的方式,在class上使用@component等注解,例如
@component public class xxxservicer{ .... }
通过在@configuration类下的@bean的方式,例如
@configuration public class xxxconfiguration{ @bean public mybean mybean(){ return new mybean(); } }
虽然这三种定义bean的方式不一样,对应的处理细节也不一样,但是从大的逻辑上来看,都是一样。主要的流程如下图: 最关键的就是问题就是这么去找到定义bean的方式,然后生成beandefinition后注册到spring上下文中,由spring自动创建bean的实例。
2 beandefinition
beandefinition是一个接口,用来描述一个bean实例,例如是singleton还是prototype,属性的值是什么,构造函数的参数是什么等。简单来说,通过一个beandefinition我们就可以完成一个bean实例化。 beandefinition及其主要的子类:
下面简单说一下各个子类:
- rootbeandefinition和childbeandefinition: 这2个beandefinition是相对的关系,自spring 2.5 出来以后,已经被genericbeandefinition代替。因为这样强迫我们在编写代码的时候就必须知道他们之间的关系。
- genericbeandefinition: 相比于rootbeandefinition和childbeandefinition在定义的时候就必须硬编码,genericbeandefinition的优点可以动态的为genericbeandefinition设置parent。
- annotatedbeandefinition:看名字就是知道是用来读取通过注解定义bean。
3 通过xml文件定义bean
通过xml定义bean是最早的spring定义bean的方式。因此,怎么把xml标签解析为beandefinition(), 入口是在org.springframework.beans.factory.xml.xmlbeandefinitionreader这个类,但是实际干活的是在org.springframework.beans.factory.xml.beandefinitionparserdelegate。代码很多,但实际逻辑很简单,就是解析spring定义的<bean> <property> 等标签 。
4 通过@component等spring支持的注解加载bean
如果要使用@component等注解定义bean,一个前提条件是:有<context:component-scan/>或者@componentscan注解。但这2个方式还是有一点点区别:
4.1 <context:component-scan/>
由于<context:component-scan/>是一个xml标签,因此是在解析xml,生成的类org.springframework.context.annotation.componentscanbeandefinitionparser,关键代码:
@override public beandefinition parse(element element, parsercontext parsercontext) { //获取base-package标签 string basepackage = element.getattribute(base_package_attribute); basepackage = parsercontext.getreadercontext().getenvironment().resolveplaceholders(basepackage); string[] basepackages = stringutils.tokenizetostringarray(basepackage, configurableapplicationcontext.config_location_delimiters); // 实际处理类是classpathbeandefinitionscanner classpathbeandefinitionscanner scanner = configurescanner(parsercontext, element); //扫描basepackage下所有的类,如果有@component等标签就是注册到spring中 set<beandefinitionholder> beandefinitions = scanner.doscan(basepackages); registercomponents(parsercontext.getreadercontext(), beandefinitions, element); return null; }
4.2 @componentscan
注解对应生成的类是org.springframework.context.annotation.componentscanannotationparser 其实最后实际干活的还是classpathbeandefinitionscanner这个。componentscanannotationparser类的生成是伴随着@configuration这个注解处理过程中(意思说@componentscan必须和@configuration一起使用)。而处理@configuration其实是org.springframework.context.annotation.configurationclasspostprocessor。是不是感觉有点绕。
其实简单来说,在处理@configuration的时候发现有@componentscan注解,就会生成componentscanannotationparser去扫描@component注解
4.3 classpathbeandefinitionscanner
上面说到了,无论注解还是标签的方式,最后都会交给classpathbeandefinitionscanner这个类来处理,这个类做的就是1.扫描basepackage下所有class,如果有@component等注解,读取@component相关属性,生成scannedgenericbeandefinition,注册到spring中。
5 通过@bean方式
前面说了@componentscan是在@configuration处理过程中的一环,既然@bean注解也是必须和@configuration一起使用,那么说明@bean的处理也是在@configuration中,其实最后是交给configurationclassbeandefinitionreader这个类来处理的,关键代码:
private void loadbeandefinitionsforconfigurationclass(configurationclass configclass, trackedconditionevaluator trackedconditionevaluator) { //如果自己是通过@import注解定义的,那么需要把自己注册到spring中 if (configclass.isimported()) { registerbeandefinitionforimportedconfigurationclass(configclass); } //这里就是处理方法上的@bean for (beanmethod beanmethod : configclass.getbeanmethods()) { loadbeandefinitionsforbeanmethod(beanmethod); } //处理@importresource,里面解析xml就是上面说到的解析xml的xmlbeandefinitionreader loadbeandefinitionsfromimportedresources(configclass.getimportedresources()); loadbeandefinitionsfromregistrars(configclass.getimportbeandefinitionregistrars()); }
6 把beandefinition实例化
前面分别说了怎么把不同定义bean的方式转换为beandefinition加入到spring中去(确切来说是保持在beanfactory的beandefinitionmap中),实例是在applicationcontext最后阶段,关键代码在defaultlistablebeanfactory中
@override public void preinstantiatesingletons() throws beansexception { for (string beanname : beannames) { rootbeandefinition bd = getmergedlocalbeandefinition(beanname); if (!bd.isabstract() && bd.issingleton() && !bd.islazyinit()) { if (isfactorybean(beanname)) { final factorybean<?> factory = (factorybean<?>) getbean(factory_bean_prefix + beanname); boolean iseagerinit; if (system.getsecuritymanager() != null && factory instanceof smartfactorybean) { iseagerinit = accesscontroller.doprivileged(new privilegedaction<boolean>() { @override public boolean run() { return ((smartfactorybean<?>) factory).iseagerinit(); } }, getaccesscontrolcontext()); } else { iseagerinit = (factory instanceof smartfactorybean && ((smartfactorybean<?>) factory).iseagerinit()); } if (iseagerinit) { getbean(beanname); } } else { getbean(beanname); } } } }
通过getbean最后最后实例的代码,在abstractautowirecapablebeanfactory中
protected object initializebean(final string beanname, final object bean, rootbeandefinition mbd) { //处理xxaware接口 if (system.getsecuritymanager() != null) { accesscontroller.doprivileged(new privilegedaction<object>() { @override public object run() { invokeawaremethods(beanname, bean); return null; } }, getaccesscontrolcontext()); } else { invokeawaremethods(beanname, bean); } object wrappedbean = bean; if (mbd == null || !mbd.issynthetic()) { // 调用beanpostprocessors#postprocessbeforeinitialization wrappedbean = applybeanpostprocessorsbeforeinitialization(wrappedbean, beanname); } try { //初始化,先判断是否是initializingbean, invokeinitmethods(beanname, wrappedbean, mbd); } catch (throwable ex) { throw new beancreationexception( (mbd != null ? mbd.getresourcedescription() : null), beanname, "invocation of init method failed", ex); } if (mbd == null || !mbd.issynthetic()) { // 调用beanpostprocessors#postprocessafterinitialization wrappedbean = applybeanpostprocessorsafterinitialization(wrappedbean, beanname); } return wrappedbean; }
从上面初始化可以看出,initializebean和beanpostprocessors的调用顺序
7 总结
综上分析,spring加载bean其实大的思想都是一样的,先读取相关信息生成beandefinition,然后通过beandefinition初始化bean。如果知道了上面了套路以后,就可以清楚怎么自定义xml标签或者自定义注解向spring中注入bean。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Spring多种加载Bean方式解析
-
spring security(SpringBoot)自定义Provider实现多种认证方式
-
Spring的Bean实例化的三种方式、属性注入、对象注入、复杂注入(xml文件配置方式)
-
Spring中多配置文件以及寻找引用其他bean的方式 博客分类: Spring springbeanxmlref读取配置文件
-
Spring启动后获取所有拥有特定注解的Bean 博客分类: Spring SpringApplicationListener启动后加载
-
Spring中多配置文件及引用其他bean的方式
-
Java类获取Spring中bean的5种方式
-
详解Spring学习总结——Spring实现AOP的多种方式
-
Spring中多配置文件及引用其他bean的方式
-
Java类获取Spring中bean的5种方式