Spring注解版学习笔记——声明式事务

 1、介绍

spring操作数据库时,实现包含数据库操作方法如果异常退出,对数据库的操作是回退,反之方法正常退出,对数据库才操作提交。

2、使用

  • 导入相关依赖:数据源,数据库驱动,Spring-jdbc模块
  • 配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
 1     //数据源
 2     @Bean
 3     public DataSource dataSource() throws Exception{
 4         ComboPooledDataSource dataSource = new ComboPooledDataSource();
 5         dataSource.setUser("root");
 6         dataSource.setPassword("123456");
 7         dataSource.setDriverClass("com.mysql.jdbc.Driver");
 8         dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
 9         return dataSource;
10     }
11     
12     //
13     @Bean
14     public JdbcTemplate jdbcTemplate() throws Exception{
15         //Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
16         JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
17         return jdbcTemplate;
18     }
View Code
  • 配置事务管理器来控制事务
1 //注册事务管理器在容器中
2     @Bean
3     public PlatformTransactionManager transactionManager() throws Exception{
4         return new DataSourceTransactionManager(dataSource());
5     }
View Code
  • 给方法上标注 @Transactional 表示当前方法是一个事务方法;
 1 @Service
 2 public class UserService {
 3     
 4     @Autowired
 5     private UserDao userDao;
 6     
 7     @Transactional
 8     public void insertUser(){
 9         userDao.insert();
10         //otherDao.other();xxx
11         System.out.println("插入完成...");
12         int i = 10/0;
13     }
14 
15 }
View Code
  • @EnableTransactionManagement 开启基于注解的事务管理功能;

3、原理

同样先从@EnableXxx作为入口,看它是否给Spring容器中注入了一个什么样的组件,这个组件是否是一个后置处理器,做了哪些工作。

3.1、@EnableTransactionManagement

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 @Import(TransactionManagementConfigurationSelector.class)
 5 public @interface EnableTransactionManagement {
 6 
 7     /**
 8      * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
 9      * opposed to standard Java interface-based proxies ({@code false}). The default is
10      * {@code false}. <strong>Applicable only if {@link #mode()} is set to
11      * {@link AdviceMode#PROXY}</strong>.
12      * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
13      * Spring-managed beans requiring proxying, not just those marked with
14      * {@code @Transactional}. For example, other beans marked with Spring's
15      * {@code @Async} annotation will be upgraded to subclass proxying at the same
16      * time. This approach has no negative impact in practice unless one is explicitly
17      * expecting one type of proxy vs another, e.g. in tests.
18      */
19     boolean proxyTargetClass() default false;
20 
21     /**
22      * Indicate how transactional advice should be applied. The default is
23      * {@link AdviceMode#PROXY}.
24      * @see AdviceMode
25      */
26     AdviceMode mode() default AdviceMode.PROXY;
27 
28     /**
29      * Indicate the ordering of the execution of the transaction advisor
30      * when multiple advices are applied at a specific joinpoint.
31      * The default is {@link Ordered#LOWEST_PRECEDENCE}.
32      */
33     int order() default Ordered.LOWEST_PRECEDENCE;
34 
35 }
View Code
 1 public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
 2 
 3     /**
 4      * {@inheritDoc}
 5      * @return {@link ProxyTransactionManagementConfiguration} or
 6      * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
 7      * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
 8      */
 9     @Override
