文章目录
前言
基于学习遗忘曲线收敛太快,决定将Spring源码的解读记录下来。今天是第一篇,容器的启动
那就开始吧~
一、Spring是什么?
用java的应该都清楚Spring框架是什么,目前市面上的主流java框架也都会做和Spring的结合。Spring两大利器:IOC+AOP。API的学习可以借助官网,因为有时候中文翻译或某些同学的博客翻译有问题:https://spring.io/projects/spring-framework#learn
二、探究
鉴于大家现在java项目几乎都是使用Springboot,那我们从Springboot项目的启动切入口查看Spring容器的启动。具体springboot版本就不指定了,Spring容器启动差别不大。
1. Spring容器启动
从Springboot的run方法开始进入
1.1 容器类型(contextClass)的判断
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
// 通过反射生成ApplicationContext类型的对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
通过判断webApplicationType来进行容器创建类型contextClass的设置。
因为默认的webApplicationType在前期被deduce成了SERVLET,所以创建的是AnnotationConfigServletWebServerApplicationContext
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
1.2 容器的实例化
这个其实不算难了,一句话总结,就是利用反射进行实例化拿到容器的对象。
具体是BeanUtils.instantiateClass(contextClass)
- 拿到Class的构造方法clazz.getDeclaredConstructor()
- 获取构造方法的参数类型
- 调用ctor.newInstance(argsWithDefaultValues)进行容器对象创建及初始化
2. 容器的创建及实例化过程
2.1 构造方法读下去
构造方法代码如下:
/**
* Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
* to be populated through {@link #register} calls and then manually
* {@linkplain #refresh refreshed}.
*/
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
对于java来说,调用自己的无参构造方法之前,会把父类的无参构造方法都调用:
AnnotationConfigServletWebServerApplicationContext ->
ServletWebServerApplicationContext ->
GenericWebApplicationContext ->
GenericApplicationContext->
/**
* Create a new GenericApplicationContext.
* @see #registerBeanDefinition
* @see #refresh
*/
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
由此可以看到applicationContext里面的beanFactory属性默认会被初始化为DefaultListableBeanFactory对象。
2.1.1 scanner的作用
scaner的作用,顾名思义,扫描器,就是后期为了扫描所有生成的.class文件,然后根据class文件生成BeanDefinition放入到Spring的容器里,至于BeanDefinition的作用,后面再讲,先记住即可。
2.1.2 reader的作用
那reader的作用呢,其实就是为了补充scaner的力所不能及。怎么说呢,因为scaner扫描出来的class,需要具体的类对象来进行操作,咱们姑且用X来代替,那么X来把别人加入到容器,X从何而来呢?其实对于我们这些程序员来说,可能最直观的想法,就是直接new呗。但是Spring作为一个牛逼开源框架,他的开发者选择了一种尽量自洽的方式来处理,也就是通过reader把这些X类的BeanDefinition放入到了applicationContext里面,然后后续通过调用getBean的方式从容器中拿到X。
具体逻辑可以从AnnotatedBeanDefinitionReader的构造方法debug下去看到,最终会调用:
/**
* Create a new {@code AnnotatedBeanDefinitionReader} for the given registry,
* using the given {@link Environment}.
* @param registry the {@code BeanFactory} to load bean definitions into,
* in the form of a {@code BeanDefinitionRegistry}
* @param environment the {@code Environment} to use when evaluating bean definition
* profiles.
* @since 3.1
*/
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//在这一步会进行刚刚说的X的BeanDefinition的放入
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
具体放入了哪些类的BeanDefinition呢?
这5个类对象的具体作用,我们以后再说。
- 此处还得记住AnnotatedBeanDefinitionReader还有个register方法,这个方法的作用是把某个类注册进Spring的容器中,
- reader之所以可以进行register操作是因为reader里面拥有一个BeanDefinitionRegistry的属性,而我们默认的DefaultListableBeanFactory又正好实现了这个接口,
- 所以利用**this.reader = new AnnotatedBeanDefinitionReader(this);**进行reader的设定的时候,其实就是把DefaultListableBeanFactory对象设置成了reader里面的registry属性。
- 可以发现我们的applicationContext和scaner通过BeanFactory对象进行了绑定。
- 再看看我们的DefaultListableBeanFactory的register实际逻辑:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// do something
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
可以看到register具体做的事儿,其实就是往BeanDefinitionMap里面put对应的beanDefinition,如果对应name之前已经存在beanDefinition,就覆盖,同时重新生成对应的bean实例。
而相对于reader初始化做的事儿,scanner对象的初始化就显得很简单,就是做了一些属性的赋值。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了Springboot使用的ApplicationContext的contextClass的选取及对象实例化的一些基本工作。
也就是Springboot的run方法中的**context = createApplicationContext();**进行的逻辑。