下面扩展下上文步骤涉及的一些功能点
Context、Listener、BeanPostProcesser分析
Context
上下文是啥?Spring最核心的功能是IOC容器,BeanFactory是一个顶级接口,只提供基础功能。ApplicationContext(也是接口)继承自BeanFactory,同时增加了很多接口用于扩展功能如事件监听、加载文件资源、国际化等。上下文一般指ApplicationContext的实现以及其派生实现,用以支持更多扩展功能,使spring的bean工厂越来越强大。
Listener
先说说监听器Listener,监听什么呢?监听上下文,也就是上面提到的applicationContext,上下文会在应用启动过程中发布很多事件(如启、停、刷新事件等,顶级类为ApplicationEvent),而上下文监听器注册到上下文中以后,会监听自己关注的事件,然后做一些处理(仅为匹配的事件对象调用侦听器)。
上下文抽象类AbstractApplicationContext提供了发布事件的方法:
public void publishEvent(ApplicationEvent event) {
this.publishEvent(event, (ResolvableType)null);
}
而ApplicationListener接口提供了监听事件的方法,前面提到的11个监听器都会实现各自的监听方法,区别在于事件可能不一样,但都继承与ApplicationEvent。
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
挑一个监听器看看都做了什么:ClearCachesApplicationListener
上下文加载完成后清理缓存,具体就是清理掉反射工具类缓存以及调用类加载器的clearCache方法逐级清理加载器缓存。
/**
* {@link ApplicationListener} to cleanup caches once the context is loaded.
*
* @author Phillip Webb
*/
class ClearCachesApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ReflectionUtils.clearCache();
clearClassLoaderCaches(Thread.currentThread().getContextClassLoader());
}
private void clearClassLoaderCaches(ClassLoader classLoader) {
if (classLoader == null) {
return;
}
try {
Method clearCacheMethod = classLoader.getClass().getDeclaredMethod("clearCache");
clearCacheMethod.invoke(classLoader);
}
catch (Exception ex) {
// Ignore
}
clearClassLoaderCaches(classLoader.getParent());
}
}
何时清理呢?自然是上下文加载完成后主动广播发布一个事件:我加载好了!监听器收到广播通知发现正是自己监听的事件,好家伙,等你好久了,终于可以干活了。
再看下上下文何时何地发布这个事件的,finishRefresh在刷新容器的最后阶段执行,发布一个ContextRefreshedEvent事件
protected void finishRefresh() {
this.clearResourceCaches();
this.initLifecycleProcessor();
this.getLifecycleProcessor().onRefresh();
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
LiveBeansView.registerApplicationContext(this);
}
何时注册到上下文开始监听呢?在prepareContext准备上下文时就开始陆续注册不同的监听器。准备上下文后面详解,这里大致介绍下,调用相关postProcesser处理context, 然后应用applyInitializers(此时有几个initializer会加入到监听者里),调用 listeners.contextPrepared(context)执行initializer监听器的处理动作; prepareContext的末尾调用listeners.contextLoaded(context);将SpringApplication 的applicationListener(创建时匹配到的,上文提到的11个)加入到上下文监听者里(后续刷新容器时还有些新的监听器会加入监听者集合里),然后当上下文发布事件是,监听者开始干活。
BeanPostProcesser
BeanPostProcessor简介
俗称bean的后置处理器,spring容器启动过程中多次参与bean实例化,不同时机不同切入点执行匹配的处理器对bean进行增强处理。
/**
* Factory hook that allows for custom modification of new bean instances —
* for example, checking for marker interfaces or wrapping beans with proxies.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* <h3>Registration</h3>
* <p>An {@code ApplicationContext} can autodetect {@code BeanPostProcessor} beans
* in its bean definitions and apply those post-processors to any beans subsequently
* created. A plain {@code BeanFactory} allows for programmatic registration of
* post-processors, applying them to all beans created through the bean factory.
*
* <h3>Ordering</h3>
* <p>{@code BeanPostProcessor} beans that are autodetected in an
* {@code ApplicationContext} will be ordered according to
* {@link org.springframework.core.PriorityOrdered} and
* {@link org.springframework.core.Ordered} semantics. In contrast,
* {@code BeanPostProcessor} beans that are registered programmatically with a
* {@code BeanFactory} will be applied in the order of registration; any ordering
* semantics expressed through implementing the
* {@code PriorityOrdered} or {@code Ordered} interface will be ignored for
* programmatically registered post-processors. Furthermore, the
* {@link org.springframework.core.annotation.Order @Order} annotation is not
* taken into account for {@code BeanPostProcessor} beans.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor
允许对一个新的bean实例进行自定义修改。Factory hook,工厂沟子…意思是采用hook的方式实现的,hook—一种程序处理机制,据说在windows系统中很常见很重要(咱也不了解,不细究)。
使用hook方式对bean的新创建的实例进行自定义修改是什么意思呢?或许使用回调来描述更容易一些,在bean实例化之前和之后,提供回调方法,对需要实例化的bean为所欲为,可以修改,也可以替换(重新生成一个,如aop代理),当然 也可以啥都不做!具体想干啥,取决于这个“自定义”,框架帮我们自定义了一些处理,我们自己也可以使用这个特性做些小动作,如在bean实例化前后对其进行监控,或满足特定条件给他换个子类实例,或者…仅仅想打印一句:hello world。
BeanPostProcessor原理
如何回调呢,这个就要看spring实例化bean时的初始化代码了,spring启动过程中初始化单例bean时,先创建对象实例,然后填充属性,然后执行初始化方法。下面看下如何执行初始化方法
前置处理方法:
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
后置处理方法
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
入参传的是已经实例化且填充过属性的bean,可以看到如果经过前置或者后置的处理,一旦有所返回,则会影响最终初始化方法的返回值,也就允许可以修改甚至替换实例化对象的。(多提一句:java是值传递,传入的是对象地址,可以根据入参对象地址定位并修改原始对象,如果需要替换对象,则只能返回新对象,挤掉原有对象)。
BeanPostProcessor扩展
关于bean的后置处理其实不仅限于此,这里只是初始化时对bean进行前后处理,springbean创建过程中其实不止一处用到了bean后置处理,只不过实现的都是BeanPostProcessor的派生,毕竟初始化的前后处理也只能用到初始化时,前面提到的创建,填充其实也有类似需求。初次看源码时,对代码中各种调用BeanPostProcessor进行处理看的那叫一个云里雾里,其实仔细看执行的方法名,也是有差异的,下面列举几个例子:
实例化后,填充属性前,需要解决循环依赖,因为可能存在aop循环依赖,需要提前生成代理对象,这里就用到了一种bean的后处理:SmartInstantiationAwareBeanPostProcessor. getEarlyBeanReference
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
之后的填充属性,又用到了一种bean后处理器:InstantiationAwareBeanPostProcessor,而且还是个双黄蛋。
- InstantiationAwareBeanPostProcessor. postProcessAfterInstantiation
- InstantiationAwareBeanPostProcessor. postProcessProperties
如果1中提前处理了属性注入,则直接返回,即属性填充结束
否则使用自动注入处理器处理属性填充操作。
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}