Spring源码--Bean的管理总结
Posted shen-qian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码--Bean的管理总结相关的知识,希望对你有一定的参考价值。
前奏
最近看了一系列解析spring管理Bean的源码的文章,在这里总结下,方便日后复盘。文章地址https://www.cnblogs.com/CodeBear/p/10336704.html
spring的一大核心就是Ioc,即把Bean放到一个公共的容器中,既替开发者管理了Bean的生命周期,又解耦了类之间的持有关系。
spring中,管理Bean的容器都叫xxxContext,这里的继承关系有点复杂,但是功能是相同的--存放并管理各种Bean。我们常用的用注解生成的Bean,都放在AnnotationConfigApplicationContext类型的容器里。所以,程序启动时一定会执行如下语句:
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
生成一个AnnotationConfigApplicationContext容器实例,用来存放通过注解生成的Bean。具体包括:@Configuration,@Component,@Import,@Resouce,@Service,@ComponentScan等注解。所以重点是AnnotationConfigApplicationContext这个类。
AnnotationConfigApplicationContext类
其构造方法只有三行代码,长这样:
//根据参数类型可以知道,其实可以传入多个annotatedClasses,但是这种情况出现的比较少 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { //调用无参构造函数,会先调用父类GenericApplicationContext的构造函数 //父类的构造函数里面就是初始化DefaultListableBeanFactory,并且赋值给beanFactory //本类的构造函数里面,初始化了一个读取器:AnnotatedBeanDefinitionReader read,一个扫描器ClassPathBeanDefinitionScanner scanner //scanner的用处不是很大,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的 this(); //把传入的类进行注册,这里有两个情况, //传入传统的配置类 //传入bean(虽然一般没有人会这么做 //看到后面会知道spring把传统的带上@Configuration的配置类称之为FULL配置类,不带@Configuration的称之为Lite配置类 //但是我们这里先把带上@Configuration的配置类称之为传统配置类,不带的称之为普通bean register(annotatedClasses); //刷新 refresh(); }
参数是多个Class对象,表示要实例化的Bean的类型,一般情况只传一个。
this():做的是初始化操作,BeanDefinitionRegistry的对象是注册中心,根据它的信息生成几个初始的、必要的Bean。
register(annotatedClasses) :将传入的参数,目标Bean的类型注册进来,即注册到BeanDefinitionRegistry对象里去。
refresh() :由于第二步新注册了目标Bean的Class,因此要刷新,通过反射生成上一步新添加的类型的Bean。
第一步:this() 初始化
this()调用此类的无参构造方法:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry { //注解bean定义读取器,主要作用是用来读取被注解的了bean private final AnnotatedBeanDefinitionReader reader; //扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的 private final ClassPathBeanDefinitionScanner scanner; /** * Create a new AnnotationConfigApplicationContext that needs to be populated * through {@link #register} calls and then manually {@linkplain #refresh refreshed}. */ public AnnotationConfigApplicationContext() { //会隐式调用父类的构造方法,初始化DefaultListableBeanFactory //初始化一个Bean读取器 this.reader = new AnnotatedBeanDefinitionReader(this); //初始化一个扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的 this.scanner = new ClassPathBeanDefinitionScanner(this); } }
这个方法初始化了AnnotatedBeanDefinitionReader,字面意思就可以看出是读取一个容器中装着的Bean,因此参数直接把this传入。 另外,this()中又会自动调用父类GenericApplicationContext的无参构造:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private final DefaultListableBeanFactory beanFactory; public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); } }
可以看到,GenericApplicationContext这个父类实例化了一个DefaultListableBeanFactory,所以作为继承者的AnnotatedBeanDefinitionReader也就有了DefaultListableBeanFactory实例。从名字就可以看出这是一个生产Bean的工厂。Bean工厂的原料在哪里呢?我们需要初始化这个工厂的信息。这里根据BeanDefinitionRegistry的注册信息,生成一个Map<name,definition>作为工厂的原料:
private static BeanDefinitionHolder registerPostProcessor( BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) { definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(beanName, definition); return new BeanDefinitionHolder(definition, beanName); } -------------------- public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { this.beanFactory.registerBeanDefinition(beanName, beanDefinition); } -------------------- //beanDefinitionMap是Map<String, BeanDefinition>, //这里就是把beanName作为key,ScopedProxyMode作为value,推到map里面 this.beanDefinitionMap.put(beanName, beanDefinition); //beanDefinitionNames就是一个List<String>,这里就是把beanName放到List中去 this.beanDefinitionNames.add(beanName);
this()这一行总结就是:
(1) 初始化了一个AnnotatedBeanDefinitionReader,以本容器为构造参数,读取本容器放的Bean;
(2) 从父类继承来DefaultListableBeanFactory,里面有Map以BeanName为key,BeanDefinition为value,其中BeanDefinition记录了此Bean的类型、是否懒加载、作用域等信息;还有List,存放每个Bean的名字。
(3) 容器之所以为容器就是因为持有一个Factory可以根据名字得到具体信息从而实例化一个Bean返回,其中Factory的原料又从注册类BeanDefinitionRegister中来。
第二步:register(annotatedClasses) 注册Bean的类型
初始情况下,只注册有几个spring的内置Bean,当通过注解要在容器中生成用户自己的Bean时则要调用此代码将目标Bean的Class注册进去,再刷新从而让DefaultListableBeanFactory得到更新。这里贴一下原文步骤:
-
通过AnnotatedGenericBeanDefinition的构造方法,获得配置类的BeanDefinition,这里是不是似曾相似,在注册ConfigurationClassPostProcessor类的时候,也是通过构造方法去获得BeanDefinition的,只不过当时是通过RootBeanDefinition去获得,现在是通过AnnotatedGenericBeanDefinition去获得。
-
判断需不需要跳过注册,Spring中有一个@Condition注解,如果不满足条件,就会跳过这个类的注册。
-
然后是解析作用域,如果没有设置的话,默认为单例。
-
获得BeanName。
-
解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description。
-
限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上是任何注解,这里没有判断注解的有效性)。
-
把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中(这个不是很重要,可以简单的理解为方便传参)。
- 注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册:
以上是关于Spring源码--Bean的管理总结的主要内容,如果未能解决你的问题,请参考以下文章