由LifecycleBeanPostProcessor引起spr
[toc]
由LifecycleBeanPostProcessor引起spring缓存失效说起
文章来源:临窗旋墨的博客
由LifecycleBeanPostProcessor引起spring缓存失效说起
- springBean 加载时候各方法执行顺序
- shiro引起的种种坑
- springcache初始化过程
- spring事务初始化过程
一 问题场景:
1 shiro 造成springcache失效
spring4.1.6 + springmvc + spring-shiro1.4.2
shiro 造成注入配置文件失效
springboot2.1.7 + spring-shiro1.4.2
shiro造成事务失效?
本人没有关注是否存在这个问题, 但是事务的生效机制和cache理论上差不多
二 解决方案
方式一
- springmvc中把LifecycleBeanPostProcessor移到到其他xml(和mvc级别的xml一起执行)
- springboot中把LifecycleBeanPostProcessor移到到其他@Configuration
方式二
方式三:
- springBoot中注册LifecycleBeanPostProcessor的方法(@Bean)设置为静态方法
三 前置条件
- 了解BeanPostProcessor的工作原理
- 了解shiro的LifecycleBeanPostProcessor的处理过程
- 了解spring的cache初始化流程
- 综合,如何差生交叉影响
bean的加载顺序
- 在web.xml中,ContextLoaderListener和DispatcherServlet的书写顺序不会影响相应的xml文件加载顺序。ContextLoaderListener中的xml先加载,DispatcherServlet中的xml后加载。
- ContextLoaderListener中如果contextConfigLocation通过模糊匹配到多个xml文件时,xml按照文件命名顺序加载。但是如果contextConfigLocation逐个指定了具体加载某个xml,则会按照其指定顺序加载。
- 同一个spring的xml文件中,bean的加载顺序按照书写顺序加载
- 通过component-scan扫描的方式加载bean,在扫描范围内按照class的命名顺序加载
- 如果bean之间的创建存在依赖关系,则被依赖的bean会被优先创建
springBean 方法初始化加载顺序:结果如下
测试过程如下:(代码很简单,略)
新建bean1 实现InitializingBean, DisposableBean,SmartInitializingSingleton重写相关方法,构造方法等
新建bean2 实现BeanPostProcessor,实现接口方法
新增(@Configuration)bean3 配置@bean1(指定初始化/销毁方法),@bean2
run&close springbootApplication
非常重要的结论关于bean初始化和销毁过程中调用的方法的顺序!!!!!!!!!!!!!!!!!!!!!!!!!
1. 构造方法
1.1 BeanPostProcessor#postProcessBeforeInitialization
2. @PostConstruct修饰的方法
3. InitializingBean#afterPropertiesSet
4. 指定的init函数
4.1 BeanPostProcessor#postProcessAfterInitialization
5. SmartInitializingSingleton#afterSingletonsInstantiated
6. DisposableBean#destroy
7 指定的销毁方法
四 shiro的LifecycleBeanPostProcessor到底做了什么?
realm的集成结构
AuthorizingRealm extends AuthenticatingRealm
implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware
LifecycleBeanPostProcessor前置处理方法如下(截取部分代码)
public class LifecycleBeanPostProcessor implements DestructionAwareBeanPostProcessor, PriorityOrdered {
//把本后置处理器在PriorityOrdered级别中优先级置为最低
public LifecycleBeanPostProcessor() {
this(LOWEST_PRECEDENCE);
}
//前置处理逻辑
public Object postProcessBeforeInitialization(Object object, String name) throws BeansException {
if (object instanceof Initializable) {
((Initializable) object).init();
}
return object;
}
}
- 判断对象是否是Initializable的实例,是的话调用init()方法。
- 由于Realm符合这个条件,因此会调用realm的init方法,从而触发realm的实例化。
- 由于自动一relm中注入了授权相关的service(如UserService), 从而导致相关service初始化。
结论:LifecycleBeanPostProcessor会导致realm中注入的service提前初始化。
另外:LifecycleBeanPostProcessor实现了PriorityOrdered,并把order置为最低,表示在所有后置处理器中优先级很高(而且高于同级别的实现PriorityOrdered的处理器)
五 BeanPostProcessor的处理时机
再次回顾下bean初始化和销毁的执行顺序
BeanPostProcessor本身也是一个Bean, 那么它的初始化时机是什么呢?
AbstractApplicationContext#refresh()——>registerBeanPostProcessors(beanFactory)方法会注册BeanPostProcessors:
源码略,摘录部分注释如下:
注册BeanPostProcessorChecker,它在以下情况下记录信息消息:
bean是在BeanPostProcessor实例化期间创建的,即
bean不适合由所有beanPostProcessor处理。
检查可在当前Bean上起作用的BeanPostProcessor个数与总的BeanPostProcessor个数,
如果起作用的个数少于总数打印:
xxx is not eligible for getting processed by all BeanPostProcessors
(for example: not eligible for //auto-proxying)
根据是否实现 PriorityOrdered,Ordered, and the rest来区分BeanPostProcessors
1,注册实现 PriorityOrdered BeanPostProcessors
PriorityOrdered类型的BeanPostProcessor会预初始化
2,注册实现 Ordered BeanPostProcessors
3 注册所有无序(没有实现Ordered/ PriorityOrdered) BeanPostProcessors.
4, 注册所有内部(MergedBeanDefinitionPostProcessor) BeanPostProcessors.
结论:BeanPostProcessor注册顺序如下:
- 实现了PriorityOrdered接口的BeanPostProcessor()
- 实现了Ordered接口的BeanPostProcessor
- 注册无实现任何接口的BeanPostProcessor
- 实现了MergedBeanDefinitionPostProcessor接口的BeanPostProcessor
关于PriorityOrdered:
实现了PriorityOrdered的BeanPostProcessor先于其他BeanPostProcessor,并会影响到其他BeanPostProcessor的autowiring behavior(参见PriorityOrdered接口上的注释)
public interface PriorityOrdered extends Ordered {
}
- Ordered 接口用于spring中相同接口实现类的的排序,
- Ordered 有多个实现类,PriorityOrdered类型的实现类,优先级更高;
六 关于springcache是如何初始化的?
6.1 以springmvc的配置为开始:如何开启springcache
- 配置<cache:annotation-driven />
- 配置一个cacheManager
6.2 从<cache:annotation-driven />标签的解析器开始
spring标签的解析器全都实现了NamespaceHandlerSupport
(来自spring-beans包)
[图片上传失败...(image-3ec3a-1597890905331)]
其中 CacheNamespaceHandler就是用于解析<cache:annotation-driven />标签
public class CacheNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new
AnnotationDrivenCacheBeanDefinitionParser());
registerBeanDefinitionParser("advice", new CacheAdviceParser());
}
}
根据源码,可以看出将下列重要类注册到容器中:
- InfrastructureAdvisorAutoProxyCreator
- AnnotationCacheOperationSource
- CacheInterceptor(方法的拦截器,缓存的逻辑实现)
- BeanFactoryCacheOperationSourceAdvisor(重点关注这个,这个类在创建代理的时候被使用)
6.3 InfrastructureAdvisorAutoProxyCreator:cache逻辑的后置处理器
InfrastructureAdvisorAutoProxyCreator实现了SmartInstantiationAwareBeanPostProcessor接口(此接口集成了BeanPostProcessor接口)
回顾下前文的类生命周期执行顺序
InfrastructureAdvisorAutoProxyCreator部分源码摘录:
//执行顺序对应上文的 4.1 BeanPostProcessor#postProcessAfterInitialization, 在初始化其他bean的时候织入此逻辑
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
...
// 1 获取当前类的所有切面拦截类
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//2 如果拦截类不为空,则需要创建当前类的代理类
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//3.创建代理类
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
- 在获取当前类的所有切面拦截器的时候会获取所有的Advisor,然后过滤出符合当前场景的,也即
BeanFactoryCacheOperationSourceAdvisor
- 创建的代理类,在执行缓存功能的时候,调用代理类的invoke()方法,在invoke()方法中调用CacheInterceptor拦截器的execute()方法,拦截器会使用缓存器(本例中的SimpleCacheManager)来进行具体方法实现。
跳过代码细节,给出的结论是
1)解析<cache:annotation-driven />,将InfrastructureAdvisorAutoProxyCreator注入到Spring容器中,该类的作用是在Spring创建bean实例的时候,会执行其postProcessAfterInitialization()方法,生成bean实例的代理类
2)解析<cache:annotation-driven />,将BeanFactoryCacheOperationSourceAdvisor类注入到Spring容器中,该类的主要作用是作为一个Advisor添加到上述代理类中
3)BeanFactoryCacheOperationSourceAdvisor类拥有对CacheInterceptor的依赖,CacheInterceptor作为一个方法拦截器,负责对缓存方法的拦截,
4)当前类方法调用被拦截到CacheInterceptor后,CacheInterceptor会调用我们在配置文件中配置的CacheManager实现(也就是本例中的SimpleCacheManager),来真正实现缓存功能
七 springboot的缓存原理类似springmvc
从@EnableCaching开始,
引入了@Import({CachingConfigurationSelector.class})
这个类添加了AutoProxyRegistrar.java,ProxyCachingConfiguration.java两个类
过程略,参见文末的“本文参考”。
八 spring(mvc) 事务的启动原理
了解了springcache的原理之后,事务的原理就呼之欲出了,二者基本都是一个理念都是一致的。
8.1 TxNamespaceHandler为入口(tx:annotation-driven)
public class TxNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new
JtaTransactionManagerBeanDefinitionParser());
}
}
- AnnotationDrivenBeanDefinitionParser有个静态内部类AopAutoProxyConfigurer (对应的model 为proxy模式)
- AopAutoProxyConfigurer 注册了四个类
- InfrastructureAdvisorAutoProxyCreator:是不是很眼熟,和springcache那里是一样的
- AnnotationTransactionAttributeSource:解析事务类,得到事务配置相关信息
- TransactionInterceptor:事务拦截器,实现了 Advice、MethodInterceptor 接口。
- BeanFactoryTransactionAttributeSourceAdvisor::实现了 PointcutAdvisor 接口,依赖 TransactionInterceptor 和 TransactionAttributeSourcePointcut。
- 通过spring生成的代理类,在执行事务方法其实调用的是TransactionInterceptor的invoke()方法,类似CacheInterceptor
- 而在TransactionInterceptor的invoke()方法中就是大家熟悉的事务管理器PlatformTransactionManager等相关bean了,参见我些的另外一篇关于spring事务的源码的文章(spring事务入口及核心类)。
九 总结shiro造成springcache和spring事务失效的原因
- shiro配置的LifecycleBeanPostProcessor实现了BeanPostProcessor,并且在前置处理(#postProcessBeforeInitialization)逻辑中调用了实现Initializable(shiro的)接口的javaBean的init方法,Realm刚好实现了这个接口,因此,造成Realm的提前初始化,同时造成注入Realm中的bean的提前初始化。
- 这个顺序对应上文中的顺序号为1.1(非常的提前了,仅次于构造函数)
-
springcache和spring事务的代码织入时机在InfrastructureAdvisorAutoProxyCreator类的后置处理(#postProcessAfterInitialization)逻辑中产生
-
wrapIfNecessary();
- 这个顺序对应上文中的顺序号为4.1
- 其中LifecycleBeanPostProcessor实现了PriorityOrdered,InfrastructureAdvisorAutoProxyCreator实现了Ordered,(PriorityOrdered的优先级大于Ordered),不过这么用不到,因为shiro的是前置处理器,cache和tx是后置处理器。
-
- 因此,LifecycleBeanPostProcessor的前置处理器先执行,造成Realm中的注入的service提前处理化,没有经过cache和tx的后置处理器,因而会导致缓存和事务失效。
- 所以,在注入Realm中的service上加上@Lazy注解,让它延迟加载是一个不错的处理办法;
TODO
被BeanPostProcessor 提前初始化的bean还会进入其他BeanPostProcessor 吗?
这是一篇没有完成的文章,我还有很多疑问待校验
主要的原因是对很多原理性的东西不熟悉, 我会在后续的时间里,慢慢的一点点的补充吧,
本文参考:
文章来源:临窗旋墨的博客