10     protected String[] selectImports(AdviceMode adviceMode) {
11         switch (adviceMode) {
12             case PROXY:
13                 return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
14             case ASPECTJ:
15                 return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
16             default:
17                 return null;
18         }
19     }
20 
21 }
View Code
 1 public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
 2 
 3     public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";
 4 
 5 
 6     /**
 7      * The name of the {@link AdviceMode} attribute for the annotation specified by the
 8      * generic type {@code A}. The default is {@value #DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME},
 9      * but subclasses may override in order to customize.
10      */
11     protected String getAdviceModeAttributeName() {
12         return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;
13     }
14 
15     /**
16      * This implementation resolves the type of annotation from generic metadata and
17      * validates that (a) the annotation is in fact present on the importing
18      * {@code @Configuration} class and (b) that the given annotation has an
19      * {@linkplain #getAdviceModeAttributeName() advice mode attribute} of type
20      * {@link AdviceMode}.
21      * <p>The {@link #selectImports(AdviceMode)} method is then invoked, allowing the
22      * concrete implementation to choose imports in a safe and convenient fashion.
23      * @throws IllegalArgumentException if expected annotation {@code A} is not present
24      * on the importing {@code @Configuration} class or if {@link #selectImports(AdviceMode)}
25      * returns {@code null}
26      */
27     @Override
28     public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
29         Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
30         AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
31         if (attributes == null) {
32             throw new IllegalArgumentException(String.format(
33                 "@%s is not present on importing class '%s' as expected",
34                 annoType.getSimpleName(), importingClassMetadata.getClassName()));
35         }
36 
37         AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
38         String[] imports = selectImports(adviceMode);
39         if (imports == null) {
40             throw new IllegalArgumentException(String.format("Unknown AdviceMode: '%s'", adviceMode));
41         }
42         return imports;
43     }
44 
45     /**
46      * Determine which classes should be imported based on the given {@code AdviceMode}.
47      * <p>Returning {@code null} from this method indicates that the {@code AdviceMode} could
48      * not be handled or was unknown and that an {@code IllegalArgumentException} should
49      * be thrown.
50      * @param adviceMode the value of the {@linkplain #getAdviceModeAttributeName()
51      * advice mode attribute} for the annotation specified via generics.
52      * @return array containing classes to import; empty array if none, {@code null} if
53      * the given {@code AdviceMode} is unknown.
54      */
55     protected abstract String[] selectImports(AdviceMode adviceMode);
56 
57 }
View Code

 解析:

Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);

 获取泛型的类型,上例中就是获取 EnableTransactionManagement.class

AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);

  获取注解类型 annoType(也就是 EnableTransactionManagement.class)熟悉值。

可以得到 @EnableTransactionManagement 是要往容器里面注入  AutoProxyRegistrar、ProxyTransactionManagementConfiguration 两个类型的组件

3.2、AutoProxyRegistrar

 1 public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
 2 
 3     private final Log logger = LogFactory.getLog(getClass());
 4 
 5     @Override
 6     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 7         boolean candidateFound = false;
 8         Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
 9         for (String annoType : annoTypes) {
10             AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
11             if (candidate == null) {
12                 continue;
13             }
14             Object mode = candidate.get("mode");
15             Object proxyTargetClass = candidate.get("proxyTargetClass");
16             if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
17                     Boolean.class == proxyTargetClass.getClass()) {
18                 candidateFound = true;
19                 if (mode == AdviceMode.PROXY) {
20                     AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
21                     if ((Boolean) proxyTargetClass) {
22                         AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
23                         return;
24                     }
25                 }
26             }
27         }
28         ....
29     }
30 
31 }
View Code
1 public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
2         return registerAutoProxyCreatorIfNecessary(registry, null);
3     }
4 
5     public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
6         return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
7     }
View Code
 1 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
 2         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
 3         if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
 4             BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
 5             if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
 6                 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
 7                 int requiredPriority = findPriorityForClass(cls);
 8                 if (currentPriority < requiredPriority) {
 9                     apcDefinition.setBeanClassName(cls.getName());
10                 }
11             }
12             return null;
13         }
14         RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
15         beanDefinition.setSource(source);
16         beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
17         beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
18         registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
19         return beanDefinition;
20     }
View Code

结论:给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;

InfrastructureAdvisorAutoProxyCreator  ->  AbstractAdvisorAutoProxyCreator  ->  AbstractAutoProxyCreator  implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

