通过一次事故来复盘Spring中的Bean实例化顺序以及带来的影响,内含大量源码分析

这篇文章主要是为了记录在事故复盘过程中的一些总结。如题,问题的表象就是事务失效了,导致后续的事件发送没成功。

@Servicepublic class AService{    @autoWire    private ApplicationContext context;        @Transactional(rollBack = Exception.class)    public void xxTest(){        // service code        // 事务失效,导致触发事件失败        application.publish(new BEvent())    }}@Componentpublic class BService{  @TransactinoalEventListener  public void eventHander(){    // send mobile  }}

接下来就针对这个现象来一步一步分析。

01

初步分析

这一节,我们通过问题现象进行初步分析,并找到解决方案。

事务不成功,可能导致的原因有以下几个:

  • Service类中,自己调自己的方法。
  • 应用未开启事务支持,@EnableTransactionManagement。
  • Service类作为Bean注入容器的时候,不是代理类。

首先是看代码是否写的有问题,主要看AServicce.xxTest() 方法是否在本类中调用。

@Servicepublic class AService{    @autoWire    private ApplicationContext context;    // 主要观察AService中是否有这样的方法调用        public void xxBTest(){        xxTest();    }        @Transactional(rollBack = Exception.class)    public void xxTest(){        application.publish(new BEvent())    }}

如意料的一样,并没有,因为项目之前是没问题的,是因为改造成SpringBoot 框架后出现的问题。第一点排除。

继续排查第二点,检查应用是否开启事务支持,虽然理论上使用Springboot开发的项目默认会开启事务支持,但还需要进一步用代码来证明。

// 写一个单测@RunWith(SpringRunner.class)@SpringBootTest(classes =Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)public class TransactionTest {    @Autowired    private AService aService;    @Test    public void createDeployment() {        aServicce.test();    }}// Transactional在事务代理方法上debug断点, 只要方法有事务,一定会走到这个方法protected Object invokeWithinTransaction(Method method, @Nullable Class> targetClass,      final InvocationCallback invocation) throws Throwable {      // ......    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {      // 在这里打一个断点      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);      Object retVal;      try {        retVal = invocation.proceedWithInvocation();      }      catch (Throwable ex) {        // target invocation exception        completeTransactionAfterThrowing(txInfo, ex);        throw ex;      }      finally {        cleanupTransactionInfo(txInfo);      }      commitTransactionAfterReturning(txInfo);      return retVal;    }

No problem !! 单测走到了这个断点上,也就是说应用本身是有事务支持的。

我们遇到的是最难的情况:Service类实例化的时候,没有被代理。通过debug调试查看执行时类的状态,和猜测的没错。

spring事务未提交查询不到数据 spring事务失效源码解读_springaop事务逻辑原理

具体原因后面分析,这里先稍微提一下三种情况下Bean会被代理增强:

