核心类介绍
BeanPostProcessor
该类是在Bean对象已经调用构造函数构造出对象后会使用该接口对应的实现类对象进行处
理,该接口包含两个方法:
Object postProcessBeforeInitialization(Object bean, String beanName) throws
BeansException;
这个方法会在调用bean的init方法(比如bean继承了InitializingBean
接口的afterPropertiesSet
方法或者指定了init-method)之前进行调用,可以对bean进行一些操作,BeanAware相关的操作一般在这个方法内进行调用。
Object postProcessAfterInitialization(@NonNull Object bean, String beanName)
throws BeansException;
这个方法会在调用bean的init方法之后进行调用,默认操作是直接返回bean,即不进行任何处理。
何时需要自己定义Aware接口?
最近我在实现分布式会话的过程中,遇到一个比较诡异的问题"No servlet context set",通过不断的打断点,找到的原因如下:我在代码配置Tomcat的SessionManager时,由于我的Manager注入了一些自定义的component,在正常情况下,Spring bean都是在Tomcat容器初始化之后才进行扫描注册。但是由于我定义的Manager引用了相关bean,导致Spring容器提前扫描了Configuration中的bean。而WebMvcConfigurationSupport
作为一个Configuration,同样对其进行扫描并注册相关bean,由于其实现了ServletContextAware
接口,但是由于Tomcat容器初始化未完成,WebMvcConfigurationSupport
中的servletContext为null,所以定义某些依赖servletContext的bean时,由于Spring在前面进行了Assert.notNull断言,导致抛出"No servlet context set"的异常。
所以为了解决这些问题,我自定义了AwareProcessor延迟bean的注入,下面根据我的需要抽象了一个AbstractGenericAwareProcessor
:
/**
* @author lidelin
*/
public abstract class AbstractGenericAwareProcessor<A> implements BeanPostProcessor, ApplicationContextAware {
protected ApplicationContext applicationContext;
@Override
public Object postProcessBeforeInitialization(@NonNull Object bean, String beanName) throws BeansException {
if (filterBean(beanName, bean)) {
applicationContext.getBeansOfType(getAwareClass())
.forEach((k, v) -> {
processBean(beanName, bean, getAwareClass().cast(v));
});
}
return bean;
}
@Override
public Object postProcessAfterInitialization(@NonNull Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 筛选bean
* @param beanName bean name
* @param bean bean
* @return 是否符合
*/
protected abstract boolean filterBean(String beanName, Object bean);
/**
* 获取需要处理的aware接口的类型
* @return 类型
*/
protected abstract Class<A> getAwareClass();
/**
* 处理符合条件的bean
* @param beanName bean name
* @param bean bean
*/
protected abstract void processBean(String beanName, Object bean, A aware);
}
下面是一个注入RedisTemplate的实现样例:
/**
* @author lidelin
*/
public class RedisTemplateAwareProcessor extends AbstractGenericAwareProcessor<RedisTemplateAware> {
@Override
protected boolean filterBean(String beanName, Object bean) {
return bean instanceof RedisTemplate && "redisTemplate".equals(beanName);
}
@Override
protected Class<RedisTemplateAware> getAwareClass() {
return RedisTemplateAware.class;
}
@Override
@SuppressWarnings("rawtypes")
protected void processBean(String beanName, Object bean, RedisTemplateAware aware) {
aware.setRedisTemplate((RedisTemplate) bean);
}
}