前言
在 Spring 框架中,每个应用程序上下文(ApplicationContext)管理着一个 BeanFactory,BeanFactory 主要负责 Bean 定义的保存、Bean 的创建、Bean 之间依赖的自动注入等。应用程序上下文则是对 BeanFactory 和 Bean 的生命周期中的各个环节进行管理,并且提供扩展接口允许用户对 BeanFactory 和 Bean 的各个阶段进行定制,本文从以下三个点进行切入讲解。
refresh 阶段
应用程序上下文刷新操作最终调用的是 AbstractApplicationContext 的 refresh 方法,其核心执行步骤如下图所示。
无论是解析 XML 作为 Bean 来源的 ClassPathXmlApplicationContext 还是基于扫描注解类作为 Bean 来源的 AnnotationConfigApplicationContext,在刷新上下文的过程中最终都会走这个流程,不同在于这两者覆盖的该流程中的一些方法可能会有不同,其实这个属于设计模式里面的模板模式。
获取 BeanFactory
如上图中,步骤(1)获取一个 BeanFactory,对应 ClassPathXmlApplicationContext 应用程序上下文来说,这个步骤首先创建了一个 DefaultListableBeanFactory,然后解析配置 Bean 的 XML,并把 Bean 定义注册到 BeanFactory,内部主要函数为 refreshBeanFactory,代码如下。
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) {
... }
}
可以通过覆盖该步骤内的 refreshBeanFactory 方法,实现自己的 BeanFactory 创建和解析配置文件的 Bean 的策略。
标准初始化配置 BeanFactory
步骤(2)配置步骤(1)创建的 BeanFactory,比如设置 BeanFactory 工厂创建 Bean 时使用什么样的类加载器,默认情况下使用线程上下文类加载器(默认为 AppClassLoader)。
这里如果想实现不同的 BeanFactory 创建 Bean,使用不同的 classloader 来实现模块隔离,可以通过在不同的 ClassPathXmlApplicationContext 上调用 setClassLoader 方法来设置不同的 classloader 来实现。
另外步骤(2)还向 BeanFactory 添加了一个 BeanPostProcessor 的实现类 ApplicationContextAwareProcessor,这个后面会讲到,代码如下。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.setBeanClassLoader(getClassLoader());
...
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
...
}
自上下文对 BeanFactory 进行个性化定制的扩展
步骤(3)是在步骤(2)对 BeanFactory 进行标准初始化配置后,留出的允许子上下文对 BeanFactory 进行个性化定制的扩展,这时候会加载所有的 Bean 的定义,但是这时候还没有 Bean 被实例化,这时允许注册一些 BeanPostProcessors 类型的 Bean 用来在 Bean 初始化前后做一些事情。
例如 XmlWebApplicationContext 上下文里面的 postProcessBeanFactory 的实现,代码如下。
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
...
}
注册了 ServletContextAwareProcessor,用来把 servletContext 设置到实现了 ServletContextAware 接口的 Bean。
步骤(4)执行用户注册的 BeanFactoryPostProcessor 扩展 Bean,用来对 BeanFactory 中的 Bean 定义进行修改,比如常见的是统一设置某些 Bean 的属性变量值。那么 BeanFactoryPostProcessor 为何物呢?
public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
如上代码 BeanFactoryPostProcessor 是一个接口,有一个方法,该方法参数是 beanFactory,由于通过 beanFactory 可以访问所有的 Bean 的定义,所以当我们实现了该接口,并注入实现类到 Spring 容器后,就可以在实例化 Bean 前对指定的 Bean 定义进行修改或者注册新的 Bean。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
如上代码,BeanDefinitionRegistryPostProcessor 接口继承自 BeanFactoryPostProcessor,它新添加了一个接口,用来在BeanFactoryPostProcessor 实现类中 postProcessBeanFactory 方法执行前再注册一些 Bean 到 beanFactory 中。
基础知识普及完毕后,下面来看步骤(4)做了什么?
步骤(4) 首先执行实现了 BeanDefinitionRegistryPostProcessor 接口的 Bean 的 postProcessBeanDefinitionRegistry 方法,然后再执行实现了 BeanFactoryPostProcessor 接口的 Bean 的 postProcessBeanFactory 方法。由于接口的实现类可能会有多个,如果你想先执行某些接口的方法,可以通过实现 PriorityOrdered 或者 Ordered 接口给每个接口定义一个优先级,另外实现 PriorityOrdered 接口的优先级大于实现 Ordered 的优先级。
比如,基于扫描注解类作为 Bean 来源的 AnnotationConfigApplicationContext,会在 refresh 阶段前注册一个ConfigurationClassPostProcessor,它实现了 BeanDefinitionRegistryPostProcessor、PriorityOrdered 两个接口。
因为实现了第一接口,所以会在步骤(4)的时候执行 postProcessBeanDefinitionRegistry 方法,这个方法内部作用是使用ConfigurationClassParser 解析所有标注有 @Configuration 注解的类,并解析该类里面所有标注 @Bean 的方法和标注 @Import 的bean,并注入这些解析的 Bean 到 Spring上下文容器里面。
因为实现了第二个接口,所以该类有 getOrder 方法返回该类的优先级,这里实现为O rdered.LOWEST_PRECEDENCE,也就是优先级最低。
比如解析 ${...}占位符的 PropertyPlaceholderConfigurer 会在步骤(4)阶段执行 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 方法对 Bean 定义的属性值中 ${...} 进行替换,具体一个例子如下。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>config.properties</value>
</property>
<property name="fileEncoding">
<value>UTF-8</value>
</property></bean><bean id="testPlaceholder" class="zlx.test.annotationConfigApplicationContext.TestImpl">
<property name="name" value="${name}"></property></bean>
如上代码,首先注入了 propertyConfigurer 实例并且配置了属性值来源为 config.properties,并且在注入 TestImpl 实例的时候使用了占位符 "${name}" 来设置 name 属性,其中 config.properties 内容如下:
name=jiaduo
其中 TestImpl 代码如下:
public class TestImpl { private String name; public String getName() { return name;
} public void setName(String name) { this.name = name;
} public void say(){
System.out.println("hello " +name);
}
}
那么当我们通过运行以下代码:
ClassPathXmlApplicationContext cpxa = new ClassPathXmlApplicationContext("bean.xml");
cpxa.getBean("testPlaceholder",TestImpl.class).say();
会输出以下结果:
hello jiaduo
占位符替换的时机就是在步骤(4)执行 PropertyPlaceholderConfigurer 类的 postProcessBeanFactory 方法时候,该方法用 config.properties文件 中 key 为 name 的属性值替换 BeanFactory 里面 Bean 的属性值为 "${name}"的属性。需要注意的是这时候 Bean 还没有被实例化,只是静态的进行属性值替换。
小结:BeanFactoryPostProcessor 后置处理器扩展接口是在 Bean 进行实例化前执行的,它的作用是对 BeanFactory 中 Bean 的定义做修改(比如新增 Bean 的定义,修改已有 Bean 定义,修改 Bean 的属性值等)。
相比 BeanFactoryPostProcessor 是在 Bean 实例化前对 BeanFactory 进行扩展,BeanPostProcessor 是在 Bean 实例化后对 Bean 进行扩展,下面看看 BeanPostProcessor 的接口定义,代码如下。
public interface BeanPostProcessor { //在Bean实例化后,初始化前进行一些扩展操作
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean;
} //在Bean实例化后,初始化后进行一些扩展操作
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean;
}
}
本阶段就是把用户注册的实现了该接口的 Bean 进行收集,然后放入到 BeanFactory 的 beanPostProcessors 属性里面,待后面使用。
为应用上下文子类初始化一些特殊类留出的扩展
refresh 核心执行步骤(6)是为应用上下文子类初始化一些特殊类留出的扩展,例如 SpringBoot 中 AbstractApplicationContext 的子类 EmbeddedWebApplicationContext 应用程序上下文,重写的 onRefresh 方法如下:
protected void onRefresh() { super.onRefresh(); try {
createEmbeddedServletContainer();
} catch (Throwable ex) { throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
如上代码在重写的 onRefresh 方法内创建了内嵌 Web 容器。