Spring是企业级Java开发中应用最广泛的编程框架。在EJB日渐式微的情况下,Spring已成为企业级Java开发的事实标准。经过十多年的发展,不仅传统的配置方式仍然在广泛使用,同时也诞生了Spring Boot,Spring Cloud等基于Spring的新的框架,在方兴未艾的微服务领域继续引领着潮流。
在使用Spring的过程中,相信我们都很容易感受到Spring给我们带来的便利,这不禁就会引发我们对Spring本身的设计和实现的兴趣。通过阅读文档和代码,我们会发现Spring堪称Java项目设计与实现的典范,它的设计非常地优雅,是我们学习Java开发,乃至面向对象设计与开放的很好的教材。
在Spring的体系中,依赖注入(Dependency Injection,简称DI)是一个基础设施级的功能。一般说使用Spring,默认都会认为必然会使用DI的功能。所以学习Spring的源代码,一般也会从DI入手。
Spring的依赖注入主要是靠应用上下文(ApplicationContext)来实现的。顾名思义,应用上下文就是持有了应用启动必须的各种信息的对象。在多种ApplicationContext中,ClassPathXmlApplicationContext是比较简单的一个,从它的名字可以看出,它是基于Java的Classpath的,同时是基于Xml配置的。
下面先分析一下ClassPathXmlApplicationContext的类关系 我们首先关注一下ClassPathXmlApplicationContext到ApplicationContext的继承和实现关系。从顶向下各个接口和实现类的功能如下:
ApplicationContext:这个接口是提供了应用程序配置的核心接口,当程序运行时它是只读的,不能修改状态,但是可以重新加载(reload),只要具体的实现类支持。
ConfigurableApplicationContext:这是一个支持SPI加载的接口,绝大多数应用上下文都实现了它。它内部定义了一些默认的基础常量,同时提供了ApplicationContext之外的配置应用程序的方法。
AbstractApplicationContext:应用上下文的抽象实现类。这个类可以说是应用上下文的骨架实现类。它不关心用到的配置的存储方式;实现了通用的应用上下文功能;采用了模板设计模式,具体的实现类需要实现它定义的抽象方法。
Base class for {@link org.springframework.context.ApplicationContext}
implementations which are supposed to support multiple calls to {@link #refresh()},
creating a new internal bean factory instance every time.
Typically (but not necessarily), such a context will be driven by
a set of config locations to load bean definitions from.
AbstractRefreshableApplicationContext:这个类支持多次刷新上下文。每次刷新时,它会创建一个新的内部Bean Factory(Bean工厂,通过它实际持有创建的bean)。
AbstractRefreshableConfigApplicationContext:提供了对某种形式的存储配置文件路径的支持,包括类路径(ClassPath),文件系统等。
AbstractXmlApplicationContext:这个类提供了从XML文件中提取bean定义的功能(通过XmlBeanDefinitionReader实现)
ClassPathXmlApplicationContext:这个类从Class path获取Context配置文件。
下面就以dubbo源代码中提供的demo来跟踪一下ClassPathXmlApplicationContext这个应用上下文的启动过程。它主要通过下面这一行代码来启动:
1
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml" });
这行代码很简单,就是调用了构造方法,参数是一个字符串数组,只有一个元素,指出了配置文件的路径。
进入这个构造方法:1
2
3
4
5
6
7
8
9
* Create a new ClassPathXmlApplicationContext, loading the definitions
* from the given XML files and automatically refreshing the context.
* @param configLocations array of resource locations
* @throws BeansException if context creation failed
*/
public ClassPathXmlApplicationContext (String... configLocations) throws BeansException {
this (configLocations, true , null );
}
它又调用了自己的另一个构造函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext (String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super (parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
这个构造函数先调用了超类的构造函数,之后判断传入的是否刷新的布尔值,如果为true,则调用refresh方法。
超类的构造方法基本什么也没做,除了每个超类定义的在构造函数之前就需要初始化的field的初始化之外,只是在AbstractApplicationContext的构造函数中设置了一下parent context(Spring支持有层级的应用上下文,但本例中不涉及)。
由此可见,所有的启动和刷新上下文的功能都是refresh这个方法完成的。这个方法是在ConfigurableApplicationContext中定义,在AbstractApplicationContext中定义的,子类没有覆盖它,这也说明Spring不同的上下文启动和刷新的流程是通用的。
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@Override
public void refresh () throws BeansException, IllegalStateException {
synchronized (this .startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
prepareFresh方法主要做了一些准备工作,如设置启动时间,设置关闭状态为false,活动状态为true,初始化属性源等。
obtainFreshBeanFactory方法内部通过AbstractRefreshableApplicationContext中的refreshBeanFactory方法刷新bean工厂,它先判断内部的bean factory是否已存在,若存在则销毁它们保存的bean,并关闭之。之后这个方法的核心工作是调用了createBeanFactory方法创建内部的bean factory。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
* Create an internal bean factory for this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation creates a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
* context's parent as parent bean factory. Can be overridden in subclasses,
* for example to customize DefaultListableBeanFactory's settings.
* @return the bean factory for this context
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
*/
protected DefaultListableBeanFactory createBeanFactory () {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
可见是新建了一个DefaultListableBeanFactory。这个类的类关系如下图:
关注一下BeanFactory这个接口,它是访问Spring bean容器的根接口,提供了访问bean容器的基本功能。
Extension of the {@link BeanFactory} interface to be implemented by bean factories
that can enumerate all their bean instances, rather than attempting bean lookup
by name one by one as requested by clients. BeanFactory implementations that
preload all their bean definitions (such as XML-based factories) may implement
this interface.
ListableBeanFactory提供了枚举bean实例的功能,它会预加载bean的定义
BeanDefinitionRegistry:持有bean定义,例如root bean definition和child bean definition实例通常被bean factory实现。
DefaultListableBeanFactory:ListableBeanFactory和BeanDefinitionRegistry的默认实现。常用于在访问bean之前,保存所有bean的definition。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory () throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this .beanFactoryMonitor) {
this .beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
通过loadBeanDefinition加载定义到beanFactory中。这个方法是通过XmlBeanDefinitionReader去加载配置文件中的bean定义。具体过程比较繁琐,这里就不展开了,后续有时间再专门介绍。加载完之后,会将beanDefinition保存在DefaultListableBeanFactory的一个field中:
1
2
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256 );
至此,Spring容器启动过程中的第一大步骤就算基本完成了,就是将bean定义从配置文件中读取出来,并解析为BeanDefinition保存在应用上下文的内置bean factory的内部的一个map钟,key为配置文件中定义的bean的name。
之后回到refresh方法,下面是prepareBeanFactory方法,这个方法就是对内部的bean factory做各种设置,以方便后面使用。具体就不介绍了。感兴趣可以自行研究代码。
postProcessBeanFactory是一个空方法,可以自定义一些对bean factory的定制化处理。由此以及后续的过程可以看出,Spring非常注重扩展性,留出了很多供使用者灵活扩展的地方,充分体现了“对修改关闭,对扩展开放”的面向对象设计原则。
invokeBeanFactoryPostProcessors:实例化并调用所有的BeanFactoryPostProcessor,BeanFactoryPostProcessor就是在bean factory的标准初始化流程结束之后,对它进行一些特殊配置的类。这个接口和后面的一些接口都可以看出Spring设计的原则,那就是先定义好某个功能的标准处理流程,但也提供了进行定制化处理的接口,并通过先注册后调用的方式很有秩序的进行处理。
registerBeanPostProcessors:实例化并调用所有已经注册的BeanPostProcessor。BeanPostProcessor和BeanFactoryPostProcessor类似,只不过一个是针对bean factory,一个是针对具体的bean。它定义了两个方法postProcessBeforeInitialization和postProcessAfterInitialization。前者会在某个bean的初始化方法(InitializingBean接口的afterPropertiesSet方法,或自定义的init-method)调用之前被调用。后者则是在初始化方法调用之后调用。
initMessageSource方法初始化message source。
initApplicationEventMulticaster方法初始化应用事件多播器。应用事件多播器是管理一系列ApplicationListener的,并且发布事件给它们。
onRefresh空方法,留给子类扩展。
registerListeners是获取所有实现了ApplicationListener的类,并注册它们,同时将一些早起的Application event发布出去。
finishBeanFactoryInitialization终于到最重要的一步了,就是完成Context中的bean factory的初始化,并初始化所有的还未初始化的单例bean。这个方法首先又对bean factory做了一系列设置,之后调用DefaultListableBeanFactory的preInstantiateSingletons方法对bean进行了初始化。
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
32
33
34
35
36
37
public void preInstantiateSingletons () throws BeansException {
if (this .logger.isDebugEnabled()) {
this .logger.debug("Pre-instantiating singletons in " + this );
}
List<String> beanNames = new ArrayList<String>(this .beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run () {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
基本过程是就是遍历所有的bean definition,判断不是抽象类,同时是单例,并且没有设置lazy-init的就进行处理。处理时又分为是否是工厂类和不是工厂类进行处理。普通bean直接调用getBean进行处理,工厂bean则要进行一些处理,判断是否是立即加载的。
getBean内部直接调用了doGetBean方法,doGetBean中最终调用了createBean方法来创建一个bean,createBean中调用了doCreateBean来实际创建一个bean。
Spring是通过一个BeanWrapper接口来包裹我们实际要创建的类型的bean,这也是一种比较常见的设计模式,就是通过包装类来提供一些额外的功能。BeanWrapper的实现类主要是实现了Bean的属性编辑器的功能。doCreateBean做的事情比较杂,后续有时间再专门分析。
finishRefresh方法主要是完成刷新,主要做了一些善后工作。
通过对ClassPathXmlApplicationContext的启动过程的分析,我们可以总结一些规律。一是Spring的应用上下文的类体系设计得比较复杂,也因此显得很强大和完善。二是标准流程和扩展流程相分离,给使用者的扩展留出了足够的空间。三是采用了很多内部缓存类,比如缓存了bean的定义,bean实例,bean的name等都用了不同的集合做了专门的缓存。特别是针对单例bean的三级缓存,可以解决循环依赖的问题。