记一次transactional注解失效排查

563 阅读2分钟

前言

这次问题排查比较特殊,涉及到公司使用的统一配置中间件,其大致原理是:通过继承AutowiredAnnotationBeanPostProcessor接口(其实现了InstantiationAwareBeanPostProcessor接口,顺序方面,是实现的PriorityOrdered接口),覆盖postProcessPropertyValues方法,实现动态配置(开关、动态改变日志级别等)。而在该方法中,为了获取该中间件自己的配置类(假设为Config.class),调用了applicationContext.getBean(Config.class)。我们问题的根源即出在这里。

transactional注解的实现原理

由切面实现,相关advisor为TransactionAttributeSourceAdvisor,而spring负责管理切面的BeanPostProcessor为InfrastructureAdvisorAutoProxyCreator,它的顺序接口是实现的Ordered。

问题分析

首先来看refresh()中registerBeanPostProcessors()方法的节选:

/**
 * Instantiate and invoke all registered BeanPostProcessor beans,
 * respecting explicit order if given.
 * <p>Must be called before any instantiation of application beans.
 */

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory{
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, truefalse);

    //。。。略去无关部分

    // First, register the BeanPostProcessors that implement PriorityOrdered.
    OrderComparator.sort(priorityOrderedPostProcessors);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

    // Next, register the BeanPostProcessors that implement Ordered.
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
            //。。。略去
 }
由于统一配置的BeanPostProcessor是实现的PriortyOrdered,故而会先一步注册到BeanFactory中,那么当注册实现Order的那些BeanPostProcessor(负责处理切面的BeanPostProcessor)时,就会调用统一配置的BeanPostProcessor中的postProcessPropertyValues,进而去调用applicationContext.getBean(Config.class),而applicationContext.getBean(Class)方法,该方法会扫描容器中的所有bean,当遇到FactoryBean的时候,最终会调用AbstractAutowireCapableBeanFactory#getTypeForFactoryBean()方法 ,这个方法会尝试通过直接调用FactoryBean#getObjectType()方法来做shortCut,但是我当时写的FactoryBean是通过setter注入设置的objectType,导致不能用到这个shortCut。最终导致相关bean在未经切面代 理的情况下完成了初始化。

总结: 问题根本原因在于,在实现了PriorityOrdered的PostBeanProcessor中调用applicationContext.getBean(Class),导致通过setter设置objectType的factoryBean初始化提前。可以通过构造器注入的方式避免该问题。