参照  Spring注解版学习笔记——AOP 中的 AnnotationAwareAspectJAutoProxyCreator:

AnnotationAwareAspectJAutoProxyCreator  ->  AspectJAwareAdvisorAutoProxyCreator  ->  AbstractAdvisorAutoProxyCreator  ->  AbstractAutoProxyCreator  implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

可以得出结论:两者都是利用后置处理器机制,在对象创建完成以后,判断所有增强器是否可以用于这个对象上,返回一个代理对象,代理对象执行方法利用拦截器链进行调用。

再观察一下两个类之间区别:

 1 public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
 2 
 3     private ConfigurableListableBeanFactory beanFactory;
 4 
 5 
 6     @Override
 7     protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 8         super.initBeanFactory(beanFactory);
 9         this.beanFactory = beanFactory;
10     }
11 
12     @Override
13     protected boolean isEligibleAdvisorBean(String beanName) {
14         return (this.beanFactory.containsBeanDefinition(beanName) &&
15                 this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
16     }
17 
18 }
View Code
 1 public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
 2     
 3         private List<Pattern> includePatterns;
 4 
 5     private AspectJAdvisorFactory aspectJAdvisorFactory;
 6 
 7     private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;
 8 
 9     public void setIncludePatterns(List<String> patterns) {
10         this.includePatterns = new ArrayList<Pattern>(patterns.size());
11         for (String patternText : patterns) {
12             this.includePatterns.add(Pattern.compile(patternText));
13         }
14     }
15 
16     @Override
17     protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
18         super.initBeanFactory(beanFactory);
19         if (this.aspectJAdvisorFactory == null) {
20             this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
21         }
22         this.aspectJAdvisorsBuilder =
23                 new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
24     }
25 
26     protected boolean isEligibleAspectBean(String beanName) {
27         if (this.includePatterns == null) {
28             return true;
29         }
30         else {
31             for (Pattern pattern : this.includePatterns) {
32                 if (pattern.matcher(beanName).matches()) {
33                     return true;
34                 }
35             }
36             return false;
37         }
38     }
39 
40 
41         @Override
42         protected boolean isEligibleBean(String beanName) {
43             return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName);
44         }
45     }
46 
47         ...
48 
49 }
View Code

可以看到两个类在判断是否是为可增强对象的方法上不相同,一个是通过判断 BeanDefinition.role 是否为 ROLE_INFRASTRUCTURE;另一个是通过切面规则是否匹配。

3.3、ProxyTransactionManagementConfiguration 

 1 @Configuration
 2 public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
 3 
 4     @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
 5     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
 6     public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
 7         BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
 8         advisor.setTransactionAttributeSource(transactionAttributeSource());
 9         advisor.setAdvice(transactionInterceptor());
10         advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
11         return advisor;
12     }
13 
14     @Bean
15     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
16     public TransactionAttributeSource transactionAttributeSource() {
17         return new AnnotationTransactionAttributeSource();
18     }
19 
20     @Bean
21     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
22     public TransactionInterceptor transactionInterceptor() {
23         TransactionInterceptor interceptor = new TransactionInterceptor();
24         interceptor.setTransactionAttributeSource(transactionAttributeSource());
25         if (this.txManager != null) {
26             interceptor.setTransactionManager(this.txManager);
27         }
28         return interceptor;
29     }
30 
31 }
View Code

可以看到ProxyTransactionManagementConfiguration 主要是往容器里面注册事务增强器

  • 事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
  • 事务拦截器:
    • TransactionInterceptor;保存了事务属性信息,事务管理器;他是一个 MethodInterceptor;在目标方法执行的时候;事务拦截器:
      • 先获取事务相关的属性
      • 再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager;
      • 执行目标方法,如果异常,获取到事务管理器,利用事务管理回滚操作;如果正常,利用事务管理器,提交事务

4、引申

原文地址:https://www.cnblogs.com/demo12138/p/12666978.html