容器扩展点的概念
Spring的IoC部分被设计成可扩展的。应用程序开发者通常不需要继承各种各样的BeanFactory或者ApplicationContext的实现类(BeanFactory和ApplicationContext都是接口)。通过插入(plug in)特殊集成接口的实现,可以无限扩展Spring IoC容器。 说白了,扩展点,就是允许你在不修改Spring源码的情况下,通过实现一些Spring预留的接口来把你自己的代码融入到Spring IoC容器初始化的过程中。接下来将详细介绍所有这些不同的集成接口。
1.使用BeanPostProcessors来自定义(customize)beans
我们将首先来看看BeanPostProcessor接口这个扩展点。这个接口定义了一些回调方法(callback methods),让作为应用开发者的你可以通过实现(implement)它来提供你自己的实例化(指实例化bean)逻辑(instantiation logic),或者说覆盖容器默认的实例化逻辑,还有提供你自己依赖解析逻辑等等。如果你想在spring容器完成实例化、配置、初始化(initialize)一个bean后自定义一些特殊的逻辑,你可以通过插入一个或多个BeanPostProcessor的实现类。
可以配置多个BeanPostProcessor。还可以通过设置它们的顺序(order)属性来决定它们的执行顺序,前提是BeanPostProcessor实现了Ordered接口。
注意:BeanPostProcessor操作的是bean(对象)实例,也就是说Spring的IoC容器为你实例化了一个bean,然后BeanPostProcessors们才有机会去改变bean实例。 如果你想改变bean的定义(即BeanDefinition,Spring会把bean的定义抽象成BeanDefinition),你需要去使用BeanFactoryPostProcessor。Spring容器会提前实例化BeanFactoryPostProcessor的实例,再用它去处理BeanDefinition。 另外,BeanPostProcessor的作用域是容器级的,一个BeanPostProcessor只能改变它所在的容器中的bean实例,不能改变别的Spring容器中的bean实例。
org.springframework.beans.factory.config.BeanPostProcessor
接口只有两个回调方法:
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
当一个类注册为容器的(bean实例化的)后处理器post-processor,对于每一个容器中的bean实例的创建,都会回调这两个方法。下面是如何向一个Spring容器注册BeanPostProcessor的代码:
ConfigurableBeanFactory factory = new XmlBeanFactory(...);
// now register any needed BeanPostProcessor instances
MyBeanPostProcessor postProcessor = new MyBeanPostProcessor();
factory.addBeanPostProcessor(postProcessor);
// now start using the factory
注意,只有低级的容器,一般是名字中带BeanFactory的容器才需要像上面这样手动注册,高级的容器,如ApplicationContext的实现类会自动帮你完成BeanPostProcessor的注册(可以查看AbstractApplicationContext类的refresh方法中的registerBeanPostProcessors(beanFactory)部分)。
postProcessBeforeInitialization会在initialization方法(例如InitializingBean接口的afterPropertiesSet方法)前调用,postProcessAfterInitialization会在之后调用,如下面的Spring bean生命周期图所示:
post-processor可以对Spring容器准备好的bean的实例做任何它想做的事情,你也可以在BeanPostProcessor的两个回调方法中什么都不做,直接返回。通常我们实现BeanPostProcessor还是想做点什么的,比如可以把一个bean用代理包起来,一些Spring AOP框架的类就是通过实现BeanPostProcessor,然后做一些代理包装逻辑。
BeanPostProcessors和AOP自动代理(auto-proxying) 实现BeanPostProcessor接口的类会被Spring容器特殊对待,所有BeanPostProcessors和它们使用到的bean都会在容器启动的时候实例化,是ApplicationContext特殊启动阶段的一部分。然后,所有的BeanPostProcessors会被排序后注册到容器中,再然后会作用于其他所有bean实例化的过程中。由于AOP自动代理是通过BeanPostProcessor实现的,所以BeanPostProcessor及其直接引用的bean都不可以被自动代理,也就是说不会有切面(aspect)织入(weave)它们。
2.通过BeanFactoryPostProcessors来自定义配置元数据(configuration metadata)
再来看另一个扩展点org.springframework.beans.factory.config.BeanFactoryPostProcessor。这个接口的语义(semantics)和BeanPostProcessor有点像,但是有一个主要的区别:BeanFactoryPostProcessor操作的是bean配置元数据,也就是说,spring IoC容器允许在容器实例化bean之前对bean定义的配置元数据进行修改。
你同样可以定义多个BeanFactoryPostProcessor,然后通过order控制它们的执行顺序。
注意: BeanFactoryPostProcessor的作用域也是容器级的,一个容器里的BeanFactoryPostProcessor不能改变另一个容器中的BeanDefinition。
一个Bean factory后处理器可以手动执行(在低级容器中),也可以自动执行(在高级容器中)。Spring提供了一些现成的beanfactory post-processor,例如:PropertyOverrideConfigurer和 PropertyPlaceholderConfigurer。手动应用BeanFactoryProcessor的代码类似下面的:
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
// bring in some property values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);
同样,用高级容器后就不用手动去写了。
3.用FactoryBeans自定义实例化逻辑
org.springframework.beans.factory.FactoryBean接口的实现类本身就是工厂。
FactoryBean接口是Spring IoC容器实例化逻辑的可插拔点。如果你有一些复杂的实例化逻辑,就可以创建自己的FactoryBean,然后把复杂的实例化逻辑放在里面,再把你的工厂bean放到Spring容器中。
FactoryBean接口提供了三个方法:
//返回这个工厂创建的对象实例,这个实例可以是共享的(单例的),当然这个取决于工厂返回的是singletons还是prototypes
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
在spring的AOP模块中,为了创建代理对象(这个过程还挺麻烦的),是通过配置和调用org.springframework.aop.framework.ProxyFactoryBean
来完成的。这个生成过程可以使用JDK的Proxy,也可以使用CGLib。
参考资料:
1.https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s07.html