Spring容器注册的源码分析

2020-02-18 11:12
1307
0
前文简单写了下Spring以注解方式加载容器的示例。
在此接上文,通过查看AnnotationConfigApplicationContext的源码,来了解Spring的内部机制。
Spring容器初始化做了很多操作,此篇文章关注的重点是注册(registry),也就是Spring如何加载的@Configuration注解,以代替了spring xml的配置。
至于容器启动时的其他操作留待后续文章中进行讲解。
 
一、切入点
配置类注入到 AnnotationConfigApplicationContext 构造函数中,如下代码:
 //以注解的方式执行
ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        
通过这一行代码,就获得了Spring的上下文context,然后context就直接可以通过getBean获得实例对象。
很明显AnnotationConfigApplicationContext构造函数做了很多事情,比如:对Spring容器进行了加载。
 
二、相关类图了解
首先在此把相关的类做成了一个类图,核心的类和方法如下图所示,下文步骤结合这个类图来看会更加直观。
三、源码分析
3.1、进入构造函数
直接上代码,来看new AnnotationConfigApplicationContext(MySpringConfig.class)里都做了啥:
	/**
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given component classes and automatically refreshing the context.
	 * @param componentClasses one or more component classes — for example,
	 * {@link Configuration @Configuration} classes
	 */
	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();//初始化类
		register(componentClasses);//对配置类进行注册
		refresh();//手动刷新
	}
通过上面代码可以看出,构造函数中干了3件事:
1、调用无参构造函数去实例化对象。
2、对配置类(示例代码就是:MySpringConfig类)进行注册。
3、刷新(Bean工厂、初始化单例对象等)
这里主要讲讲1、2步怎么实现的注册进行,刷新方法可以在后续开一篇SpringBean的生命周期,进行讲解。
3.2、无参构造函数去实例化对象
来看this()里都做了啥:
1、为2个类实例化:
  • 读取注解bean信息读取类(AnnotatedBeanDefinitionReader);
  • bean类扫描器(ClassPathBeanDefinitionScanner);

源代码如下:

2、AnnotatedBeanDefinitionReader实例化
如下是AnnotatedBeanDefinitionReader实例化的源码,至于ClassPathBeanDefinitionScanner实例化这里就不细说了,不是重点。
着重看如上图箭头,即把AnnotationConfigApplicationContext对象传入自己的registry变量中。
 
3、跟进AnnotatedBeanDefinitionReader父类构造函数
调用自身构造函数之前会先调用父类的构造函数,所以我们再去GenericApplicationContext中看看。
如下图所示,实例化了一个beanFactory。BeanFactory用来干啥?用来注入对象到容器中,后面会讲到。
4、回到第一点,可以看到在实例化2个类时传入了this,以便这两个类能获得第二点中GenericApplicationContext的几个对象。
 
小结:AnnotationConfigApplicationContext的无参构造函数,其关键点在beanFactory的创建(因为最终要用到beanFactory进行注册。)
3.3、register方法
继续跟进,看register(componentClasses)里都做了啥?
1、老规矩,直接上源码,register方法如下,this.reader即为上文3.2第1点中,实例化AnnotatedBeanDefinitionReader的对象,即,调用了AnnotatedBeanDefinitionReader对象中的register方法。
2、那继续往下走,如下两图所示,register->registerBean->doRegisterBean
3、重点就在doRegisterBean方法中。
AnnotatedGenericBeanDefinition封装了配置类的信息,BeanDefinitionHolder包装了AnnotatedGenericBeanDefinition和beanName.
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {
		//注解方式启动的配置类 注入IOC bean信息(即为@Configuration注解的类注入容器)
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		//判断注解配置类是否使用条件注册(即符合@Condition注解的条件)
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}
		//设置回调方式
		abd.setInstanceSupplier(supplier);
		//判断config类上是否有加上@scope作用域
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		//设置scope
		abd.setScope(scopeMetadata.getScopeName());
		//有beanName用beanName,没有生成一个
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		//对一些通用注解进行处理:Lazy、Primary、DependsOn、Role、Description
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		//有没有给BeanDefinition的回调,有就加上
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}
		//注册配置类
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
看最后一行,BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
这里的this.registry就是上文3.2第2点所讲,AnnotationConfigApplicationContext对象。
4、继续追击看看registerBeanDefinition里都干了啥
感觉快要追上了,registerBeanDefinition里实现了按名字注册bean和按别名注册,别名不是关键点,主要关注名字注册,registry.registerBeanDefinition方法。
registry在第3点的时候说了是AnnotationConfigApplicationContext对象,继承于GenericApplicationContext类,registerBeanDefinition方法在GenericApplicationContext类中实现,所以继续追踪。
5、GenericApplicationContext中的registerBeanDefinition方法如下图。
可以看出其调用了this.beanFactory中的registerBeanDefinition方法,this.beanFactory参数即为3.2第3点实例化GenericApplicationContext时new的DefaultListableBeanFactory对象。
6、感觉就要到了,继续追击DefaultListableBeanFactory中的registerBeanDefinition方法。
如下图所示,因为篇幅太长就不一一说明,重点就在下图红框标识的两行。
this.beanDefinitionMap,get、put,感觉好熟悉,看看是啥?如下图,其就是一个ConcurrentHashMap。
beanDefiniton对象就存储在一个线程安全的HashMap中。
 
四、总结一下
1、AnnotationConfigApplicationContext就是通过BeanFactory(DefaultListableBeanFactory)把配置类的BeanDefinition存储在BeanFactory的ConcurrentHashMap对象中。
2、程序中通过上下文(context)获得bean,其实就是通过context中的BeanFactory把map中的value取出来。
 
当然,到目前为止,注册只是把配置类加载进了Spring容器中。其他的Bean呢?在3.1的刷新方法中,本文就先卖个关子,后续写SpringBean的生命周期的文章中进行讨论。

全部评论