Spring提取@Transactional事务注解的源码解析
来源:互联网 发布:金融行业前景知乎 编辑:程序博客网 时间:2024/06/06 05:01
Spring提取@Transactional事务注解的源码解析
声明:本文是自己在学习spring注解事务处理源代码时所留下的笔记; 难免有错误,敬请读者谅解!!!
1、事务注解标签
<tx:annotation-driven />
2、tx 命名空间解析器
事务tx命名空间解析器TxNamespaceHandler
org.springframework.transaction.config.TxNamespaceHandler#init
3、AnnotationDrivenBeanDefinitionParser#parse 解析事务标签
(1)、以下方法的核心逻辑主要是选择是否使用 Aspect 方式实现代理,默认方式为 JDK 的动态代理。
org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser#parse
public BeanDefinition parse(Element element, ParserContext parserContext) { String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { // mode="aspectj" registerTransactionAspect(element, parserContext); } else { // mode="proxy" 注意 AopAutoProxyConfigurer 为当前内的内部类 AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null;}
(2)、进入如下方法,该方法的核心逻辑通过硬编码的方式配置 Aop 动态代理的解析器
AopAutoProxyConfigurer#configureAutoProxyCreator
通过硬编码,Spring 为我们定义了如下的 Spring BeanDefinition 对象
(a)、AnnotationTransactionAttributeSource.class 事务注解属性解析器BeanDefinition 对象。
AnnotationTransactionAttributeSource 构造方法会初始化:
public AnnotationTransactionAttributeSource() { this(true);}public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2); this.annotationParsers.add(new SpringTransactionAnnotationParser());// @Transactional 注解解析器 if (ejb3Present) { this.annotationParsers.add(new Ejb3TransactionAnnotationParser());// Ejb 解析器 }
(b)、TransactionInterceptor.class 事务拦截器BeanDefinition 对象
(c)、BeanFactoryTransactionAttributeSourceAdvisor.class 事务切面解析器
(d)、TransactionInterceptor.class 事务拦截器BeanDefinition 对象
(e)、容易忽略的第一行代码:AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); 在这个方法内部 Spring 为我们的注入了:InfrastructureAdvisorAutoProxyCreator.class
private static class AopAutoProxyConfigurer { public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { // 非常重要的一行代码,在这个里面注册了:InfrastructureAdvisorAutoProxyCreator.class 该类实现了Spring BeanProcessor 的扩展接口 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. RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);// 事务注解解析器 sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // Create the TransactionInterceptor definition. 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. RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);//事务切面解析器 advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef); 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); } }}
4、Spring Bean 实例化创建代理对象
(a)、AbstractAutowireCapableBeanFactory#initializeBean(Java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
(b)、AbstractAutoProxyCreator#postProcessAfterInitialization
还记得上面我们提到的 InfrastructureAdvisorAutoProxyCreator 的类图吧,最后我们的 @Transactional 注解的类会执行该类中的 postProcessAfterInitialization 方法
(c)、Bean 的初始化后置处理,通过注释可以了解到,当前方法处理后会返回一个 bean 的代理对象
/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.containsKey(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey);// 创建代理类的核心方法 } } return bean;}
(d)、AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.containsKey(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; } // Create proxy if we have advice. 获取切面 获取的过程是一个非常复杂的过程 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // 创建代理对象,默认的情况下会使用 JDK 的动态代理接口创建代理对象 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;}
(c)、获取到的事务切面
(d)、事务切面获取逻辑
(e)、委托 ProxyFactory 创建代理对象
protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory(); // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. proxyFactory.copyFrom(this); if (!shouldProxyTargetClass(beanClass, beanName)) { // Must allow for introductions; can't just set interfaces to // the target's interfaces only. Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader); for (Class<?> targetInterface : targetInterfaces) { proxyFactory.addInterface(targetInterface); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 最终会使用:JdkDynamicAopProxy 创建事务的Aop 代理对象 return proxyFactory.getProxy(this.proxyClassLoader);}
(f)、最终生成代理对象
5、代理类执行
JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { // 如果目标方法没有实现equals if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } // 如果目标方法没有实现hashcode if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } // 根据代理对象的配置来调用服务 if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. // 获取目标对象 target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // 获取定义好的拦截器链 // Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. // 没有拦截链则直接调用target方法 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // We need to create a method invocation... //对拦截链进行封装 得到对象ReflectiveMethodInvocation 调用 proceed 方法 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed();// 方法内部将执行拦截器的切面直到目标方法被执行 } // Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } }}
7、代理会执行到: ReflectiveMethodInvocation#proceed 方法
8、最终会执行到:TransactionInterceptor#invoke 方法
9、执行 TransactionAspectSupport 事务方法
TransactionInterceptor#invoke 方法会调用到父类的 TransactionAspectSupport#invokeWithinTransaction 方法
10、业务方法执行
11、Spring 事务处理的流程
(a)、开启事务
(b)、事务回滚
(c)、事务提交
声明:本编文章是自己在查看spring提取@Transactional注解的源码过程中随手记下的笔记,只做了大概流程的记录,未做详细分析,如有错误还请谅解。
1、事务切面匹配处理类
AopUtils#canApply(Pointcut, Class , boolean)
方法中会调用到 TransactionAttributeSourcePointcut#matches 方法
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); if (!pc.getClassFilter().matches(targetClass)) { return false; } MethodMatcher methodMatcher = pc.getMethodMatcher(); IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass); for (Class<?> clazz : classes) { Method[] methods = clazz.getMethods(); for (Method method : methods) { //methodMatcher.matches(method, targetClass) 方法会匹配对应的处理类,在Transaction提取的过程中会匹配到:TransactionAttributeSourcePointcut if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) || methodMatcher.matches(method, targetClass)) { return true; } } } return false;}
2、事务切点匹配
TransactionAttributeSourcePointcut#matches
在阅读TransactionAttributeSourcePointcut内的源代码的时候,我们发现该类是一个抽象,但是他确没有实现的子类!!!那么这个类到底在哪被引用了呢?
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { public boolean matches(Method method, Class targetClass) { // 该处调用了 getTransactionAttributeSource() 的抽象方法,但是却没有子类实现这个方法,这是怎么一回事呢? TransactionAttributeSource tas = getTransactionAttributeSource(); return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof TransactionAttributeSourcePointcut)) { return false; } TransactionAttributeSourcePointcut otherPc = (TransactionAttributeSourcePointcut) other; return ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), otherPc.getTransactionAttributeSource()); } @Override public int hashCode() { return TransactionAttributeSourcePointcut.class.hashCode(); } @Override public String toString() { return getClass().getName() + ": " + getTransactionAttributeSource(); } /** * Obtain the underlying TransactionAttributeSource (may be {@code null}). * To be implemented by subclasses. */ protected abstract TransactionAttributeSource getTransactionAttributeSource();}
3、TransactionAttributeSourcePointcut 抽象类的应用
我们怀着上面的疑问全局搜索 TransactionAttributeSourcePointcut 可以在 BeanFactoryTransactionAttributeSourceAdvisor 里面找到如下的代码:
public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor { private TransactionAttributeSource transactionAttributeSource; // 此处利用了匿名内部类的方式实例化了 TransactionAttributeSourcePointcut 对象,在此我们找到了上面问题的答案。 private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() { @Override protected TransactionAttributeSource getTransactionAttributeSource() { return transactionAttributeSource; } }; /** * Set the transaction attribute source which is used to find transaction * attributes. This should usually be identical to the source reference * set on the transaction interceptor itself. * @see TransactionInterceptor#setTransactionAttributeSource */ public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) { this.transactionAttributeSource = transactionAttributeSource; } /** * Set the {@link ClassFilter} to use for this pointcut. * Default is {@link ClassFilter#TRUE}. */ public void setClassFilter(ClassFilter classFilter) { this.pointcut.setClassFilter(classFilter); } public Pointcut getPointcut() { return this.pointcut; }}
3、TransactionAttributeSource 属性的 Bean 定义过程
其实,在实例化 BeanFactoryTransactionAttributeSourceAdvisor 时,Spring 已经为我们的 BeanFactoryTransactionAttributeSourceAdvisor 设置了 TransactionAttributeSource 属性,可以进入 AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer#configureAutoProxyCreator 方法中看源代码:
private static class AopAutoProxyConfigurer { public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME; if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { Object eleSource = parserContext.extractSource(element); // 注解事务 transactionAttributeSource Spring 定义的Bean为: AnnotationTransactionAttributeSource 实例 // Create the TransactionAttributeSource definition. RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // Create the TransactionInterceptor definition. 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 BeanFactoryTransactionAttributeSourceAdvisor Bean 的定义 // Create the TransactionAttributeSourceAdvisor definition. RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 设置 transactionAttributeSource 属性 advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName", interceptorName); if (element.hasAttribute("order")) { advisorDef.getPropertyValues().add("order", element.getAttribute("order")); } parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef); 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); } }}
4、TransactionAttributeSource#getTransactionAttribute 方法的调用过程
通过以上的分析,我们可以确定
TransactionAttributeSourcePointcut#getTransactionAttributeSource 返回的是:AnnotationTransactionAttributeSource 实例,AnnotationTransactionAttributeSource继承自:AbstractFallbackTransactionAttributeSource, 故此TransactionAttributeSourcePointcut#matches 最终会调用到 AbstractFallbackTransactionAttributeSource#getTransactionAttribute 方法
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { public boolean matches(Method method, Class targetClass) { TransactionAttributeSource tas = getTransactionAttributeSource(); // 最终会调用到 AbstractFallbackTransactionAttributeSource#getTransactionAttribute 方法 return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); } // 省略其他代码 ……………………}
再看 AbstractFallbackTransactionAttributeSource#getTransactionAttribute 方法
// 获取事务属性public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) { // 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. if (cached == NULL_TRANSACTION_ATTRIBUTE) { return null; } else { return (TransactionAttribute) cached; } } else { // We need to work it out. 根据 method、targetClass 推算事务属性,TransactionAttribute TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass); // Put it in the cache. if (txAtt == null) { this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); } else { if (logger.isDebugEnabled()) { logger.debug("Adding transactional method '" + method.getName() + "' with attribute: " + txAtt); } this.attributeCache.put(cacheKey, txAtt); } return txAtt; }}
5、事务属性的推算过程:
// 推算事务属性,TransactionAttribute private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // Ignore CGLIB subclasses - introspect the actual user class. Class<?> userClass = ClassUtils.getUserClass(targetClass); // 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 = ClassUtils.getMostSpecificMethod(method, userClass); // If we are dealing with method with generic parameters, find the original method. specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); // 通过上面的分析,findTransactionAttribute 该方法最终会调用到:AnnotationTransactionAttributeSource#findTransactionAttribute(java.lang.Class<?>) // First try is the method in the target class. 方式1: 从目标类的方法上找 Transaction注解 TransactionAttribute txAtt = findTransactionAttribute(specificMethod); if (txAtt != null) { return txAtt; } // Second try is the transaction attribute on the target class. 方式2: 从目标类上找 Transaction注解 txAtt = findTransactionAttribute(specificMethod.getDeclaringClass()); if (txAtt != null) { return txAtt; } if (specificMethod != method) {// 以上两种方式如果还没有找到 TransactionAttribute 属性,那就要从目标类的接口开始找 // Fallback is to look at the original method. 方式3:接口的方法上找 Transaction注解 txAtt = findTransactionAttribute(method); if (txAtt != null) { return txAtt; } // Last fallback is the class of the original method. 方式4:接口的类上找 Transaction注解 return findTransactionAttribute(method.getDeclaringClass()); } return null;}
6、事务注解属性的解析
AnnotationTransactionAttributeSource#findTransactionAttribute(Java.lang.Class
7、获取事务注解
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable { public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { //获取 Transactional 注解 Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class); if (ann != null) { //从 @Transactional 注解上获取事务属性值,并包装成 TransactionAttribute 返回 return parseTransactionAnnotation(ann); } else { return null; } } public TransactionAttribute parseTransactionAnnotation(Transactional ann) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); rbta.setPropagationBehavior(ann.propagation().value()); rbta.setIsolationLevel(ann.isolation().value()); rbta.setTimeout(ann.timeout()); rbta.setReadOnly(ann.readOnly()); rbta.setQualifier(ann.value()); ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>(); Class[] rbf = ann.rollbackFor(); for (Class rbRule : rbf) { RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule); rollBackRules.add(rule); } String[] rbfc = ann.rollbackForClassName(); for (String rbRule : rbfc) { RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule); rollBackRules.add(rule); } Class[] nrbf = ann.noRollbackFor(); for (Class rbRule : nrbf) { NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule); rollBackRules.add(rule); } String[] nrbfc = ann.noRollbackForClassName(); for (String rbRule : nrbfc) { NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule); rollBackRules.add(rule); } rbta.getRollbackRules().addAll(rollBackRules); return rbta; } @Override public boolean equals(Object other) { return (this == other || other instanceof SpringTransactionAnnotationParser); } @Override public int hashCode() { return SpringTransactionAnnotationParser.class.hashCode(); }}
- Spring提取@Transactional事务注解的源码解析
- spring的事务注解@Transactional
- spring 事务注解@Transactional
- spring 事务注解@Transactional
- spring 事务注解@Transactional
- Spring注解事务@Transactional
- Spring 注解@Transactional 事务
- 基于@Transactional注解的Spring事务
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- Spring 之注解事务 @Transactional
- C++学习---一道笔试题:测试当前电脑系统是大端存储还是小端存储数据
- 22篇
- Docker学习笔记——构建镜像
- Eclipse配置错误 —— Syntax error, annotations are only available if source level is 1.5 or greater
- Quartz框架
- Spring提取@Transactional事务注解的源码解析
- 时间的账单
- Linux 内核学习经验总结
- 面向对象:我笑,便面如春花,定能感动人,任他是谁
- 程序员秒懂,但会不会误导小朋友?
- C#下操作USB设备的方法
- 关于JSch的使用,执行ssh命令,文件上传和下载以及连接方式
- 《Effective Java》笔记
- 如何理解与快速构建python编程程环境,eclipse+pydev插件+python虚拟平台