精华内容
下载资源
问答
  • @Transactional是spring中常用的注解之一,通常情况下我们在需要对一个service方法添加事务时,加上这个注解,如果发生unchecked exception,就会发生rollback MyBatis自动参与到spring事务管理中,无需额外配置,...

    一、介绍

    @Transactional是spring中常用的注解之一,通常情况下我们在需要对一个service方法添加事务时,加上这个注解,如果发生unchecked exception,就会发生rollback

    MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。

    二、@Transactional 注解的属性信息

    @Transactional**属性

    属性 类型 描述
    value String 可选的限定描述符,指定使用的事务管理器
    propagation enum: Propagation 可选的事务传播行为设置
    isolation enum: Isolation 可选的事务隔离级别设置
    readOnly boolean 读写或只读事务,默认读写
    timeout int (in seconds granularity) 事务超时时间设置
    rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
    rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
    noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
    noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

    三、@Transactional 注解的属性信息用法

    @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

    虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。但是这个被注解的方法将不会展示已配置的事务设置。

    默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

    @Transactional(readOnly = true)
    public class DefaultFooService implements FooService {
    
      public Foo getFoo(String fooName) {
        // do something
      }
    
      // these settings have precedence for this method
      //方法上注解属性会覆盖类注解上的相同属性
      @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
      public void updateFoo(Foo foo) {
        // do something
      }
    }
    
    展开全文
  • @Transactional可以说是spring中最常用的注解之一了,通常情况下我们在需要对一个service方法添加事务时,加上这个注解,如果发生unchecked exception,就会发生rollback,最典型例子如下。 @Service public class...

    @Transactional可以说是spring中最常用的注解之一了,通常情况下我们在需要对一个service方法添加事务时,加上这个注解,如果发生unchecked exception,就会发生rollback,最典型的例子如下。

    @Service
    public class StudentService {
        @Autowired
        StudentDao studentDao;
    
        @Transactional
        public void innerSave(int i) {
            Student student = new Student();
            student.setName("test" + i);
            studentDao.save(student);
            //i=5 会出现异常
            int a = 1 / (i - 5);
        }
    }

    在调用innerSave(5)时会发运算异常,导致保存操作回滚,不在此赘述了。
    新的需求:循环保存10个学生,发生异常时要求回滚。
    我们理所当然的写出了下面的代码,在StudentService.java添加如下方法

    public void outerLooper1() {
           for (int i = 1; i <= 10; i++) {
               try{
                   innerSave(i);
               }catch (Exception e){
                   e.printStackTrace();
               }
           }
       }

    先考虑一下test5这个学生有没有保存呢?
    结果:
    在这里插入图片描述
    依然出现了,考虑下问题出在哪儿了?
    其实也好理解,spring中@Transactional 的事务开启 ,是基于接口 或者是类的代理被创建的。所以在同一个类中一个普通方法outerLooper1()调用另一个有事务的方法innerSave(),事务是不会起作用的。要解决这个问题,一般我的做法是写一个帮助类,注入到当前类中,来完成事务操作。

        @Autowired
        UtilService utilService;
    
        public void outerLooper2() {
            for (int i = 1; i <= 10; i++) {
                utilService.innerSave(i);
            }
        }

    在这里插入图片描述
    在spring中使用事务需要遵守一些规范和了解一些坑点,别想当然。列举一下一些注意点。

    • 在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的public 方法上。@Transactional 注解只能应用到 public 可见度的方法上。如果你在 protected、private 或者package-visible 的方法上使用@Transactional 注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
    • Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用@Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
    • @Transactional 的事务开启 ,或者是基于接口的或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。
    • 了解事务的隔离级别,各个数据库默认的隔离级别是不一样的,在spring中用的是isolation =
      Isolation.READ_COMMITTED来设置;了解事务的传播机制,当发生事务嵌套时,按照业务选择对应的传播机制,用propagation
      = Propagation.REQUIRED来设置。
    展开全文
  • 1,场景一,最常见用法,在方法上使用@Transactional 注解,事务正常起作用。无异常时正常插入数据库,有异常时数据不插入数据库,代码如下。 @Service public class ComeServiceImpl implements ComeService { ...

    一,spring中管理事务一般使用@Transactional  注解,下面对@Transactional 使用的各个场景做一个列举,尽可能的将所有场景罗列出来

    1,场景一,最常见的用法,在方法上使用@Transactional  注解,事务正常起作用。无异常时正常提交,有异常时数据回滚,代码如下。

    @Service
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
    
        @Override
        @Transactional
        public int saveUser() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
    //        int i = 1 / 0;
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            return 0;
        }
    }

    2,场景二,常见的用法,在类上使用@Transactional  注解,对整个类的方法,事务起作用。无异常时正常提交,有异常时数据回滚,代码如下。

    @Service
    @Transactional
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        public int saveUser() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            // int i = 1 / 0;
            return 0;
        }
    }

    3,场景三,将异常信息使用try-catch 包裹,异常被处理,@Transactional 注解不起作用,数据提交,没有回滚,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        @Transactional
        public int saveUser() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            try {
                int i = 1 / 0;
            }catch (Exception e){
                System.out.println("异常。。。");
            }
            return 0;
        }
    }
    

    4,场景四,同一个Service内方法调用,当@Transactional 注解作用在A方法上时,事务起作用。方法A中的数据回滚,方法B中的数据回滚,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        @Transactional
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            this.B();
            return 0;
        }
    
        @Override
        public int B() {
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            int i = 1 / 0;
            return 0;
        }
    }

    5,场景五,同一个Service内方法调用,当@Transactional 注解作用在B方法上时,事务不起作用。方法A中的数据提交,方法B中数据提交,遇到异常没有回滚,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            this.B();
            return 0;
        }
    
        @Override
        @Transactional
        public int B() {
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            int i = 1 / 0;
            return 0;
        }
    }
    

    6,场景六,同一个Service内方法调用,当@Transactional 注解作用在类上时,事务起作用,数据回滚,代码如下。

    @Service
    @Slf4j
    @Transactional
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            this.B();
            return 0;
        }
    
        @Override
        public int B() {
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            int i = 1 / 0;
            return 0;
        }
    }

    7,场景七,同一个Service内方法调用私有的方法C,当@Transactional 注解作用在方法A上时,事务起作用,数据回滚,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        @Transactional
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            this.C();
            return 0;
        }
        private int C() {
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            int i = 1 / 0;
            return 0;
        }
        @Override
        public int B() {
            return 0;
        }
    }

    8,场景八,同一个Service内方法调用私有的方法C,当@Transactional 注解作用在方法C上时,事务不起作用,方法A中的数据提交,方法C中的数据提交,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Override
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            this.C();
            return 0;
        }
        @Transactional
        private int C() {
            User user2 = new User(11,"b",111,"b");
            userMapper.saveUser(user2);
            int i = 1 / 0;
            return 0;
        }
        @Override
        public int B() {
            return 0;
        }
    }

    9,场景九,不同Service方法间调用,当@Transactional 注解作用在方法A上时,事务起作用,方法A中的数据回滚,方法saveClassInfo中的数据回滚,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Autowired
        ClassInfoService classInfoService;
    
        @Override
        @Transactional
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            classInfoService.saveClassInfo();
            return 0;
        }
        @Override
        public int B() {
            return 0;
        }
    }
    @Service
    public class ClassInfoServiceImpl implements ClassInfoService {
        @Autowired
        ClassInfoMapper classInfoMapper;
    
        @Override
        public int saveClassInfo() {
            ClassInfo classInfo = new ClassInfo("c","c",69D);
            classInfoMapper.saveClassInfo(classInfo);
            int i = 1/0;
            return 0;
        }
    }

    10,场景十,不同Service方法间调用,当@Transactional 注解作用在方法saveClassInfo上时,事务对A不起作用,方法A中的数据提交,方法saveClassInfo数据回滚,代码如下。

    @Service
    @Slf4j
    public class ComeServiceImpl implements ComeService {
        @Autowired
        UserMapper userMapper;
        @Autowired
        ClassInfoService classInfoService;
    
        @Override
        public int A() {
            User user1 = new User(11,"a",111,"a");
            userMapper.saveUser(user1);
            classInfoService.saveClassInfo();
            return 0;
        }
        @Override
        public int B() {
            return 0;
        }
    }
    @Service
    public class ClassInfoServiceImpl implements ClassInfoService {
        @Autowired
        ClassInfoMapper classInfoMapper;
    
        @Override
        @Transactional
        public int saveClassInfo() {
            ClassInfo classInfo = new ClassInfo("c","c",69D);
            classInfoMapper.saveClassInfo(classInfo);
            int i = 1/0;
            return 0;
        }
    }

    未完待续。。。。持续更新

    展开全文
  • 「七剑下天山」一:@EnableTransactionManagement二:@Transactional三:@TransactionEventListener四:TransactionTemplate五:DataSourceUtils六:TransactionSynchronizationManager七:...

    七剑下天山

    一:@EnableTransactionManagement二:@Transactional三:@TransactionEventListener四:TransactionTemplate五:DataSourceUtils六:TransactionSynchronizationManager七:TransactionAwareDataSourceProxy

    01

    @EnableTransactionManagement

    1.1 作用

    此注解是 Spring 支持注解事务配置的标志。表明 Spring 开启注解事务配置的支持。是注解驱动开发事务配置的必备注解。

    1.2 源码

    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(TransactionManagementConfigurationSelector.class)public @interface EnableTransactionManagement {    /**     * 指定基于目标类代理还是基于接口代理。     * 默认采用JDK官方的基于接口代理。     * @return     */    boolean proxyTargetClass() default false;    /**     * 指定事务通知是如何执行的。默认是通过代理方式执行的。     * 如果是同一个类中调用的话,请采用AdviceMode.ASPECTJ     * @return     */    AdviceMode mode() default AdviceMode.PROXY;    /**     * 指示在特定连接点应用多个通知时事务处理的执行顺序。     * 默认值是:最低优先级(Integer.MAX_VALUE)     * @return     */    int order() default Ordered.LOWEST_PRECEDENCE;}

    1.3 源码分析

    • @EnableTransactionManagement 通过在 @Import 注解中传入 TransactionManagementConfigurationSelector 类,会给容器中导入两个组件:

      • AutoProxyRegistrar

      • ProxyTransactionManagementConfiguration

    • AutoProxyRegistrar:给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用。

    • ProxyTransactionManagementConfiguration 使用 @Configuration 注解修饰,表明是一个配置类。

      • TransactionInterceptor;保存了事务属性信息,事务管理器;

      • TransactionInterceptor 本身是一个 MethodInterceptor,MethodInterceptor 在 spring-aop 的课程中已经分析过了。在目标方法执行的时候,会 getAdvisors() 获取拦截器链,并执行拦截器链,当只有事务拦截器时:

        1)先获取事务相关的属性

        2)再获取 PlatformTransactionManager,如果事先没有添加指定任何 transactionmanger,最终会从容器中按照类型获取一个 PlatformTransactionManager;

        3)执行目标方法

        如果正常,利用事务管理器,提交事务

        如果异常,获取到事务管理器,利用事务管理回滚操作;

      • transactionAdvisor 方法:给容器中注册事务增强器 transactionAdvisor;

      • transactionAttributeSource 方法:创建了一个注解事务的属性解析对象

      • transactionInterceptor 方法:返回值是:事务拦截器 transactionInterceptor:

    02

    @Transactional

    2.1 作用

    此注解是 Spring 注解配置事务的核心注解,无论是注解驱动开发还是注解和 XML 混合开发,只有涉及配置事务采用注解的方式,都需要使用此注解。

    通过源码我们看到,该注解可以出现在接口上,类上和方法上。分别表明:

    • 接口上:当前接口的所有实现类中重写接口的方法有事务支持。

    • 类上:当前类中所有方法有事务支持。

    • 方法上:当前方法有事务的支持。

    优先级:方法上 > 类上 > 接口上。

    2.2 源码

    @Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface Transactional {    /**     * 指定事务管理器的唯一标识     */    @AliasFor("transactionManager") String value() default "";    /**     * 指定事务管理器的唯一标识     */    @AliasFor("value") String transactionManager() default "";    /**     * 指定事务的传播行为     */    Propagation propagation() default Propagation.REQUIRED;    /**     * 指定事务的隔离级别     */    Isolation isolation() default Isolation.DEFAULT;    /**     * 指定事务的超时时间     */    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;    /**     * 指定事务是否只读     */    boolean readOnly() default false;    /**     * 通过指定异常类的字节码,限定事务在特定情况下回滚     */    Class extends Throwable>[] rollbackFor() default {};    /**     * 通过指定异常类的全限定类名,限定事务在特定情况下回滚     */    String[] rollbackForClassName() default {};    /**     * 通过指定异常类的字节码,限定事务在特定情况下不回滚     */    Class extends Throwable>[] noRollbackFor() default {};    /**     * 通过指定异常类的全限定类名,限定事务在特定情况下不回滚     */    String[] noRollbackForClassName() default {};}

    2.3 源码分析

    2.3.1 在 @EnableTransactionManagement 注解中,有一个导入器:

    TransactionManagementConfigurationSelector,导入器中在 AdiviceMode 为默认值 PROXY 时,往

    容器中注入了两个bean对象,AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration。

    // 通过 @Import注解,Spring 定义了一个事务管理配置类的导入器。@Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement {   //其余代码略 }

    2.3.2 ProxyTransactionManagementConfiguration 类中的一个方法:

    @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() {   //创建了一个注解事务的属性解析对象   return new AnnotationTransactionAttributeSource(); }

    2.3.3 AnnotationTransactionAttributeSource 类的实例化

    public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {    /**     * 默认构造函数     */    public AnnotationTransactionAttributeSource() {        this(true);    }    /**     * 带参构造(表明是否为public方法)     */    public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {        this.publicMethodsOnly = publicMethodsOnly;        //判断是不是jta或者是ejp        if (jta12Present || ejb3Present) {            this.annotationParsers = new LinkedHashSet<>(4);            this.annotationParsers.add(new SpringTransactionAnnotationParser());            //jta            if (jta12Present) {                this.annotationParsers.add(new                        JtaTransactionAnnotationParser());            }            //ejp            if (ejb3Present) {                this.annotationParsers.add(new                        Ejb3TransactionAnnotationParser());            }        } else {            //当都不是的时候,构建一个SpringTransactionAnnotationParser(事务注解解析器)            this.annotationParsers = Collections.singleton(new                    SpringTransactionAnnotationParser());        }    }}

    2.3.4 SpringTransactionAnnotationParser 的注解解析:

    public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {    /**     * 根据传入被注解的元素解析。     * 可以是Method,Field,Class,Package,Construct等等。     */    @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;        }    }    /**     * 根据传入注解解析     */    public TransactionAttribute parseTransactionAnnotation(Transactional ann) {        return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));    }    /**     * 根据传入的注解属性解析     */    protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes                                                                      attributes) {        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();        Propagation propagation = attributes.getEnum("propagation");        rbta.setPropagationBehavior(propagation.value());        Isolation isolation = attributes.getEnum("isolation");        rbta.setIsolationLevel(isolation.value());        rbta.setTimeout(attributes.getNumber("timeout").intValue());        rbta.setReadOnly(attributes.getBoolean("readOnly"));        rbta.setQualifier(attributes.getString("value"));        List rollbackRules = new ArrayList<>();        for (Class> rbRule : attributes.getClassArray("rollbackFor")) {            rollbackRules.add(new RollbackRuleAttribute(rbRule));        }        for (String rbRule : attributes.getStringArray("rollbackForClassName")) {            rollbackRules.add(new RollbackRuleAttribute(rbRule));        }        for (Class> rbRule : attributes.getClassArray("noRollbackFor")) {            rollbackRules.add(new NoRollbackRuleAttribute(rbRule));        }        for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {            rollbackRules.add(new NoRollbackRuleAttribute(rbRule));        }        rbta.setRollbackRules(rollbackRules);        return rbta;    }    //其余代码略}

    03

    @TransactionEventListener

    3.1 作用

    它是 spring 在 4.2 版本之后加入的注解。用于配置一个事务的事件监听器。使我们在事务提交和回滚前后可以做一些额外的功能。

    例如:对事务执行监控,执行中同步做一些操作等等。

    3.2 使用示例

    自定义事件类:

    public class MyApplicationEvent extends ApplicationEvent {    public MyApplicationEvent(Object source) {        super(source);    }}

    自定义监听类:

    @Component@Slf4jpublic class MyTransactionEventListener {    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)    public void doSomething(MyApplicationEvent event) {        Map map = (Map) event.getSource();        log.info("事务提交后执行===>转出账户:" + map.get("sourceName") + ",转入账户:" + map.get("targetName") + ",转账金额:" + map.get("money"));    }    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)    public void otherSomething(MyApplicationEvent event) {        Map map = (Map) event.getSource();        log.info("事务回滚后执行===>转出账户:" + map.get("sourceName") + ",转入账户:" + map.get("targetName") + ",转账金额:" + map.get("money"));    }}

    业务层:

    @Servicepublic class AccountServiceImpl implements AccountService {    @Autowired    private AccountDao accountDao;    @Autowired    private ApplicationEventPublisher applicationEventPublisher;    @Override    @Transactional(rollbackFor = Exception.class)    public void transfer(String sourceName, String targetName, Double money) {        try {            //1.根据名称查询转出账户            Account source = accountDao.findByName(sourceName);            //2.根据名称查询转入账户            Account target = accountDao.findByName(targetName);            //3.转出账户减钱            source.setMoney(source.getMoney() - money);            //4.转入账户加钱            target.setMoney(target.getMoney() + money);            accountDao.update(source);             // 模拟转账异常            int i = 1 / 0;             //6.更新转入账户            accountDao.update(target);        } finally {            Map map = new HashMap<>();            map.put("sourceName", sourceName);            map.put("targetName", targetName);            map.put("money", money);            // 发布自定义事件(此处可以发布多个事件,如邮件通知、短信通知等,利用自定义监听器实现代码解耦)            applicationEventPublisher.publishEvent(new MyApplicationEvent(map));        }    }}

    3.3 源码

    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@EventListenerpublic @interface TransactionalEventListener {    /**     * 指定事务监听器的执行是在何时。     * 取值有:     *      事务提交之前     *      事务提交之后(默认值)     *      事务回滚之后     *      事务执行完成之后     */    TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;    /**     * 若没有事务的时候,对应的event是否已经执行     * 默认值为false表示没事务就不执行了     */    boolean fallbackExecution() default false;    /**     * 指定事件类的字节码     */    @AliasFor(annotation = EventListener.class, attribute = "classes") Class>[] value() default {};    /**     * 它和value属性的作用是一样的     */    @AliasFor(annotation = EventListener.class, attribute = "classes") Class>[] classes() default {};    /**     * 用于指定执行事件处理器的条件。取值是基于Spring的el表达式编写的。     */    String condition() default "";}

    3.4 源码分析

    3.4.1 在 IoC 容器加载时,执行 AbstractApplicationContext 的 refresh() 方法,一共十二个步骤,在执行到

    // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);//触发其他单例bean的加载

    3.4.2 AbstractApplicationContext 的 finishBeanFactoryInitialization 方法执行时,初始化剩余的单例 bean 对象。

    /** * 初始化剩余单例bean对象的方法 */protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {    // 方法中的其他代码略    // 初始化剩余单例bean对象.调用的是DefaultListableBeanFactory类中的preInstantiateSingletons方法。    beanFactory.preInstantiateSingletons();}

    3.4.3 DefaultListableBeanFactory 类中的 preInstantiateSingletons 方法中执行了 afterSingletonsInstantiated() 方法。此方法是SmartInitializingSingleton 接口中声明的。具体实现类包含:EventListenerMethodProcessor 事件监听器方法处理器。

    3.4.4 EventListenerMethodProcessor 中的 afterSingletonsInstantiated 会被执行,该方法中包含处理 bean 的方法:processBean。

    3.4.5 在 processBean 方法中调用了创建事件监听器的方法 createApplicationListener。该方法是 EventListenerFactory 接口中声明的方法。

    3.4.6 TransactionalEventListenerFactory 类实现了 EventListenerFactory 接口,并重写了 createApplicationListener 方法。

    /** * 重写接口中的创建监听器方法 */@Overridepublic ApplicationListener> createApplicationListener(String beanName, Class> type, Method method) {    return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);}

    3.4.7 ApplicationListenerMethodTransactionalAdapter 的实例化。至此,解析 TransactionalEventListener 注解的过程完成。

    /** * 构造函数 */public ApplicationListenerMethodTransactionalAdapter(String beanName, Class> targetClass, Method method) {    super(beanName, targetClass, method);    TransactionalEventListener ann = AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class);    if (ann == null) {        throw new IllegalStateException("No TransactionalEventListener annotation found on method: "+method);    }    this.annotation = ann;}

    04

    TransactionTemplate

    此类是用于编程式事务的模板对象。源码分析如下:

    public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {    /**     * 定义日志组件.     */    protected final Log logger = LogFactory.getLog(getClass());    /**     * 定义事务管理器对象     */    @Nullable    private PlatformTransactionManager transactionManager;    /*** 默认构造函数 */    public TransactionTemplate() {    }    /*** 通过事务管理器构建事务模板对象 */    public TransactionTemplate(PlatformTransactionManager transactionManager) {        this.transactionManager = transactionManager;    }    /*** 通过事务管理器和事务定义信息构建模板对象 */    public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {        super(transactionDefinition);        this.transactionManager = transactionManager;    }    /*** 当使用默认构造函数构建事务模板对象时,可以通过此方法注入事务管理器 */    public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {        this.transactionManager = transactionManager;    }    /*** 获取使用的事务管理器对象 */    @Nullable    public PlatformTransactionManager getTransactionManager() {        return this.transactionManager;    }    /*** 判断事务管理器是否为空 */    @Override    public void afterPropertiesSet() {        if (this.transactionManager == null) {            throw new IllegalArgumentException("Property 'transactionManager' is required");        }    }    /*** 编程事务控制的核心方法,重写的是TransactionOperations接口中的方法 */    @Override    @Nullable    public  T execute(TransactionCallback action) throws TransactionException {        Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");         // 判断当前事务管理器是否为CallbackPreferringPlatformTransactionManager类型, 如果是的话,直接使用该接口实现类中的execute方法执行。而无需继续让 PlatformTransactionManager的实现类控制事务,当前坐标环境下它只有一个实现类:WebSphereUowTransactionManager。        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {            return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);        } else {            // 需要借助PlatformTransactionManager的实现类控制事务。            TransactionStatus status = this.transactionManager.getTransaction(this);            T result;            try {                // 执行TransactionCallback中的doInTransaction方法,此处又是策略模式。                // spring只提供了一个接口(还有一个抽象实现类),而具体需要事务支持的业务代 码由使用者提供。                result = action.doInTransaction(status);            } catch (RuntimeException | Error ex) {                 // 当doInTransaction执行有异常时事务回滚                rollbackOnException(status, ex);                throw ex;            } catch (Throwable ex) {                 // 当doInTransaction执行有无法预知的异常时,事务回滚。                rollbackOnException(status, ex);                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");            }            // 没有异常的话,事务提交            this.transactionManager.commit(status);             // 返回执行结果(有可能是结果集,也有可能是影响数据库记录的行数)            return result;        }    }    /** 出现异常事务回滚 */    private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {        Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");        logger.debug("Initiating transaction rollback on application exception", ex);        try {            // 执行事务管理器中提供的回滚方法。            this.transactionManager.rollback(status);        } catch (TransactionSystemException ex2) {            logger.error("Application exception overridden by rollback exception", ex);            ex2.initApplicationException(ex);            throw ex2;        } catch (RuntimeException | Error ex2) {            logger.error("Application exception overridden by rollback exception", ex);            throw ex2;        }    }    /*** 重写equals方法 */    @Override    public boolean equals(Object other) {        return (this == other || (super.equals(other) && (!(other instanceof TransactionTemplate) || getTransactionManager() == ((TransactionTemplate) other).getTransactionManager())));    }}

    05

    DataSourceUtils

    Spring 中数据源的工具类。里面定义着获取连接的方法。

    public abstract class DataSourceUtils {    /*** 获取连接的方法,它本身没有任何操作而是调用了doGetConnection */    public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {        try {            // 真正获取连接的方法            return doGetConnection(dataSource);        } catch (SQLException ex) {            throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: ", ex);        } catch (IllegalStateException ex) {            throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage());        }    }    /*** 真正从数据源中获取连接的方法 */    public static Connection doGetConnection(DataSource dataSource) throws SQLException {        Assert.notNull(dataSource, "No DataSource specified");        // 通过事务同步管理器对象获取连接持有者对象        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);         // 当连接持有者不为null时,并且再满足连接持有者有连接或者是同步的事务其中任何一个条件, 则直接返回连接持有者的连接对象。        // (synchronizedWithTransaction默认值为false。但是在DataSourceTransactionManager中的doBegin方法中对synchronizedWithTransaction属性赋值为 true了。        if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {            conHolder.requested();            // 如果ConnectionHandle为null,则返回false,此处取反。就表示 ConnectionHandle为null时,进入if代码块中,给ConnectionHolder设置一个连接            if (!conHolder.hasConnection()) {                logger.debug("Fetching resumed JDBC Connection from DataSource");                conHolder.setConnection(fetchConnection(dataSource));            }// 返回ConnectionHolder对象中的连接            return conHolder.getConnection();        }        logger.debug("Fetching JDBC Connection from DataSource");        //如果不满足上面的条件,则从数据源中获取一个连接        Connection con = fetchConnection(dataSource);        //判断是否激活了事务同步器        if (TransactionSynchronizationManager.isSynchronizationActive()) {            try {                // 在激活同步的条件下,如果ConnectionHolder为null就创建连接持有者对象                ConnectionHolder holderToUse = conHolder;                if (holderToUse == null) { //创建连接持有者对象                    holderToUse = new ConnectionHolder(con);                } else {                    // 直接使用已经存在的连接持有者,并把获取到的连接填充进去                    holderToUse.setConnection(con);                }                holderToUse.requested(); //注册同步器                TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource)); //设置snchronizedWithTransaction属性为true                holderToUse.setSynchronizedWithTransaction(true);                // 从名称为Transactional resources的ThreadLocal中获取绑定的 Map,并把数据源和ConnectionHolder存入map中                if (holderToUse != conHolder) {                    TransactionSynchronizationManager.bindResource(dataSource, holderToUse);                }            }        } catch(RuntimeException ex) {            // Unexpected exception from external delegation call -> close Connection and rethrow.            releaseConnection(con, dataSource);            throw ex;        }        // 此时如果激活了事务同步管理器,则返回当前线程的连接。如果没激活,返回的就是数据源中拿 到的连接。        return con;    }    /*** 从数据源中获取一个连接的方法,此时没有和线程绑定 */    private static Connection fetchConnection(DataSource dataSource) throws SQLException {        //从数据源中获取一个连接        Connection con = dataSource.getConnection();        //如果没有,则表示数据源中没有连接        if (con == null) {            throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);        }        // 返回拿到的连接对象        return con;    }    // 其余代码略}

    06

    TransactionSynchronizationManager

    事务的同步管理器类实现连接和线程绑定从而控制事务的核心类

    它是个抽象类,但是没有任何子类 因为它所有的方法都是静态的。

    public abstract class TransactionSynchronizationManager {    // 定义了很多ThreadLocal    // 应用代码随事务的声明周期绑定的对象 比如:DataSourceTransactionManager有这么做:    // TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());    // TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);    // 简单理解为当前线程的数据存储中心~~~~    // 使用的同步器,用于应用扩展    // TransactionSynchronization同步器是最为重要的一个扩展点~~~    // 这里是个set 所以每个线程都可以注册N多个同步器    private static final ThreadLocal> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");    // 事务的名称    private static final ThreadLocal currentTransactionName = new NamedThreadLocal<>("Current transaction name");    // 事务是否是只读    private static final ThreadLocal currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");    // 事务的隔离级别    private static final ThreadLocal currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");    // 事务是否开启 actual:真实的    private static final ThreadLocal actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");    // 判断是否是开启了和线程绑定机制。当开启了之后,我们通过DataSourceUtils获取的连接就是当 前线程的连接了。    public static boolean isSynchronizationActive() {        return (synchronizations.get() != null);    }    /*** 初始化同步器方法。此方法就是在synchronizations中设置了一个LinkedHashSet * 当然如果事务已经开启了,就不能再初始化同步器了 而是直接注册 */    public static void initSynchronization() throws IllegalStateException {        if (isSynchronizationActive()) {            throw new IllegalStateException("Cannot activate transaction synchronization - already active");        }        logger.trace("Initializing transaction synchronization");        synchronizations.set(new LinkedHashSet<>());    }// 清除所有和当前线程相关的(注意:此处只是clear清除,和当前线程的绑定而已~~~)     public static void clear() {        synchronizations.remove();        currentTransactionName.remove();        currentTransactionReadOnly.remove();        currentTransactionIsolationLevel.remove();        actualTransactionActive.remove();    }    //其余方法略 }

    07

    TransactionAwareDataSourceProxy

    这是 Spring 提供的一个数据源代理类,它继承了 DelegatingDataSource 类。

    因为数据连接泄露是个很头疼的问题,Spring 框架也提供了很多种办法来避免这个问题。

    比如使用 XXXTemplate,当然其背后是 DataSourceUtils。

    同时还有另外一种办法,使用 TransactionAwareDataSourceProxy。

    通过 TransactionAwareDataSourceProxy 对数据源代理后,数据源对象就有了事务上下文感知的能力了。

    当然看源码会发现,其实它还是使用的 DataSourceUtils。

    public class TransactionAwareDataSourceProxy extends DelegatingDataSource {    /*** 暴露出来的获取连接的方法 */    @Override    public Connection getConnection() throws SQLException {        return getTransactionAwareConnectionProxy(obtainTargetDataSource());    }    /*** 使用JDK的动态代理创建连接的代理对象 */    protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) {        return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(), new Class>[]{ConnectionProxy.class}, new TransactionAwareInvocationHandler(targetDataSource));    }    /*** InvocationHandler的具体实现(增强的部分) */    private class TransactionAwareInvocationHandler implements InvocationHandler {        private final DataSource targetDataSource;        @Nullable        private Connection target;        private boolean closed = false;        public TransactionAwareInvocationHandler(DataSource targetDataSource) {            this.targetDataSource = targetDataSource;        }        @Override        @Nullable        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on ConnectionProxy interface coming in...             if (method.getName().equals("equals")) {                // Only considered as equal when proxies are identical.                return proxy == args[0];            } else if (method.getName().equals("hashCode")) {                // Use hashCode of Connection proxy.                return System.identityHashCode(proxy);            } else if (method.getName().equals("toString")) {                // Allow for differentiating between the proxy and the raw Connection.                StringBuilder sb = new StringBuilder("Transaction-aware proxy for target Connection ");                if (this.target != null) {                    sb.append("[").append(this.target.toString()).append("]");                } else {                    sb.append(" from DataSource [").append(this.targetDataSource).append("]");                }                return sb.toString();            } else if (method.getName().equals("unwrap")) {                if (((Class>) args[0]).isInstance(proxy)) {                    return proxy;                }            } else if (method.getName().equals("isWrapperFor")) {                if (((Class>) args[0]).isInstance(proxy)) {                    return true;                }            } else if (method.getName().equals("close")) {                // 释放资源仍然使用的是DataSourceUtils中的方法                DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource);                this.closed = true;                return null;            } else if (method.getName().equals("isClosed")) {                return this.closed;            }            if (this.target == null) {                if (this.closed) {                    throw new SQLException("Connection handle already closed");                }                if (shouldObtainFixedConnection(this.targetDataSource)) {                    this.target = DataSourceUtils.doGetConnection(this.targetDataSource);                }            }            Connection actualTarget = this.target;            if (actualTarget == null) {                 //获取连接使用的也是DataSourceUtils中的方法                actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource);            }            if (method.getName().equals("getTargetConnection")) {                return actualTarget;            }// Invoke method on target Connection.             try {                Object retVal = method.invoke(actualTarget, args);                // If return value is a Statement, apply transaction timeout.                 // Applies to createStatement, prepareStatement, prepareCall.                 if (retVal instanceof Statement) {                    DataSourceUtils.applyTransactionTimeout((Statement) retVal, this.targetDataSource);                }                return retVal;            } catch (InvocationTargetException ex) {                throw ex.getTargetException();            } finally {                if (actualTarget != this.target) {                    DataSourceUtils.doReleaseConnection(actualTarget, this.targetDataSource);                }            }        }    }    //其余代码略 }

    加油

    精彩推荐 #

    45f669d3588bf1618f83ccbb33eef741.png 分布式系统「全链路日志追踪」实战之 RestTemplate & Feign

    45f669d3588bf1618f83ccbb33eef741.png 分布式任务调度框架 Elastic-Job 之动态任务发布实现详解

    45f669d3588bf1618f83ccbb33eef741.png  小白都能看得懂的服务调用链路追踪设计与实现

    45f669d3588bf1618f83ccbb33eef741.png   事务看完这篇你只能算入门

    45f669d3588bf1618f83ccbb33eef741.png  [三步法] 可视化分析定位线上 JVM 问题

    45f669d3588bf1618f83ccbb33eef741.png  从 Java 代码如何运行聊到 JVM 和对象的创建-分配-定位-布局-垃圾回收

    45f669d3588bf1618f83ccbb33eef741.png   记一次生产频繁出现 Full GC 的 GC日志图文详解

    45f669d3588bf1618f83ccbb33eef741.png   从源码到实战之 Spring 中的 JdbcTemplate 及策略模式自定义 JdbcTemplate 实现

    588bb5e6bb9f994883e19fd62b681f5b.png

     "在看"吗,赶快分享和收藏吧

    d92d7803222cac958a074a89b5769723.gif

    展开全文
  • 关于Spring的Transactional注解作用范围

    千次阅读 2016-08-26 16:55:40
    Spring@Transactional注解是可继承了(Spring4.1)
  • 1,一般在service里加@Transactional注解,不建议在接口上添加,加了此注解后此类会纳入spring事务管理中,每个业务方法执行时,都会开启一个事务,不过都是按照相同管理机制。 2,@Transactional注解只能应用到...
  • @Transactional注解不起作用

    千次阅读 2019-01-14 21:58:21
    最近,在做springMVC框架练习时,体验了几种事务管理方式,其中在用@Transactional注解实现事务管理时,遇到了一些问题,最终得到了解决,现跟大家分享下。 先介绍下我做练习用例子:在数据库中建了个学生信息...
  • 引言昨天公众号粉丝咨询了一个...@Transactional注解相信大家并不陌生,平时开发中很常用一个注解,它能保证方法内多个数据库操作要么同时成功、要么同时失败。使用@Transactional注解时需要注意许多细节,不然...
  • 近期项目中遇到很多需要事务处理的逻辑,所以对@Transactional注解的作用条件测试了一番。 @Transactional注解是spring定义的一个注解,可以被子类继承,可以声明在类型、方法上,声明在类型时,可以看作给该类型下...
  • Sqlserver(自动提交)2、Ssm采用注解方式管理事务,采用是spring提供DataSourceTransactionManager类来进行管理事务。当请求到来,spring该类首先判断是否有该注解,如果有则表示支持事务,于是sp...
  • @Transactional注解不起作用解决办法及原理分析

    万次阅读 多人点赞 2018-12-09 00:21:50
    Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。例如以下代码。 定义一个错误@Transactional标注实现,修饰一个默认访问符方法 /** * @author zhoujy * @date 2018年12月06日...
  • @Transactional注解事务不起作用 问题:今天在项目中碰到一个事务问题,使用@Transactional注解事务,抛出异常不会滚。 解决一:https://blog.csdn.net/u011410529/article/details/54287307 解决二:以上方案没有能...
  • 1、@Transactional注解的注意点 @Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的...
  • @Transactional注解详解

    千次阅读 2019-09-11 15:45:18
    @Transactional注解可以作用于接口、接口方法、类以及类方法上@Transactional注解的可用参数readOnly该属性用于设置当前事务是否为只读事务,设...
  • Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。例如以下代码。 定义一个错误@Transactional标注实现,修饰一个默认访问符方法 @Component public class TestServiceImpl { @...
  • 用法@Transactional 可以作用于...虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口代理时它才会生效。另外, @T
  • 目录引言事务@Transactional介绍@Transactional注解可以作用于哪些地方?@Transactional注有哪些属性?propagation属性isolation 属性 引言 在开发过程中,经常需要使用事务保证业务逻辑一致性。这个时候可以使用...
  • 1、@Transactional 作用在非 public 修饰方法上 2、@Transactional 作用于接口,使用 CGLib 动态代理 3、@Transactional 注解属性 propagation 设置以下三种可能导致无法回滚 SUPPORTS:如果当前存在事务,则...
  • @Transactional注解使用

    2019-10-21 16:25:23
    @Transactional注解 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类所有 public 方法将都具有该类型事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别定义。 虽然@...
  • @Transactional注解事务不回滚不起作用无效

    万次阅读 多人点赞 2017-01-09 11:23:58
    先来了解一下@Transactional注解事务特性吧,可以更好排查问题 1、service类标签(一般不建议在接口上)上添加@Transactional,可以将整个类纳入spring事务管理,在每个业务方法执行时都会开...
  • @Transactional注解

    2020-08-03 09:20:27
    一般情况下我们在处理具体的业务都是在Service层来进行处理操作,此时如果在Service类上添加@Transactional注解的话,那么Service曾的每一个业务方法调用的时候都会打开一个事务。 注意点: Spring默认情况下会对...
  • 一、前言开发中我们经常使用 @Transactional注解来启用Spring事务管理,但是如果使用方法不当,会遇到注解不生效该事务回滚地方却没有回滚问题。总结下一般是以下几个原因:@Transactional 注解只能应用到 ...
  • @Transactional 注解,声明事物管理两种方式之一。 @Transactional属性  用法  @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类所有 public 方法将都具有该类型事务...
  • 在类内部调用(即this调用)时,被调用方法的事务声明(@Transactional 注解)将不起作用,注意是@Transactional 注解不起作用,不是没有事务,因为可能存在共享事务(有注解的方法调用没有注解的方法)。 只有在外

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 534
精华内容 213
关键字:

transactional注解的作用