Contents

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) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
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
/** Map of bean definition objects, keyed by bean name */
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);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
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的三级缓存,可以解决循环依赖的问题。

Contents