源码分析(1)---手写Spring事务框架

AOP技术概述

  • Spring事务就是基于AOP的环绕通知和异常通知进行实现的
  • Spring的事务分为两种
    (1)编程式事务:手动提交回滚事务等
    (2)声明式事务:有注解版本,有扫包版本
  • Spring事务底层使用编程式事务进行包装的
  • Spring的核心技术:AOP和IOC
  • AOP就是面向切面编程,能够解决代码的复用问题
  • AOP编程的核心点:在方法之前或者之后处理事情
  • AOP底层的实现原理:代理设计模式(动态代理)
  • AOP的应用场景
    (1)日志
    (2)事务
    (3)权限,就是调用该方法前判断能否调用该方法
    (4)参数验证
    (5)性能监控
    (6)异常处理
    。。。。。等等
  • 为什么要使用AOP

复用代码和解耦

  • AOP技术:

(1)关注点

  • 重复代码就叫做关注点

(2)切入点

  • 执行目标对象的方法,动态植入切面代码
  • 可以通过切入点表达式,指定拦截哪些类的哪些方法;给指定的类在运行的时候植入切面类的代码

(3)切面

  • @Aspect的类
  • 关注点形成的类,就叫做切面(类)
  • 面向切面编程,就是指对很多功能都有的重复代码进行抽取,再在运行的时候向业务方法上动态植入“切面类的代码”

AOP的技术原理

AOP的原理其实就是依靠静态代理和动态代理进行实现的。其中静态代理需要手动生成目标代理对象(不推荐);动态代理又分为JDK的动态代理(基于反射实现)和CGLIB动态代理(CGLIB底层基于ASM实现的),推荐使用CGLIB因为,CGLIB效率高,直接操作字节码的效率是高于反射的。都是虚拟生成代理类的

  • CGLIB动态代理和JDK动态代理的区别:

(1)java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法之前调用InvokeHandler来处理。而CGLIB利用了ASM开源包,对代理对象类的class文件做修改和新增,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
(2)Spring中,如果目标对象实现了接口,默认情况下采用JDK的动态代理实现AOP
(3)Spring中,如果目标对象实现了接口,可以强制使用CGLIB实现AOP
(4)Spring中,如果目标对象没有实现接口,必须采用CGLIB,spring会自动在JDK和CGLIB之间做转换。
(5)JDK动态代理只能针对实现了接口的类生成代理,而不能针对类。
(6)CGLIB是针对类实现代理,主要是对指定的类生成了一个子类,覆盖其中的方法
因为是继承所以类不要用final修饰,final修饰类,不能被继承

使用SpringAOP技术面向切面编程

  • AOP分为两种
    (1)编程式事务
    (2)声明式事务
  • 声明式事务分为
    (1)注解版本
    (2)XML版本
    其中注解版本是非常推荐的,其他的方法不推荐
  • AOP编程中存在几个通知:
    (1)前置通知

@Before
在方法执行之前执行

(2)后置通知

@After
在方法运行后执行

(3)运行通知

@AfterReturning

(4)异常通知

@AfterThrowing
发生异常后执行,不发生异常就不会执行。

(5)环绕通知

@Around
在方法之前和之后处理
其中有参数ProceedingJoinPoint,proceedingJoinPoint.proceed()调用方法。如果调用方法抛出异常,不会执行环绕通知后面的代码。

  • java代码模拟实现:
    (1)主要的架构:
    在这里插入图片描述
    (2)引入pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xiyou</groupId>
    <artifactId>spring-aop</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- 引入Spring-AOP等相关Jar -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.5.3</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
    </dependencies>

</project>

(3)spring.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <context:component-scan base-package="com.xiyou"></context:component-scan>
</beans>

分别开启了AOP的注解形式,以及扫描包的范围
(4)UserService接口:

package com.xiyou.service;

public interface UserService {
    public void add();
}

(5)UserServiceImpl

package com.xiyou.service.impl;

import com.xiyou.service.UserService;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("给数据库中添加数据。。。");
    }
}

(6)AOP

package com.xiyou.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 切面类
 */
@Component
@Aspect
public class AopLog {

    /**
     * 前置通知
     */
    @Before("execution(* com.xiyou.service.UserService.add(..))")
    public void begin(){
        System.out.println("前置通知");
    }

    /**
     * 后置通知
     */
    @After("execution(* com.xiyou.service.UserService.add(..))")
    public void after(){
        System.out.println("后置通知");
    }

    /**
     * 运行通知
     */
    @AfterReturning("execution(* com.xiyou.service.UserService.add(..))")
    public void returning(){
        System.out.println("运行通知");
    }

    /**
     * 异常通知
     */
    @AfterThrowing("execution(* com.xiyou.service.UserService.add(..))")
    public void afterThrowing(){
        System.out.println("异常通知");
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     */
    @Around("execution(* com.xiyou.service.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知开始");
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知结束");
    }
}

(7)运行主函数:

package com.xiyou;

import com.xiyou.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService)classPathXmlApplicationContext.getBean("userService");
        userService.add();
    }
}

运行结果是:

前置通知
环绕通知开始
给数据库中添加数据。。。
后置通知
运行通知
环绕通知结束
  • 程序不报错的前提下:

通过上面的运行结果,我们可以看到
(1)先运行前置通知
(2)再运行环绕通知的前置
(3)执行具体的业务方法
(4)执行后置通知
(5)执行运行通知
(6)执行环绕通知的后置
此时因为没有报错,不会执行异常通知。

  • 如果我们的UserServiceImpl执行报错,那么我们的结果是:
前置通知
环绕通知开始
后置通知
异常通知
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.xiyou.service.impl.UserServiceImpl.add(UserServiceImpl.java:10)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:42)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
	at com.xiyou.aop.AopLog.around(AopLog.java:53)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
	at com.sun.proxy.$Proxy11.add(Unknown Source)
	at com.xiyou.TestMain.main(TestMain.java:10)

Process finished with exit code 1

我们发现其执行完后置通知后执行了异常通知,因为环绕通知中没有捕捉异常,所以方法报错,后续方法无法继续执行。

Spring声明事务与编程事务(刚性事务,分布式事务是柔性事务)

  • Spring的事务:
    (1)编程事务:需要自己去开启,提交,回滚等操作。
    (2)声明事务:本质上就是编程事务+反射实现。
    Spring的声明事务相当于是编程事务+反射机制进行包装的。相当于编程事务是手动事务(手动提交和回滚),声明事务是自动事务,自动事务分为注解版本和扫包版本。

  • 事务的特性:ACID
    (1)原子性
    原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
    (2)一致性
    一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性的状态,也就是说一个事务执行之前和执行之后都必须处于一致性的状态
    (3)隔离性
    隔离性是当多个用户并发访问数据库的时候,比如操作同一张表的时候,数据库为每个用户开启的事务,不能被其他事务的操作而干扰,多个并发事务之间要相互隔离。
    (4)持久性
    持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即使在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

搭建Spring事务环境,手写编程事务

  • 编程事务是手动事务,需要手动begin,commit,rollback等
  • 所谓的编程式事务指的是通过编码的方式实现事务,自己实现事务的提交,回滚等操作,类似于JDBC的编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理推荐用TransactionTemplate。
  • 举例说明,我们自己实现AOP的事务框架,直接自己实现事务的创建,提交和回滚
    (1)pom文件
<!-- 引入Spring-AOP等相关Jar -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.5.3</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>

(2)spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
	<context:component-scan base-package="com.xiyou.mayi.thread5.aop"></context:component-scan>
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->

	<!-- 1. 数据源对象: C3P0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
		<property name="user" value="root"></property>
		<property name="password" value="05131004"></property>
	</bean>

	<!-- 2. JdbcTemplate工具类实例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 3.配置事务 -->
	<bean id="dataSourceTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

</beans>

这里我们开启了AOP的注解形式,也规定了自动扫包的范围,同时注入了数据源,并且配置了JDBCTemplate和配置了事务
(3)自己实现事务的工具类,提交事务,回滚事务,创建事务,这里我们将事务设置成多例的,避免线程不安全

package com.xiyou.mayi.thread5.aop.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

/**
 * 手写事务的实现过程
 * 设置成多例避免线程安全问题
 */
@Component
@Scope("prototype")
public class TransactionUtils {
    // 获取事务源
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    // 开启事务
    // 开启默认隔离级别
    public TransactionStatus begin(){
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transactionStatus;
    }

    // 提交事务
    public void commit(TransactionStatus transactionStatus){
        dataSourceTransactionManager.commit(transactionStatus);
    }

    // 回滚事务
    public void rollback(TransactionStatus transactionStatus){
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}

(4)与数据库交互

package com.xiyou.mayi.thread5.aop.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 给数据库中提交数据
     * @param name
     * @param age
     */
    public void add(String name, int age) {
        String sql = "insert into t_users(name, age) values(?, ?)";
        int update = jdbcTemplate.update(sql, name, age);
        System.out.println("updateResult : " + update);
    }
}

(5)Service层
接口:UserService

package com.xiyou.mayi.thread5.aop.service;

public interface UserService {
    public void add();
}

实现类:UserServiceImpl

package com.xiyou.mayi.thread5.aop.service.impl;

import com.xiyou.mayi.thread5.aop.dao.UserDao;
import com.xiyou.mayi.thread5.aop.service.UserService;
import com.xiyou.mayi.thread5.aop.transaction.TransactionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private TransactionUtils transactionUtils;

    @Override
    public void add() {
        userDao.add("test001", 20);
        // 故意写错,看是否提交
        int i = 1/0;
        System.out.println("#####");
        userDao.add("test002", 30);
    }
}

这里的实现调用了两次dao的add方法,我们要达到的效果是,因为调用两次dao的add方法都在一个方法中,所以要么都成功,要么都失败。所以我们在这之间故意写错,看是否能够提交
(6)自己实现AOP切面,使用异常通知进行回滚,环绕通知将其放在一个事务中,负责开启和提交事务

package com.xiyou.mayi.thread5.aop.MyAspect;

import com.xiyou.mayi.thread5.aop.transaction.TransactionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

@Aspect
@Component
public class AopTransaction {

    @Autowired
    private TransactionUtils transactionUtils;

    /**
     * 异常通知,用来发现有异常就进行回滚操作
     */
    @AfterThrowing("execution(* com.xiyou.mayi.thread5.aop.service.UserService.add(..))")
    public void afterThrowing(){
        System.out.println("######回滚事务######");
        // 获取当前的事务进行回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }

    /**
     * 环绕通知,用来开启事务,提交事务
     * 如果出错,不进行后续操作,直接到异常通知中
     */
    @Around("execution(* com.xiyou.mayi.thread5.aop.service.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("开启事务");
        TransactionStatus transactionStatus = transactionUtils.begin();
        // 执行调用,如果这里的异常进行try-catch就不会进入异常通知中
        proceedingJoinPoint.proceed();
        System.out.println("提交事务");
        transactionUtils.commit(transactionStatus);
    }
}

使用事务的注意事项:

  • 事务是程序运行如果没有错误,会自动提交事务,如果程序运行发生异常,则会自动回滚。
  • 如果使用了try-catch捕获异常,一定要在catch中进行rollback操作,因为这时候,异常通知因为try-catch的原因不会调用
  • 我们在异常通知中没有当前事务对象,得这样回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

(7)主启动类

package com.xiyou.mayi.thread5.aop;

import com.xiyou.mayi.thread5.aop.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyMain {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService)classPathXmlApplicationContext.getBean("userService");
        userService.add();

    }

}

最后的结果是:

开启事务
九月 04, 2019 11:58:06 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hge8sha41y74atp1xb8p4b|46238e3f, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge8sha41y74atp1xb8p4b|46238e3f, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/test, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
Exception in thread "main" org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope
	at org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus(TransactionAspectSupport.java:111)
	at com.xiyou.mayi.thread5.aop.MyAspect.AopTransaction.afterThrowing(AopTransaction.java:27)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:603)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:59)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
	at com.sun.proxy.$Proxy13.add(Unknown Source)
	at com.xiyou.mayi.thread5.aop.MyMain.main(MyMain.java:11)
updateResult : 1
######回滚事务######

观察数据库发现并没有进行提交事务,因为出错,所以两次add因为在一个方法中,都会回滚。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值