Spring源码分析(5)——ApplicationContext

Spring源码中最重要的一章之一,将分析Spring的核心:ApplicationContext。Bean的扫描、注册等基本上所有功能都是在这里实现的初始化。

一、待分析的源码

前面说的一直都是BeanFactory的东西,感觉BeanFactory已经把能做的都做了,那么ApplicationContext能做些什么呢?

简单一点说:ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先。(个人感觉其实ApplicationContext就是一层壳,所有事情都委托给BeanFactory来做了,它只是做了初始化配置而已)

本文流程都围绕以下代码展开,代码和之前研究BeanFactory的时候基本一模一样,只是将XmlBeanFactory替换成了ClassPathXmlApplicationContext,他们又有什么区别呢?

bean.xml
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="student" class="com.example.methodlog.Student" />
</beans>
Main.java
1
2
3
4
5
public static void main(String[] args) {
//BeanFactory bf = new XmlBeanFactory(new ClassPathResource("bean.xml"));
ClassPathXmlApplicationContext bf = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) bf.getBean("student");
}

二、ApplicationContext的refresh方法

无论从ClassPathXmlApplicationContext类的哪个构造方法进入,最终都会进入AbstractApplicationContext类的refresh()方法,这个方法是研究ApplicationContext最重要的方法,务必记住它。

AbstractApplicationContext.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新的上下文环境
prepareRefresh();
// 初始化BeanFactory,并进行XML文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对BeanFactory进行各种功能填充
prepareBeanFactory(beanFactory);
try {
// 子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
// 激活各种BeanFactoryPostProcessor,基本上所有注解和Bean的扫描都是在这完成的。
invokeBeanFactoryPostProcessors(beanFactory);
// 注册各种BeanPostProcessor,这里只是注册,真正的调用是在getBean的时候
registerBeanPostProcessors(beanFactory);
// 为上下文初始化Message源
initMessageSource();
// 初始化应用消息广播器
initApplicationEventMulticaster();
// 留给子类来初始化其他的Bean
onRefresh();
// 在所有注册的bean中查找注册Listener Bean
registerListeners();
// 初始化剩下的非懒加载的单例
finishBeanFactoryInitialization(beanFactory);
// 完成刷新过程
finishRefresh();
}
......
}
}

AbstractApplicationContext类中调用了一系列方法,看着十分清晰,这里对最重要的几个方法进行分析。

  • obtainFreshBeanFactory方法

创建或获取BeanFactory。在Spring项目中,会创建一个DefaultListableBeanFactory,并对其中配置的xml文件进行读取和Bean注册;在SpringBoot项目中,会复用之前创建的DefaultListableBeanFactory,且不会对xml文件进行读取。

  • invokeBeanFactoryPostProcessors方法

在Spring容器中找出实现了BeanFactoryPostProcessor接口的类并按一定的顺序执行。

在Spring项目中,obtainFreshBeanFactory方法在扫描到xml文件中的<context:component-scan>标签时,调用AnnotationConfigUtils向BeanDefinitionMap中注册ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor等几个特别重要的类;而SpringBoot项目中,容器类型是AnnotationConfigServletWebServerApplicationContext,而这个类初始化了AnnotatedBeanDefinitionReader,这个类也调用了AnnotationConfigUtils往BeanDefinitionMap中注册了ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor等几个特别重要的类。

AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor分别实现对@Autowired、@Resource注解的支持,这个在之前已经说过了,他们会在Bean实例化后被调用。

而ConfigurationClassPostProcessor会在invokeBeanFactoryPostProcessors方法中首先执行,这个类会去找有@Configuration注解的Bean,然后使用ConfigurationClassParser去解析这个类,解析内容包含@PropertySources、@ComponentScans、@Import、@ImportResource、@Bean这几个注解。

值得注意的是,ConfigurationClassPostProcessor在Bean中解析到@ComponentScans注解时,会对该注解中标注的包进行扫描,扫描包中所有包含@Component的类,并将扫描到的类注册到BeanDefinitionMap中,@Controller、@Service、@Repository等几个注解也标注了@Component注解,所以也会被注册为Bean。ConfigurationClassPostProcessor也会将扫描出来@Bean注解的方法,放到BeanDefinitionMap中来实现了Bean的注册。@ImportResource同理,其中配置的xml文件也会在此被扫描。注意,这些Bean只是注册了,并还未实例化,实际实例化Bean是在后面的finishBeanFactoryInitialization方法中。

综上,invokeBeanFactoryPostProcessors通过调用ConfigurationClassPostProcessor完成了对@Component、@Controller、@Service、@Repository、@Bean等注解的扫描和Bean注册。而ConfigurationClassPostProcessor的注册是在ApplicationContext创建时进行的。

  • registerBeanPostProcessors方法

从Spring容器中找出实现了BeanPostProcessor接口的Bean,然后设置到BeanFactory的beanPostProcessors变量中。

容器在创建时已经注册了一些实现BeanPostProcessor的Bean,但这些Bean只是BeanDefinition,还未实例化。registerBeanPostProcessors方法会实例化这些Bean,然后放到专门的beanPostProcessors变量中。

  • onRefresh方法

一个模板方法,不同的容器会有不同的实现。

Spring的XmlWebApplicationContext容器未做什么我们关心的事。

SpringBoot的AnnotationConfigServletWebServerApplicationContext容器会在此处启动内置的Servlet容器,如Tomcat、Jetty。

  • finishBeanFactoryInitialization方法

实例化所有预加载的Bean,刚才注册的BeanPostProcessor就会在实例化Bean的时候生效。

三、总结

  • ApplicationContext的关键方法是refresh()方法。

  • Spring项目中,obtainFreshBeanFactory方法完成了配置的xml文件的读取和Bean的注册。

  • 基本上所有常见注解的扫描和Bean的注册,都是在invokeBeanFactoryPostProcessors方法中完成的。包括@Component、@Controller、@Service、@Repository、@Bean、@ImportResource注解解析出来的Bean,都会在此完成注册,但未实例化。

  • registerBeanPostProcessors方法会将创建容器时注册的BeanPostProcessor Bean,放到专门的beanPostProcessors变量中。AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor分别在实例化Bean时实现对@Autowired、@Resource注解的支持。

  • SpringBoot在onRefresh方法完成内置Servlet容器的启动。

  • 大多数预加载的Bean都是在finishBeanFactoryInitialization方法中完成,通过调用BeanFactory的getBean方法实现。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×