引
上一节分析了<tx:annotation-driven/>
标签的解析过程,并简单介绍了InfrastructureAdvisorAutoProxyCreator,这一节接着分析Spring事物的实现过程,InfrastructureAdvisorAutoProxyCreator注册后的后续操作,InfrastructureAdvisorAutoProxyCreator实现了BeanPostProcessor接口,那么接下来我们就以BeanPostProcessor接口的postProcessBeforeInstantiation方法和postProcessAfterInitialization为入口,开始今天的分析。(不了解BeanPostProcessor或者bean生命周期的同学可以参考前面的章节,都有介绍)
接下来的分析可能在分析AOP的时候有过介绍,但是大家要注意分析其中的一些不同点。
postProcessBeforeInstantiation方法在之前已经有过详细的介绍,这里我们不再分析,重点看postProcessAfterInitialization。而且接下来很大一部分的方法之前都有分析过,我们只分析事物标签提取的过程。其他的过程不在详解。
2.postProcessAfterInitialization方法简析
/** * 如果bean被子类标识为要代理的bean,则使用配置的拦截器创建代理。 * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { // 为beanName和beanClass构建缓存key Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // 包装bean(创建代理的入口方法) return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 1、如果已经处理过或者不需要创建代理,则返回 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 2、创建代理 // 2.1 根据指定的bean获取所有的适合该bean的增强 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { // 2.2 为指定bean创建代理 this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } // 3、缓存 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
这里又涉及到了增强的提取,套路还是一样,先找出所有的增强,再找出适合当前类的增强。
3.查找适合当前类的增强
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { /** * 获取符合条件的增强并返回 */ List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); // 如果获取到的增强是个空的集合,则返回DO_NOT_PROXY-->空数组 if (advisors.isEmpty()) { return DO_NOT_PROXY; } // 将获取到的增强转换为数组并返回 return advisors.toArray(); }
/** * 为当前bean获取所有需要自动代理的增强 */ protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 1、查找所有候选增强 List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 2、从所有增强集合中查找适合当前bean的增强 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 3、在eligibleAdvisors集合首位加入ExposeInvocationInterceptor增强 // ExposeInvocationInterceptor的作用是可以将当前的MethodInvocation暴露为一个thread-local对象,该拦截器很少使用 // 使用场景:一个切点(例如AspectJ表达式切点)需要知道它的全部调用上线文环境 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { // 4.对增强进行排序 eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
3.1 查找所有增强
public List<Advisor> findAdvisorBeans() { // Determine list of advisor bean names, if not cached already. // 获取缓存的增强 String[] advisorNames = this.cachedAdvisorBeanNames; // 缓存增强为空,则重新查找增强并缓存 if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! // 从当前BeanFactory中获取所有类型为Advisor的bean advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } // 当前BeanFactory中没有类型为Advisor的bean则返回一个空的集合 if (advisorNames.length == 0) { return new ArrayList<>(); } // 循环所有获取到的bean List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { // 跳过正在创建的增强 if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isDebugEnabled()) { logger.debug("Skipping currently created advisor '" + name + "'"); } } else { try { // 通过getBean方法获取bean实例 advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { // 跳过正在创建的增强 if (logger.isDebugEnabled()) { logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // Ignore: indicates a reference back to the bean we're trying to advise. // We want to find advisors other than the currently created bean itself. continue; } } throw ex; } } } } return advisors; }
注意这里与之前分析AOP的不同点
此处,我们没有在配置文件、类中描述任何增强增强信息(例如:在之前的AOP分析中,我们在DogAspect类上开启了@Aspect),那么此处应该获取到什么样的增强信息呢?
这时候就用到了上一节分析的内容了,在上一节手动向容器中注册了TransactionInterceptor,那么这里获取到的增强就是TransactionInterceptor,并通过getBean方法将其实例化。那么到这里,我们已经拿到了所有增强(如果没有配置其他的增强的话)
3.2 查找适合当前类的增强
这里其他的非关键代码就不粘贴了,前面都有过介绍,直接来看canApply方法(不了解其中过程的同学可以参考前面AOP的介绍),在canApply方法中有这样的代码片段:
// 5、循环代理目标的所有接口和实现类的所有方法并调用matches方法做匹配判断 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? // 如果上一步得到的introductionAwareMethodMatcher对象不为空,则使用该对象的matches匹配 introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : // 否则使用Pointcut的methodMatcher对象做匹配 methodMatcher.matches(method, targetClass)) { return true; } } }
这里会调用methodMatcher.matches(method, targetClass)完成目标类方法的匹配工作,并在此方法里完成了事物标签(注解)的提取工作。
public boolean matches(Method method, Class<?> targetClass) { if (TransactionalProxy.class.isAssignableFrom(targetClass)) { return false; } // 获取事物属性源 TransactionAttributeSource tas = getTransactionAttributeSource(); // 如果获取事物属性源为空,或者没有提取到事物标签,那么返回false,表示改目标类不会被事物增强代理 // 在getTransactionAttribute完成了事物标签的提取工作 return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); }
4.事物标签(注解)提取
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) { if (method.getDeclaringClass() == Object.class) { return null; } // 1.检查是否缓存过该方法上的事物标签 // First, see if we have a cached value. Object cacheKey = getCacheKey(method, targetClass); Object cached = this.attributeCache.get(cacheKey); if (cached != null) { // Value will either be canonical value indicating there is no transaction attribute, // or an actual transaction attribute. // 判断缓存的事物标签,如果缓存的方法上没有具体的事物标签,返回null;否则返回对应的事物标签 if (cached == NULL_TRANSACTION_ATTRIBUTE) { return null; } else { return (TransactionAttribute) cached; } } // 2.如果没有缓存的事物标签,则重新提取事物标签并缓存 else { // We need to work it out. // 提取事物标签 TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass); // Put it in the cache. // 缓存事物标签 if (txAttr == null) { this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); } else { String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass); if (txAttr instanceof DefaultTransactionAttribute) { ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification); } if (logger.isDebugEnabled()) { logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr); } this.attributeCache.put(cacheKey, txAttr); } return txAttr; } }
该方法比较简单,我们继续看computeTransactionAttribute提取事物标签的过程:
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) { // Don't allow no-public methods as required. // 1.如果设置了只允许公共(public)方法允许定义事物语义,而该方法是非公共(public)的则返回null // allowPublicMethodsOnly --> 该方法默认返回false,即允许非公共方法定义事物语义 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // The method may be on an interface, but we need attributes from the target class. // If the target class is null, the method will be unchanged. // 2.如果method是一个接口,那么根据该接口和目标类,找到目标类上的对应的方法(如果有) // 注意:该原始方法不一定是接口方法 Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); // 3.提取事物标签 // First try is the method in the target class. // 3.1 首先尝试从目标类的方法上提取事物标签 TransactionAttribute txAttr = findTransactionAttribute(specificMethod); if (txAttr != null) { return txAttr; } // Second try is the transaction attribute on the target class. // 3.2 其次,尝试从目标类上提取事物标签 txAttr = findTransactionAttribute(specificMethod.getDeclaringClass()); if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { return txAttr; } // 3.3 如果从目标类上提取的方法,不等于原始方法 todo lyc 这里还要再次分析下 if (specificMethod != method) { // Fallback is to look at the original method. // 首先尝试从原始方法上提取事物标签 txAttr = findTransactionAttribute(method); if (txAttr != null) { return txAttr; } // Last fallback is the class of the original method. // 最后尝试从原始方法的实现类方法上提取事物标签 txAttr = findTransactionAttribute(method.getDeclaringClass()); if (txAttr != null && ClassUtils.isUserLevelMethod(method)) { return txAttr; } } return null; }
这里我们看到,方法上的事物注解是优先于类上的事物注解的。而且这部分的处理,分为了两个部分:从给定的类或方法上提取事物注解。
/** * 从给定的方法上提取事物标签 * @param method the method to retrieve the attribute for * @return */ @Override @Nullable protected TransactionAttribute findTransactionAttribute(Method method) { return determineTransactionAttribute(method); } /** * 从给定的类上提取事物标签 * @param clazz the class to retrieve the attribute for * @return */ @Override @Nullable protected TransactionAttribute findTransactionAttribute(Class<?> clazz) { return determineTransactionAttribute(clazz); }
但是这两个方法最终会调用同一个方法来获取事物标签:
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) { for (TransactionAnnotationParser annotationParser : this.annotationParsers) { TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae); if (attr != null) { return attr; } } return null; }
这里又涉及到三个事物注解转换器,SpringTransactionAnnotationParser、JtaTransactionAnnotationParser、Ejb3TransactionAnnotationParser,我们只关心SpringTransactionAnnotationParser的转换就行了。接下来以SpringTransactionAnnotationParser为例看具体的事物标签提取和转换:
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { // 1.提取事物注解标签(该方法较长就不粘贴了,感兴趣的自己看一下,比较简单) AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( ae, Transactional.class, false, false); // 2.解析事物标签 if (attributes != null) { return parseTransactionAnnotation(attributes); } else { return null; } }
/** * 解析事物标签属性 */ protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); // 1.解析propagation属性 Propagation propagation = attributes.getEnum("propagation"); rbta.setPropagationBehavior(propagation.value()); // 2.解析isolation属性 Isolation isolation = attributes.getEnum("isolation"); rbta.setIsolationLevel(isolation.value()); // 3.解析timeout属性 rbta.setTimeout(attributes.getNumber("timeout").intValue()); // 4.解析readOnly属性 rbta.setReadOnly(attributes.getBoolean("readOnly")); // 5.解析value属性 rbta.setQualifier(attributes.getString("value")); ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>(); // 6.解析rollbackFor属性 Class<?>[] rbf = attributes.getClassArray("rollbackFor"); for (Class<?> rbRule : rbf) { RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule); rollBackRules.add(rule); } // 7.解析rollbackForClassName属性 String[] rbfc = attributes.getStringArray("rollbackForClassName"); for (String rbRule : rbfc) { RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule); rollBackRules.add(rule); } // 8.解析noRollbackFor属性 Class<?>[] nrbf = attributes.getClassArray("noRollbackFor"); for (Class<?> rbRule : nrbf) { NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule); rollBackRules.add(rule); } // 9.解析noRollbackForClassName属性 String[] nrbfc = attributes.getStringArray("noRollbackForClassName"); for (String rbRule : nrbfc) { NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule); rollBackRules.add(rule); } rbta.getRollbackRules().addAll(rollBackRules); return rbta; }
到这里,我们就完成了目标类或者目标类方法上的事物标签提取了。
代理的创建等工作之前已经分析过,大家可以参考AOP的分析自己跟踪代码,接下来的章节,我们分析Spring事物代理方法的调用,解析Spring是如何实现事物的。