  • 加了 @Transactional、@Async、@Cacheable(缓存注解) 这几个注解的bean。
  • 被@Aspect注解中@Pointcut切点包含的bean。
  • 在@Configuration标记的类。(非AOP增强)

第三种方式,和我们的问题无关,这里就不详细讲,可以去自行百度搜索Spring @Configuratio Full模式。前面两个都是通过AbstractAdvisorAutoProxyCreator进行增强的,也就是常说的Spring Aop,增强的条件是要找到匹配的Advisor(例如:@Transactional 对应的Advisor 就是

BeanFactoryTransactionAttributeSourceAdvisor

)

/* AbstractAutoProxyCreator 类初始化后处理器,这是Spring Aop增强的核心类 */@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {    if (bean != null) {      Object cacheKey = getCacheKey(bean.getClass(), beanName);      if (this.earlyProxyReferences.remove(cacheKey) != bean) {        // 这是关键方法        return wrapIfNecessary(bean, beanName, cacheKey);      }    }    return bean;}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {      // .... 省略无关代码    // 这里就是根据类型对应的Advices 和 Advisor 增强器    // 逻辑也很简单,遍历容器中所有的Advisor,找到符合条件的    // 比如 @Transactional 就会找到对应的BeanFactoryTransactionAttributeSourceAdvisor    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);    if (specificInterceptors != DO_NOT_PROXY) {      this.advisedBeans.put(cacheKey, Boolean.TRUE);      // 这里创建代理      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;}// 通过@EnableTransactionManagement 会引入这个类@Configurationpublic class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {  /* 这里注入了 BeanFactoryTransactionAttributeSourceAdvisor  这也是为什么开启事务支持要用 @EnableTransactionManagement,Springboot默认引入*/  @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)  public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();    advisor.setTransactionAttributeSource(transactionAttributeSource());    advisor.setAdvice(transactionInterceptor());    if (this.enableTx != null) {      advisor.setOrder(this.enableTx.getNumber("order"));    }    return advisor;  }

根据上面分析,可推测有两个原因导致类没被代理:

  • 生成目标Bean时,BeanFactoryTransactionAttributeSourceAdvisor 还没有注入到容器,AbstractAutoProxyCreator没有匹配到对应的Advisor,所以没有对目标Bean进行增强。
  • 生成目标Bean时,AnnotationAwareAspectJAutoProxyCreator默认代理器还没有注入到容器中。

本质上这两个原因都是,目标Bean实例化顺序变了,导致实例化被提前了。于是google了一下,很多说Springboot整合Shiro会导致实例化顺序改变。果不其然,找到了一个和我们问题一样的,也是由于Shiro整合导致的。

spring事务未提交查询不到数据 spring事务失效源码解读_springaop事务逻辑原理_02

把上面图标注1的ShiroConfig类中的方法删除了,整个项目就恢复正常了。看这情况难道并不是上面推测的两个顺序原因,而是因为使用了自定义的DefaultAdvisorAutoProxyCreator 导致的吗?查看这个类的继承关系图

spring事务未提交查询不到数据 spring事务失效源码解读_实例化_03

可以看到同样也继承了AbstractAutoProxyCreator 类,也就是说,就算用的是这个自定义代理器,同样会去找Advisor对目标类进行增强。本质应该还是实例化顺序导致的。虽然问题已经解决了,但只是知其然而不知所以然。下面进入本文的重点,分析Bean的初始化顺序是受什么影响的。

02

前置知识点

这一节主要讲一些Spring中的知识点,作为问题解决路上的垫脚石

spring事务未提交查询不到数据 spring事务失效源码解读_实例化_04

  • Spring中Bean的类型。
  • Bean实例化的方式。
  • autowired注入之类型查找。
  • BeanPostProcessor 注入的形式和时机。

1. Spring中Bean的类型

Spring中把Bean分成两个类型:

  • 普通Bean
  • FactoryBean---FactoryBean是一种特殊bean主要为了解决真正要使用的Bean构造复杂的情况,真正要使用的bean由FactoryBean的getObject()方法产生。

因此,在Spring 源码很多地方都对FactoryBean进行了特殊处理,比如获取Bean的时候。

// AbstractFactoryprotected  T doGetBean(final String name, @Nullable final Class requiredType,      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {    final String beanName = transformedBeanName(name);    Object bean;    Object sharedInstance = getSingleton(beanName);    if (sharedInstance != null && args == null) {    // ... 省略部分代码      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);    }    protected Object getObjectForBeanInstance(      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {      // 如果不是FactoryBean 直接返回    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {      return beanInstance;    }    Object object = null;    if (mbd == null) {      object = getCachedObjectForFactoryBean(beanName);    }    if (object == null) {      // .... 省略部分代码       // 如果是FactoryBean,通过FactoryBean的getObject方法中获取真正要使用的Bean      object = getObjectFromFactoryBean(factory, beanName, !synthetic);    }    return object;  }

2. Bean实例化的方式

Spring中Bean实例化共有三种方式,可从源码得知

/* createBeanInstance 方法最后都会调用这个类 */public interface InstantiationStrategy {  // 无参数构造器实例化  Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)      throws BeansException;  // 有参数构造器实例化  Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,      Constructor> ctor, Object... args) throws BeansException;  // 工厂方法实例化  Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,      @Nullable Object factoryBean, Method factoryMethod, Object... args)      throws BeansException;}
  • 无参构造函数实例化
@Componet / @Controller / @Service 等public class User{  @autowire  private Dog dog;}
  • 有参构造函数实例化
@Componet / @Controller / @Service 等public class User{  private Dog dog;  public User(Dog dog){    this.dog = dog;      }}
  • 工厂方法实例化(Factory Mehtod)
@Configuration   // 当然这里不加 或者 加非@Configuration 比如 @Component// @Bean标注的都会当做工厂方法模式来区分public class Config{  @Bean  public User(Dog dog){    User user = new User();    user.setDog(dog);    return user;  }}

简单来说,@Bean标注的类实例化方式就是工厂方法实例化模式,这种模式有分成静态实例化 和 普通实例化。顾名思义,静态实例化就是把方法标注为静态方法。两者的区别:

静态实例化:不需要对应的工厂即Config类实例化,就可以实例化目标Bean。

普通实例化:需要对应的工厂先实例化,才可以实例化目标Bean。

spring事务未提交查询不到数据 spring事务失效源码解读_springaop事务逻辑原理_05

其实和普通方法需要有对象才能调用是一个道理。这个区别是我们本次问题的关键点之一。

另外,这里还需要提到的一点是,不管有参构造器方法模式,还是工厂方法模式,本质上都属于方法模式,即参数必须已经在容器中才能实例化(默认参数Bean注入的方式都是通过autowire按类型查找),因此工厂模式和有参构造器模式,都无法避免循环依赖,这不是本文重点,就不详细讲。

3. autowire注入之类型查找

通过前面两个知识点,我们知道了Bean分为两种类型;工厂方法模式的参数默认是通过autowire按类型查找的。那我们详细来看一下,这个查找过程。

public class Config{  @Bean  public User(Dog dog){    User user = new User();    user.setDog(dog);    return user;  }}

现在我们需要实例化这个User,首先要实例化Dog。因此需要通过类型查找来找到对应的Bean,来看一下它的调用图

spring事务未提交查询不到数据 spring事务失效源码解读_实例化_06

最终调用到了findAutowireCandidates()这个方法里,主要作用是通过getBeanNamesForType() 方法获取类型对应的beanName。

// DefaultListableBeanFactory public String[] getBeanNamesForType(@Nullable Class> type, boolean includeNonSingletons, boolean allowEagerInit) {    if (!isConfigurationFrozen() || type == null || !allowEagerInit) {      // 最终都会调用这个方法      return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);    }   // 省略部分代码    resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);    return resolvedBeanNames;  }  private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {    List<String> result = new ArrayList<>();    // Check all bean definitions.    // 这里会检查容器中所有的beanName,找到合条件的    for (String beanName : this.beanDefinitionNames) {            if (!isAlias(beanName)) {        try {          RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);          // Only check bean definition if it is complete.          if (!mbd.isAbstract() && (allowEagerInit ||              (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&                  !requiresEagerInitForType(mbd.getFactoryBeanName()))) {            // In case of FactoryBean, match object created by FactoryBean.            boolean isFactoryBean = isFactoryBean(beanName, mbd);            BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();            boolean matchFound =                (allowEagerInit || !isFactoryBean ||                    (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&                (includeNonSingletons ||                    (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&                isTypeMatch(beanName, type);            // 如果是FactoryBean, 通过isTypeMatch() 再进行判断一次。            if (!matchFound && isFactoryBean) {              // In case of FactoryBean, try to match FactoryBean instance itself next.              beanName = FACTORY_BEAN_PREFIX + beanName;              matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);            }            if (matchFound) {              result.add(beanName);            }          }        }          }    //.... 省略部分代码    return StringUtils.toStringArray(result);  }

从上面的代码可以看到,在根据类型获取对应beanNames的时候,会找到容器中所有已解析的bean,逐个判断。判断类型的方式是通过AbstractBeanFactory.isTypeMatch()这个方法,这里面是关键。来通过源码来分析一下

// AbstractBeanFactorypublic boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {    String beanName = transformedBeanName(name);    Object beanInstance = getSingleton(beanName, false);    if (beanInstance != null && beanInstance.getClass() != NullBean.class) {       // 省略部分代码,,bean 已经实例化的场景    }    // bean没有实例化场景        // 判断是否是FactoryBean    if (FactoryBean.class.isAssignableFrom(beanType)) {      if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {        // 进入到这个方法        beanType = getTypeForFactoryBean(beanName, mbd);        if (beanType == null) {          return false;        }      }    }    // 省略部分代码...     // 判断普通Bean    return typeToMatch.isAssignableFrom(beanType);  }      // AbstractAutowireCapableBeanFactory 重写方法  protected Class> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {    // 是否有factoryBeanName 和 factoryMethodName    // 这个factoryBeanName 并不是 FactoryBean类型的beanName    // 而是指对应的工厂的beanName    String factoryBeanName = mbd.getFactoryBeanName();    String factoryMethodName = mbd.getFactoryMethodName();     // 如果有说明FactoryBean是在工厂方法模式里创建的    if (factoryBeanName != null) {      if (factoryMethodName != null) {        BeanDefinition fbDef = getBeanDefinition(factoryBeanName);        if (fbDef instanceof AbstractBeanDefinition) {          AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;          // 工厂方法里的@Bean是没有BeanClass的          if (afbDef.hasBeanClass()) {            Class> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);            if (result != null) {              return result;            }          }        }      }      // 这一段是关键,如果工厂方法的工厂没有实例化,那么直接返回null      if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {        return null;      }    }    return null;  }

通过上面的分析,我们获取到三个信息:

  • 通过参数注入会通过类型找对应的beanNames。
  • 在找的过程中,会遍历所有解析的bean,通过isTypeMatch()方法判断。
  • 在isTypeMatch中会根据Bean的类型不同,针对FactoryBean进行处理,且处理过程中,又区分FactoryBean是否通过工厂模式实例化的。如果是工厂模式,就会判断对应的工厂(@Configuration注解的类)是否实例化,如果实例化了,才会生成实例化目标Bean。

4.BeanPostProcessor 注入的形式和时机。

BeanPostProcessor接口生成的类是Spring框架的一种特殊Bean,主要是为普通bean生成,提供”钩子“。因此在容器启动的时候,会比普通Bean先生成。

// AbstractApplicationContext 容器启动方法public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {      prepareRefresh();      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();      prepareBeanFactory(beanFactory);      try {        postProcessBeanFactory(beanFactory);        invokeBeanFactoryPostProcessors(beanFactory);        // 第三步:这里把实现BeanPostProcessor 接口的Bean实例化放到容器中        registerBeanPostProcessors(beanFactory);        initMessageSource();        initApplicationEventMulticaster();        // Initialize other special beans in specific context subclasses.        onRefresh();        // Check for listener beans and register them.        registerListeners();        // 第八步:这里会实例化其他Bean实例化放到容器中        finishBeanFactoryInitialization(beanFactory);        finishRefresh();      }    }  }

从上面的代码可以看出,实现了BeanPostProcessor 接口的Bean 会比其他Bean先实例化。如果正常情况,AbstractAutoPorxy 应该在第三步就实例化了,而出现问题的AService类应该在第八步才会实例化。

public static void registerBeanPostProcessors(      ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {       // 省略部分代码....    // 实例化并注册实现了PriorityOrdered 接口的BeanPostProcessor类    for (String ppName : postProcessorNames) {      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);        priorityOrderedPostProcessors.add(pp);        if (pp instanceof MergedBeanDefinitionPostProcessor) {          internalPostProcessors.add(pp);        }      }      else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {        orderedPostProcessorNames.add(ppName);      }      else {        nonOrderedPostProcessorNames.add(ppName);      }    }    // First, register the BeanPostProcessors that implement PriorityOrdered.    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);    // Next, register the BeanPostProcessors that implement Ordered.    // 实例化并注册实现了Ordered接口的类    List orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());    for (String ppName : orderedPostProcessorNames) {      BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);      orderedPostProcessors.add(pp);      if (pp instanceof MergedBeanDefinitionPostProcessor) {        internalPostProcessors.add(pp);      }    }    sortPostProcessors(orderedPostProcessors, beanFactory);    registerBeanPostProcessors(beanFactory, orderedPostProcessors);    // Now, register all regular BeanPostProcessors.    // 实例化并注册其他正常的beanPostProcessors    List nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());    for (String ppName : nonOrderedPostProcessorNames) {      BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);      nonOrderedPostProcessors.add(pp);      if (pp instanceof MergedBeanDefinitionPostProcessor) {        internalPostProcessors.add(pp);      }    }    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);    // Finally, re-register all internal BeanPostProcessors.    sortPostProcessors(internalPostProcessors, beanFactory);    registerBeanPostProcessors(beanFactory, internalPostProcessors);    // Re-register post-processor for detecting inner beans as ApplicationListeners,    // moving it to the end of the processor chain (for picking up proxies etc).    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));  }

上面代码的逻辑非常清晰明了,即BeanPostProcessor 实例化和注册 都是一批一批的,顺序为 PriortyOrdered  >  Ordered > Regular。

03

谜底揭晓

有了第二节的前置知识,这一节把前面那些知识串接起来,把问题出现的谜底揭晓。再重新回顾一下(回看一下第一节

spring事务未提交查询不到数据 spring事务失效源码解读_实例化_07

),问题出现的现象和问题解决的方式。

  • 现象:应该被增强的类,没有被增强。
  • 解决:删除了ShiroConfig类中的生成DefaultAdvisorAutoProxyCreator 的@Bean 方法。

当不删除的时候,来看一下注册BeanPostProcessor类的debug图。

spring事务未提交查询不到数据 spring事务失效源码解读_springboot默认开启事务吗_08

从图中可以看到有两个关键类,分别是DefaultAdvisorAutoProxyCreator 和 MethodValidationoPostProcessor,这两个类的实例化就是导致问题出现的罪魁祸首。

spring事务未提交查询不到数据 spring事务失效源码解读_User_09

先来看看DefaultAdvisorAutoProxyCreator,它是通过工厂模式实例化的,前面第二节提到了工厂模式实例化的时候,分为静态和普通实例化,其中普通实例化首先需要先把对应的工厂也就是方法所在的类先实例化。因此 DefaultAdvisorAutoProxyCreator 实例化完成之后,ShiroConfig也实例化了。

spring事务未提交查询不到数据 spring事务失效源码解读_spring事务未提交查询不到数据_10

再来看看MethodValidationPostProcessor 这个类,这个类其实不关键,关键在它实例化的顺序在DefaultAdvisorAutoProxyCreator之后,且也是通过工厂模式实例化的。前面第二节提到,工厂方法实例化的情况,如果有参数,一定要先注入参数,注入过程中,需要进行类型查找。查找过程中,就会碰到类型为FactoryBean的ShiroFilterFactoryBean,进行类型判断的时候:

  • 非工厂方法模式:就直接生成Bean。
  • 工厂方法模式,会判断工厂是否存在,如果工厂存在,才会生成Bean。

因此,这里ShiroFilterFactoryBean会被生成Bean,带着把它相关的AService类也带着提前生成了。在AService类生成过程中,DefaultAdvisorAutoProxyCreator 还为注册到BeanPostProcessor集合中,因此不会被代理。

至此,问题分析清楚了。一切的根由在于BeanPostProcessor 通过 @Bean 注入容器的时候,没有使用工厂模式静态实例化(方法上加 static) 导致所在的工厂提前实例化了,继而导致FactoryBean类型的Bean提前生成。 

总结:问题解决虽然重要,但里面蕴含的知识点更重要。