zoukankan      html  css  js  c++  java
  • 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、引申

  • 相关阅读:
    dfssvc.exe
    我左边的公告
    再做了一个LOGO
    做一个调查,请大家帮忙
    如果年三十没有人陪我
    我喜欢这个模版
    cisvc.exe是什么服务
    IIS与SQL服务器安全加固
    终于申请到了blogs
    重发LOGO
  • 原文地址:https://www.cnblogs.com/demo12138/p/12666978.html
Copyright © 2011-2022 走看看