一文搞懂 Spring事务是怎么通过AOP实现的 ,让你醍醐灌顶
阅读此文章需要掌握一定的AOP源码基础知识,可以更好的去理解事务,我在另外一篇文章有提过。
spring事务其实就是根据事务注解生成代理类,然后在前置增强方法里获取connection,设置
connection
到threadlocal
,开启事务。再执行原始方法,最后在后置增强方法中判断有无异常来进行事务回滚或提交,再释放连接。
对Spring中的事务功能的代码进行分析,我们先从配置文件开始入手:在配置文件中我们是通过tx:annotation-driven
的方式开启的事务配置,所以我们先从这里开始进行分析,根据以往的经验我们在自定义标签中的解析过程中一定是做了一些操作,于是我们先从自定义标签入手进行分析。使用IDEA搜索全局代码,关键字annotation-driven
,最终锁定在类TxNamespaceHandler
主要查看TxNameSpaceHandler
的init
方法。
- 看源码(
TxNamespaceHandler.java
)
@Override
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
-
源码分析
在上述源码中我们看到了
annotation-driven
这个标识,该行代码也就是说:在遇到诸如tx:annotation-driven
为开头的配置后,Spring都会使用AnnotationDrivenBeanDefinitionParser
类的parse
方法进行解析处理。我们接下来跟踪AnnotationDrivenBeanDefinitionParser
类的parse
方法 -
看源码(
AnnotationDrivenBeanDefinitionParser.java
)
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
registerTransactionalEventListenerFactory(parserContext);
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {
registerJtaTransactionAspect(element, parserContext);
}
} else {
// mode="proxy"
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
-
源码分析
在上面的源码中我们可以看到有对mode属性进行一个解析判断,根据代码,如果我们需要使用Aspect的方式进行事务的切入(Spring中的事务是以AOP为基础的),根据源码的判断条件我们可以看出我们在开始事务配置的时候也可以像如下这种方式进行开启:
<!--开启tx注解-->
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>
事务代理类的创建
根据上面我们也可以知道,Spring事务会根据配置的`mode`不同,会有不同的实现。我们分开探索:
aspectj模式
- 看方法registerJtaTransactionAspect
private void registerTransactionAspect(Element element, ParserContext parserContext) {
String txAspectBeanName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME;
String txAspectClassName = TransactionManagementConfigUtils.TRANSACTION_ASPECT_CLASS_NAME;
//如果没有注册过该类,就新注册一个class为 AnnotationTransactionAspect 的bean
if (!parserContext.getRegistry().containsBeanDefinition(txAspectBeanName)) {
RootBeanDefinition def = new RootBeanDefinition();
def.setBeanClassName(txAspectClassName);
def.setFactoryMethodName("aspectOf");
//把标签里transaction-manager或transactionManager设置的值配置到bean的成员变量transactionManagerBeanName中
registerTransactionManager(element, def);
parserContext.registerBeanComponent(new BeanComponentDefinition(def, txAspectBeanName));
}
}
看一下AnnotationTransactionAspect
类图
我们发现AnnotationTransactionAspect和父类AbstractTransactionAspect都不是正常class,是aspect;之前设置的transactionManagerBeanName属性在TransactionAspectSupport
中
接下来继续追踪AnnotationTransactionAspect
类
其实这里这种用法和@Aspect切面类里定义@Pointcut,@Around的用法一个效果
- 看源码(
AnnotationTransactionAspect.java
)
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {
public AnnotationTransactionAspect() {
super(new AnnotationTransactionAttributeSource(false));
}
/**
* Matches the execution of any public method in a type with the Transactional
* annotation, or any subtype of a type with the Transactional annotation.
*/
/**
* 作用在有Transactional注解或者包含Transactional的注解的public方法
*/
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *);
/**
* Matches the execution of any method with the Transactional annotation.
*/
/**
* 作用在任何有Transactional注解的方法
*/
private pointcut executionOfTransactionalMethod() :
execution(@Transactional * *(..));
/**
* Definition of pointcut from super aspect - matched join points
* will have Spring transaction management applied.
*/
/**
* 父类抽象方法,满足上面任一方法条件且满足父类条件的
*/
protected pointcut transactionalMethodExecution(Object txObject) :
(executionOfAnyPublicMethodInAtTransactionalType() || executionOfTransactionalMethod() ) && this(txObject);
}
- 看源码(
AbstractTransactionAspect.java
)
public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport implements DisposableBean {
/**
* Construct the aspect using the given transaction metadata retrieval strategy.
* @param tas TransactionAttributeSource implementation, retrieving Spring
* transaction metadata for each joinpoint. Implement the subclass to pass in
* {@code null} if it is intended to be configured through Setter Injection.
*/
protected AbstractTransactionAspect(TransactionAttributeSource tas) {
setTransactionAttributeSource(tas);
}
@Override
public void destroy() {
// An aspect is basically a singleton -> cleanup on destruction
clearTransactionManagerCache();
}
//!!!增强逻辑在这,事务也在这实现
@SuppressAjWarnings("adviceDidNotMatch")
Object around(final Object txObject): transactionalMethodExecution(txObject) {
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
try {
//父类方法
return invokeWithinTransaction(methodSignature.getMethod(), txObject.getClass(), new InvocationCallback() {
public Object proceedWithInvocation() throws Throwable {
//就是使用@Around注解时执行ProceedingJoinPoint.proceed()方法
return proceed(txObject);
}
}
);
}
catch (RuntimeException | Error ex) {
throw ex;
}
catch (Throwable thr) {
Rethrower.rethrow(thr);
throw new IllegalStateException("Should never get here", thr);
}
}
/**
* Concrete subaspects must implement this pointcut, to identify
* transactional methods. For each selected joinpoint, TransactionMetadata
* will be retrieved using Spring's TransactionAttributeSource interface.
*/
/**
* 由子类实现具体的pointcut
*/
protected abstract pointcut transactionalMethodExecution(Object txObject);
/**
* Ugly but safe workaround: We need to be able to propagate checked exceptions,
* despite AspectJ around advice supporting specifically declared exceptions only.
*/
private static class Rethrower {
public static void rethrow(final Throwable exception) {
class CheckedExceptionRethrower<T extends Throwable> {
@SuppressWarnings("unchecked")
private void rethrow(Throwable exception) throws T {
throw (T) exception;
}
}
new CheckedExceptionRethrower<RuntimeException>().rethrow(exception);
}
}
}
总结
aspectj模式其实就是定义了一个Aspect,里面定义了切点,针对所有@Transaction注解的方法,并对切点进行@Around增强,会调用父类
TransactionAspectSupport
的invokeWithinTransaction
方法。
proxy模式
我们从默认的配置方式进行分析(也就是不加mode="aspect"
的方式)。对AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
进行分析。首先我们先看一下AopAutoProxyConfigurer
类的configureAutoProxyCreator
方法:
AopAutoProxyConfigurer类属于AnnotationDrivenBeanDefinitionParser的内部类
- 源码分析(
AnnotationDrivenBeanDefinitionParser.java
)
private static class AopAutoProxyConfigurer {
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
// 向IOC注册 registerAutoProxyCreatorIfNecessary 这个类型的Bean
// 具体是在 AopConfigUtils 的 registerAutoProxyCreatorIfNecessary 方法中定义的 registerAutoProxyCreatorIfNecessary
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
// Create the TransactionAttributeSource definition.
// 创建 AnnotationTransactionAttributeSource 类型的Bean
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// Create the TransactionInterceptor definition.
// 创建 TransactionInterceptor 类型的Bean
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// Create the TransactionAttributeSourceAdvisor definition.
// 创建 BeanFactoryTransactionAttributeSourceAdvisor 类型的Bean
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 将上面 AnnotationTransactionAttributeSource 类型Bean注入进上面的Advisor
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
// 将上面 TransactionInterceptor 类型Bean注入进上面的Advisor
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
// 将上面三个Bean注册进IOC中
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
parserContext.registerComponent(compositeDef);
}
}
}
-
源码分析
看上述源码简单总结一下:这里主要是注册了三个Bean,分别是
AnnotationTransactionAttributeSource
、TransactionInterceptor
、BeanFactoryTransactionAttributeSourceAdvisor
,还注册了一个InfrastructureAdvisorAutoProxyCreator
的bean;其中前三个Bean支撑了整个事务的功能。在这里我们简单回顾一下AOP原理:
AOP原理:
AOP中有一个`Advisor`存放在代理类中,而Advisor中有`advise`与`pointcut`信息,每次执行被代理类的方法时都会执行代理类的`invoke`(如果是JDK代理)方法,而invoke方法会根据advisor中的pointcut动态匹配这个方法需要执行的advise链,遍历执行advise链,从而达到AOP切面编程的目的。
需要注意的地方BeanFactoryTransactionAttributeSourceAdvisor.class
,首先看到这个类的继承结构,可以看的出来它其实是一个Advisor
,这个类中有几个关键的地方需要注意一下,在前面提到的一共注册了三个Bean,将AnnotationTransactionAttributeSource
、TransactionInterceptor
这两个属性注入到了这个bean中:代码如下:
// 将上面 AnnotationTransactionAttributeSource 类型Bean注入进上面的Advisor
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
// 将上面 TransactionInterceptor 类型Bean注入进上面的Advisor
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
问题来了:它们被注入成了什么呢?进入到BeanFactoryTransactionAttributeSourceAdvisor
一看便知
@Nullable
private TransactionAttributeSource transactionAttributeSource;
在BeanFactoryTransactionAttributeSourceAdvisor
父类中AbstractBeanFactoryPointcutAdvisor
有这样的一个属性
@Nullable
private String adviceBeanName;
从上面可以大概知道,先将TransactionInterceptor
的BeanName传入到注入到Advisor
,然后将AnnotationTransactionAttributeSource
这个Bean注入到Advisor
。那么这个Source Bean有什么用呢?我们继续追踪BeanFactoryTransactionAttributeSourceAdvisor.java
的源码:
- 看源码(
BeanFactoryTransactionAttributeSourceAdvisor.java
)
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
@Override
@Nullable
protected TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
};
- 源码解析
看到这里应该明白了,这里的Source提供了Pointcut信息,作为存放事务属性的一个类注入到Advisor中。进行到这里我们也知道了这三个BeanAnnotationTransactionAttributeSource
、TransactionInterceptor
、BeanFactoryTransactionAttributeSourceAdvisor
,的作用了。简单来说就是先注册pointcut、advice、advisor,然后将pointcut和advice注入到advisor中,在之后动态代理的时候会使用这个Advisor去寻找每个Bean是否需要动态代理(取决与是否有开启事务),因为Advisor中有pointcut信息。
InfrastructureAdvisorAutoProxyCreator
:在方法的开头,首先调用了AopConfigUtils
去注册了这个Bean,那么这个Bean是做什么的?首先还是看一下这个类的结构。
这个类继承了AbstractAutoProxyCreator
,看到这个名字,结合之前说过的AOP的应该知道它是做什么的了。其次这个类还实现了BeanPostProcessor
接口,凡是实现了这个BeanPostProcessor接口的类,我们首先关注的就是它的postProcessAfterInitialization
方法,这个在其父类也就是刚刚提到的AbstractAutoProxyCreator
去实现的(这里需要知道Spring容器初始化Bean的过程,关于BeanPostProcessor的使用后续讲解,如果不知道只需要了解如果一个Bean实现了BeanPostProcessor接口,当所有Bean实例化且依赖注入之后初始化方法之后会执行这个实现Bean的postProcessAfterInitialization方法)。
接下来进入这个函数:
- 看源码(
AopNamespaceUtils.java
)
public static void registerAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
继续追踪上面的源码中的registerComponentIfNecessary
方法
- 看源码(
AopNamespaceUtils.java
)
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
parserContext.registerComponent(
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
}
}
- 源码分析
对于上面解析来的代码流程已经在AOP中有所分析,可以自行翻看AOP的文章,上面的两个函数主要的目的是注册了InfrastructureAdvisorAutoProxyCreator
类型的Bean,那么注册这个类的目的是什么呢?再次查回顾这个类的层次结构:
分析这个类的层次结构:InfrastructureAdvisorAutoProxyCreator
这个类间接实现了SmartInstantiationAwareBeanPostProcessor
接口,而SmartInstantiationAwareBeanPostProcessor这个接口有继承了InstantiationAwareBeanPostProcessor
接口。也就是说在Spring中,所有Bean实例化时Spring都会保证调用其postProcessAfterInstantiation
方法。其实现是在其父类AbstractAutoProxyCreator
中。
接下来一之前AccountByXMLServiceImpl
为例,当实例化AccountByXmlServiceImpl
的Bean时便会调用下面这个方法,方法如下:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 根据bean的class 和 name构建出一个key 格式:beanClassName_beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果它适合被代理,则需要指定封装bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这里实现的主要目的就是对指定的Bean进行封装,当然首先要确定是否需要封装,检测与封装的工作都委托给了wrapIfNecessary
函数进行:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经处理过
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 无需增强
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 给定的bean类是否是一个基础设施类,基础设施类不应该被代理,或者配置了指定的bean不需要代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取能够应用到当前 Bean 的所有 Advisor(已根据 @Order 排序)
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果有 Advisor,则进行下面的动态代理创建过程
if (specificInterceptors != DO_NOT_PROXY) {
// 如果获取到了增强则需要针对增强进行代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理 JDK 动态代理或者 CGLIB 动态代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 将代理对象的 Class 对象(目标类的子类)保存
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回这个 Bean 对象
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
wrapIfNecessary函数看上去比较复杂,但是逻辑相对还是比较简单的,在wrapIfNecessary函数中主要的工作如下:
- 找出指定bean对应的增强器
- 根据找出的增强器创建代理
听起来挺简单的逻辑,但是Spring又做了哪些复杂的工作呢?对于创建代理的工作,通过之前AOP的文章分析相信大家已经有所熟悉了。但是对于增强器的获取,Spring又是如何操作的呢?
获取对应class/method的增强器
寻找候选的增强
获取指定Bean对应的增强器,其中包含了两个关键字:增强器与对应。也就是说在getAdvicesAndAdvisorsForBean
函数(上面wrapIfNecessary函数里面的方法)中,不仅要找出增强器,而且要判断增强器是否满足要求。接下来看一下源码:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
继续查看一下上面方法中的findEligibleAdvisors函数:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
在findEligibleAdvisors
函数中我们发现了findCandidateAdvisors
和findAdvisorsThatCanApply
这两个函数;其中findCandidateAdvisors
这个函数是寻找候选的增强,我们简单扫一下这个源码:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
接着追踪里面的findAdvisorBeans
函数
- 看源码(
BeanFactoryAdvisorRetrievalHelper.java
)
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.class的类名
// 这里和AspectJ的方式有点不同,AspectJ是获取所有的Object.class,然后通过反射过滤有注解AspectJ的类
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
} else {
try {
//直接获取advisorNames的实例,封装进advisors数组
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.isTraceEnabled()) {
logger.trace("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;
}
- 源码分析
首先这个findAdvisorBeans
函数先通过BeanFactoryUtils类提供的工具方法获取对应的Advisor.class,获取的办法就是使用ListableBeanFactory
中提供的方法:
String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
**在我们讲解自定义标签时曾经注册了一个类型为`BeanFactoryTransactionAttributeSourceAdvisor`的Bean**,而在此bean中我们又注入了另外两个Bean,那么此时这个Bean就会开始被使用,因为**BeanFactoryTransactionAttributeSourceAdvisor**同样也实现了Advisor接口。那么在获取所有增强器时自然也会将此Bean提取出来,并随着其他增强器一起在后续的步骤中被植入代理。
候选增强器中寻找匹配项
当找出对应的增强器后,接下来的任务就是看这些增强器是否与对应的class匹配了,当然不只是class,class内部的方法如果匹配也可以通过验证。
接下来看在findEligibleAdvisors
函数中我的findAdvisorsThatCanApply
这个函数:
- 看源码(
AbstractAdvisorAutoProxyCreator.java
)
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 过滤已经得到的advisors
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
继续追踪里面的findAdvisorsThatCanApply
方法
- 看源码(
AopUtils
)
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
// 首先处理引介增强
/*
* 引介增强是一种特殊的增强,其它的增强是方法级别的增强,即只能在方法前或方法后添加增强。
* 而引介增强则不是添加到方法上的增强, 而是添加到类方法级别的增强,即可以为目标类动态实现某个接口,
* 或者动态添加某些方法。我们通过下面的事例演示引介增强的使用
*/
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
Boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
// 引介增强已经处理
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 对于普通bean的 进行处理
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
继续追踪该方法中的canApply方法:
public static Boolean canApply(Advisor advisor, Class<?> targetClass, Boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
} else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
} else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
- 源码分析
回想之前,因为BeanFactoryTransactionAttributeSourceAdvisor
间接实现了PointcutAdvisor
。
所以在canApply函数中的第二个if判断是就会通过判断。会将BeanFactoryTransactionAttributeSourceAdvisor中的getPointcut()方法返回值作为参数继续调用canApply方法,而getPoint()方法返回的是TransactionAttributeSourcePointcut实例,对于transactionAttributeSource这个属性相信大家还有印象,就是在解析自定义标签时注入进去的,方法如下
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
@Override
@Nullable
protected TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
}
;
那么,使用TransactionAttributeSourcePointcut
类型的实例作为函数继续追踪canApply
- 源码(
AopUtils.java
)
public static Boolean canApply(Pointcut pc, Class<?> targetClass, Boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 通过Pointcut的条件判断此类是否匹配
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
// 此时的pc表示TransactionAttributeSourcePointcut
// pc.getMethodMatcher()返回的正是自身(this)
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
//获取对应类的所有接口
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
//对类进行遍历
for (Class<?> clazz : classes) {
// 反射获取类中所有的方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
// 根据匹配原则判断该方法是否能匹配Pointcut中的规则,如果有一个方法匹配则返回true
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
通过上面的函数大致可以理清大致的脉络:
首先获取对应类的所有接口并连通本类一起遍历,遍历过程中又对类中的方法再次遍历,一旦匹配成功便认为这个类适用于当前增强器。
到这里我们就会有疑问?**对于事务的配置不仅仅局限在函数配置上,我们都知道,在类或接口的配置上可以延续到类中的每个函数上。那么,如果针对每个函数进行检测,在本类身上配置的事务属性岂不是检测不到了吗?**接下来我们带着这个疑问继续探索canApply函数中的`matcher`的方法。
做匹配的时候`methodMatcher.matches(method, targetClass)`会使用`TransactionAttributeSourcePointcut`类的`matches`方法。
- 看源码(
TransactionAttributeSourcePointcut.java
)
@Override
public Boolean matches(Method method, Class<?> targetClass) {
// 自定义标签解析时注入
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
- 源码分析
此时上述源码中的tas标识AnnotationTransactionAttributeSource
类型,这里会判断tas.getTransactionAttribute(method, targetClass),而AnnotationTransactionAttributeSource 类型的getTransactionAttribute
方法如下:
- 看源码(
AbstractFallbackTransactionAttributeSource.java
)
@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
//先从缓存中获取TransactionAttribute
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
} else {
return cached;
}
}
// 如果缓存中没有,工作又委托给了computeTransactionAttribute函数 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 dta = (DefaultTransactionAttribute) txAttr;
dta.setDescriptor(methodIdentification);
dta.resolveAttributeStrings(this.embeddedValueResolver);
}
if (logger.isTraceEnabled()) {
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
//加入缓存中
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
- 源码分析
尝试从缓存加载,如果对应信息没有缓存的话,工作有委托给了computeTransactionAttribute
函数,在computeTransactionAttribute函数中我们终于看到了事务标签的提取过程。
- 看源码(
AbstractFallbackTransactionAttributeSource.java
)
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
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.
// method代表接口中的方法,specificMethod代表实现类中的方法
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
// 查看方法中是否存在事务声明
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
// 查看方法所在类中是否存在事务声明
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
// 如果存在接口,则到接口中去寻找
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;
}
- 源码分析
对于事务属性的获取规则相信大家都已经熟悉很清楚,如果方法中存在事务属性,则使用方法上的属性,否则使用方法所在类上的属性,如果方法所在类的属性还是没有搜寻到对应的事务属性,那么搜寻接口中的方法,再没有的话,最后尝试搜寻接口类上面的声明。对于函数computeTransactionAttribute
中的逻辑与我们所认识的规则并无差别,但是上面函数中并没有真正的去做搜寻事务属性的逻辑,而是搭建了个执行框架,将搜寻事务属性的任务委托给了findTransactionAttribute
方法去执行。继续进行分析:
- 看源码(
AnnotationTransactionAttributeSource.java
)
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
return determineTransactionAttribute(clazz);
}
继续查看上面的determineTransactionAttribute
函数:
- 看源码(
AnnotationTransactionAttributeSource.java
)
@Nullable
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
for (TransactionAnnotationParser parser : this.annotationParsers) {
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
- 源码分析
this.annotationParsers是在当前类**AnnotationTransactionAttributeSource **初始化的时候初始化的,其中的值被加入了SpringTransactionAnnotationParser
,也就是当进行属性获取的时候虎其实是使用可SpringTransactionAnnotationParser
类的parseTransactionAnnotation
方法进行解析的。继续分析源码
- 看源码(
SpringTransactionAnnotationParser.java
)
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
} else {
return null;
}
}
- 源码分析
至此,我们终于看到了想看到的获取注解标记的代码,首先会判断当前的类是否包含又Transactional
注解,这是事务属性的基础,当然如果有的话会继续调用parseTransactionAnnotation
方法解析详细的属性。接着看源码:
- 看源码()
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
// 解析propagation
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
// 解析isolation
rbta.setIsolationLevel(isolation.value());
// 解析timeout
rbta.setTimeout(attributes.getNumber("timeout").intValue());
String timeoutString = attributes.getString("timeoutString");
Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
"Specify 'timeout' or 'timeoutString', not both");
rbta.setTimeoutString(timeoutString);
// 解析readOnly
rbta.setReadOnly(attributes.getBoolean("readOnly"));
// 解析value
rbta.setQualifier(attributes.getString("value"));
rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
// 解析rollbackFor
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
// 解析rollbackForClassName
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
// 解析noRollbackFor
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
// 解析noRollbackForClassName
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
- 源码解析
至此,我们终于完成了事务标签的解析,回顾一下,我们现在的任务是找出某个增强其是否适合于对应的类,而是否匹配的关键则在于是否从指定的类或类中的方法中找到对应的事务属性。现在我们之前的AccountByXMLServiceImpl
为例,已经在它的接口AccountByXMLServiceImpl
中找到了事务属性,所以,它是与事务增强器匹配的,也就是它会被事务功能修饰。
至此,事务功能的初始化工作便结束了,当判断某个bean适用于事务增强时,也就i是适用于增强器`BeanFactoryTransactionAttributeSourceAdvisor`,**BeanFactoryTransactionAttributeSourceAdvisor**作为Advisor的实现类,自然要遵从Advisor的处理方式,当代理被调用时会调用这个类的增强方法,也就是bean的Advice,又因为在解析事务定义标签时我们把`Transactionlnterceptor`类的Bean注入到了`BeanFactoryTransactionAttributeSourceAdvisor`中,所以,在调用事务增强器增强的代理类时会首先执行**Transactionlnterceptor**进行增强,同时,也就是在Transactionlnterceptor类中的invoke方法中完成了整个事务的逻辑。
总结:
这一篇文章主要将了事务的Advisor是如何注册进Spring容器的,也讲解了Spring是如何将有配置事务的类配置上事务的,实际上就是使用了AOP那一套,也讲解了Advisor和Pointcut验证流程。至此事务的初始化工作已经完成,在之后的调用过程,如果代理类的方法被调用,都会调用
BeanFactoryTransactionAttributeSourceAdvisor
这个Advisor的增强方法。目前就是我们还没有提到的那个Advisor里面的Advice;还记得吗我们在自定义标签的时候我们将TransactionInterceptor
这个Advice作为Bean注入到IOC容器中,并且将其注入到Advisor中,这个Advice在代理类的invoke方法中会被封装到拦截器链中,最终事务的功能都能在Advice中体现。
微信搜索【码上遇见你】获取更多精彩内容。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)