美文网首页我读[不懂]源码
由LifecycleBeanPostProcessor引起spr

由LifecycleBeanPostProcessor引起spr

作者: 临窗旋墨 | 来源:发表于2020-08-20 10:29 被阅读0次

    [toc]

    由LifecycleBeanPostProcessor引起spring缓存失效说起

    文章来源:临窗旋墨的博客

    由LifecycleBeanPostProcessor引起spring缓存失效说起

    1. springBean 加载时候各方法执行顺序
    2. shiro引起的种种坑
    3. springcache初始化过程
    4. 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的加载顺序

    1. 在web.xml中,ContextLoaderListener和DispatcherServlet的书写顺序不会影响相应的xml文件加载顺序。ContextLoaderListener中的xml先加载,DispatcherServlet中的xml后加载。
    2. ContextLoaderListener中如果contextConfigLocation通过模糊匹配到多个xml文件时,xml按照文件命名顺序加载。但是如果contextConfigLocation逐个指定了具体加载某个xml,则会按照其指定顺序加载。
    3. 同一个spring的xml文件中,bean的加载顺序按照书写顺序加载
    4. 通过component-scan扫描的方式加载bean,在扫描范围内按照class的命名顺序加载
    5. 如果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;
        }   
    }
    
    1. 判断对象是否是Initializable的实例,是的话调用init()方法。
    2. 由于Realm符合这个条件,因此会调用realm的init方法,从而触发realm的实例化。
    3. 由于自动一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注册顺序如下:

    1. 实现了PriorityOrdered接口的BeanPostProcessor()
    2. 实现了Ordered接口的BeanPostProcessor
    3. 注册无实现任何接口的BeanPostProcessor
    4. 实现了MergedBeanDefinitionPostProcessor接口的BeanPostProcessor

    关于PriorityOrdered:

    实现了PriorityOrdered的BeanPostProcessor先于其他BeanPostProcessor,并会影响到其他BeanPostProcessor的autowiring behavior(参见PriorityOrdered接口上的注释)

    public interface PriorityOrdered extends Ordered {
    }
    
    • Ordered 接口用于spring中相同接口实现类的的排序,
    • Ordered 有多个实现类,PriorityOrdered类型的实现类,优先级更高;

    六 关于springcache是如何初始化的?

    6.1 以springmvc的配置为开始:如何开启springcache

    1. 配置<cache:annotation-driven />
    2. 配置一个cacheManager

    6.2 从<cache:annotation-driven />标签的解析器开始

    spring标签的解析器全都实现了NamespaceHandlerSupport(来自spring-beans包)

    NamespaceHandlerSupport实现类

    [图片上传失败...(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;
        }
    
    1. 在获取当前类的所有切面拦截器的时候会获取所有的Advisor,然后过滤出符合当前场景的,也即BeanFactoryCacheOperationSourceAdvisor
    2. 创建的代理类,在执行缓存功能的时候,调用代理类的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事务失效的原因

    1. shiro配置的LifecycleBeanPostProcessor实现了BeanPostProcessor,并且在前置处理(#postProcessBeforeInitialization)逻辑中调用了实现Initializable(shiro的)接口的javaBean的init方法,Realm刚好实现了这个接口,因此,造成Realm的提前初始化,同时造成注入Realm中的bean的提前初始化。
      • 这个顺序对应上文中的顺序号为1.1(非常的提前了,仅次于构造函数)
    2. springcache和spring事务的代码织入时机在InfrastructureAdvisorAutoProxyCreator类的后置处理(#postProcessAfterInitialization)逻辑中产生
      1. wrapIfNecessary();

      2. 这个顺序对应上文中的顺序号为4.1
      3. 其中LifecycleBeanPostProcessor实现了PriorityOrdered,InfrastructureAdvisorAutoProxyCreator实现了Ordered,(PriorityOrdered的优先级大于Ordered),不过这么用不到,因为shiro的是前置处理器,cache和tx是后置处理器。
    3. 因此,LifecycleBeanPostProcessor的前置处理器先执行,造成Realm中的注入的service提前处理化,没有经过cache和tx的后置处理器,因而会导致缓存和事务失效。
    4. 所以,在注入Realm中的service上加上@Lazy注解,让它延迟加载是一个不错的处理办法;

    TODO

    被BeanPostProcessor 提前初始化的bean还会进入其他BeanPostProcessor 吗?

    这是一篇没有完成的文章,我还有很多疑问待校验

    主要的原因是对很多原理性的东西不熟悉, 我会在后续的时间里,慢慢的一点点的补充吧,


    本文参考:

    文章来源:临窗旋墨的博客

    相关文章

      网友评论

        本文标题:由LifecycleBeanPostProcessor引起spr

        本文链接:https://www.haomeiwen.com/subject/qjhrlktx.html