架构师必须掌握的知识——spring容器扩展点
写作意图
spring作为目前最为主流的框架,能掌握它的各个知识点是必不可少的技能,有些知识在业务代码中不经常使用,但在框架开发时会经常用到。这篇文章的知识就是如此,希望这篇文章能起到夯实基础的作用。本人能力有限,理解不当的地方在所难免,希望各位看官指正。
文章阅读建议
文章遵循带着问题阅读的方式,这样能深刻掌握知识。文章会先抛出要解决的问题,引导思考,一步步阐明如何解决问题。
【同时也更新到了头条上https://www.toutiao.com/i6641387847611859464/】
本次主题
本次总结的主题是spring core部分的1.8节内容——容器扩展点
本节要解决的问题_什么时候需要用到容器扩展点
- 先想一想容器解决什么问题?
答:管理beans,实例化beans,获取beans,依据是beans的定义(比如xml,@Bean会解析成Bean的定义对象)
【注意这里我用的是复数beans,因为对于单个bean有生命周期的扩展点,这会在以后文章中介绍。不要搞混了】
知道了容器能做什么,下面我们就能回答以下问题了
- 扩展容器能扩展什么?
答:自定义bean、自定义bean的定义、自定义bean的实例化逻辑。特别是需要在运行时才能确定属性的赋值,需要哪些依赖的问题。比如通过xml可以配置依赖,但是如果我的依赖需要根据引用的jar包不同,依赖不同怎么办。下面是一些其他的例子
【举个例子】:
a. 自定义bean:我希望bean在初始化之前根据一些条件改变bean的属性值,比如有的属性值在某些条件下从环境变量中取值。
b.自定义bean的定义:比如我希望根据一些条件给bean新增一些属性,修改依赖的bean,这些条件在运行时才能判断,比如引入的jar包。
c.自定义bean的实例化逻辑:我希望根据一定条件判断实例化的bean类型
如何扩展
估计你首先会想到继承容器对象,这是很自然的想法,但是遗憾的是官方并不推荐
通过可插拔的接口可以更优雅的实现扩展,我们看看有哪些接口吧
我们再进一步看看他们提供了什么方法
1. BeanPostProcessor
- 为什么叫PostProcessor?
答:bean已经完成了属性赋值的后处理 - postProcessBefore/AfterInitializing方法名透露了什么信息?
答:在bean属性赋值后,初始化之前/后调用(比如init-method,这属于bean的生命周期扩展点,我们会在下一篇文章介绍)
特别注意,由于是容器的扩展点,方法会对所有的bean进行回调,我们需要判断是否是我们需要处理的bean对象,比如:
if (bean instanceof MybatisProperties) { }
【举个例子】
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MybatisProperties) {
MybatisProperties mybatisProperties = (MybatisProperties) bean;
// 根据条件修改mybatisProperties的属性值
if(xxx){
mybatisPropertiess.setXXX(xxx);
}
}
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MybatisProperties) {
MybatisProperties mybatisProperties = (MybatisProperties) bean;
// 包装,或者代理
MybatisProperties mybatisPropertiesProxy = Wrap(mybatisProperties);
return mybatisPropertiesProxy;
}
return bean;
}
2. BeanFactoryPostProcessor
- 也叫做PostProcessor,它是什么时候的后处理?
答:容器被初始化以后,所有的bean的定义被加载到容器后,所有的bean没有被初始化
【举个例子】
transactionManager根据不同的数据源类型,动态定义依赖。这个时候你无法用xml或者@Bean去定义依赖,因为只有运行环境你才能知道引入了哪些数据源
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
String[] transactionManagers = beanFactory
.getBeanNamesForType(TransactionManager.class, true, false);
for (String transactionManager : transactionManagers) {
addTransactionManagerDependencies(beanFactory, transactionManager);
}
}
private void addTransactionManagerDependencies(
ConfigurableListableBeanFactory beanFactory, String transactionManager) {
for (String dependentBeanName : getBeanNamesForType(beanFactory,
"javax.jms.ConnectionFactory")) {
beanFactory.registerDependentBean(transactionManager, dependentBeanName);
}
for (String dependentBeanName : getBeanNamesForType(beanFactory,
"javax.sql.DataSource")) {
beanFactory.registerDependentBean(transactionManager, dependentBeanName);
}
}
3. FactoryBean
-
从名字中能看出什么含义?
答:从factory能看出是负责对象创建的,bean说明它是一个bean(注意不要和BeanFactory弄混了) -
创建对象的时机是什么?
答:既然是spring,时机是获取相应bean的时候
【举个例子】
通过FactoryBean,可以通过ProxyFactory(后面的文章会介绍到)返回一个代理对象,这个对象会产生一个实现了接口的空实现对象,接口可以注入,比如@Autowired。具体的业务逻辑通过MethodInterceptor接口拦截实现
@Override
public synchronized Object getObject() throws Exception {
if (this.proxy == null) {
ProxyFactory factory = new ProxyFactory(MyInterceptor.class, myMethodInterceptor);
this.proxy = factory.getProxy();
}
return this.proxy;
}
@Autowired
private MyInterceptor myInterceptor;
这个时候调用myInterceptor的方法,会被myMethodInterceptor进行拦截,在其中实现业务逻辑
4. 其他特性
本主题的问题已经解决,我们还需要关注这些扩展点的其他一些特性,比如BeanPostProcessor和BeanFactoryPostProcessor他们是由applicationContext自动检测生效的,他们的有效范围只针对某个容器,他们可以用Ordered接口设置执行顺序,延迟初始化设置对其无效等。这些特性总结如下: