前文简单写了下Spring以注解方式加载容器的示例。
在此接上文,通过查看AnnotationConfigApplicationContext的源码,来了解Spring的内部机制。
Spring容器初始化做了很多操作,此篇文章关注的重点是注册(registry),也就是Spring如何加载的@Configuration注解,以代替了spring xml的配置。
至于容器启动时的其他操作留待后续文章中进行讲解。
配置类注入到 AnnotationConfigApplicationContext 构造函数中,如下代码:
//以注解的方式执行
ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
通过这一行代码,就获得了Spring的上下文context,然后context就直接可以通过getBean获得实例对象。
很明显AnnotationConfigApplicationContext构造函数做了很多事情,比如:对Spring容器进行了加载。
首先在此把相关的类做成了一个类图,核心的类和方法如下图所示,下文步骤结合这个类图来看会更加直观。
直接上代码,来看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的生命周期,进行讲解。
来看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进行注册。)
继续跟进,看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的生命周期的文章中进行讨论。
全部评论