前言
AOP是Aspect-Oriented Programming面向切面编程的缩写;
AOP和IOC一样也是一种编程思想,最终的目的都是为了实现代码在编译期的解耦;
IOC可实现对象与对象之间的解耦,AOP可实现方法和方法之间的解耦(AOP解耦粒度会更细);
当我们把dao层和service层的实现类对象,以及实现类对象需要的依赖,放进Sprig的容器管理之后;
这些实现类对象的某些方法都依赖同一段代码,例如service层和dao层实现类的所有方法都需要增加日志、事务、代码性能测试功能,应该使用AOP;
AOP思想的目的是可以在不修改源代码的基础上,对原有功能进行增强。
SpringAOP是对AOP思想的一种实现,Spring底层同时支持jdk和cglib动态代理。
Spring会根据被代理的类是否有接口自动选择代理方式:
-
如果有接口,就采用jdk动态代理(当然,也可以强制使用cglib)
-
1.代理技术
代理技术是实现AOP思想的手段;
1.1.jdk代理
需要原始对象和代理对象实现同一个接口;

package com.zhanggen; import com.zhanggen.config.SpringConfig; import com.zhanggen.log.Loger; import com.zhanggen.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { //日志对象 @Autowired private Loger loger; //目标对象 @Autowired private AccountService accountService; @Test public void testSaveAndFind() { //产生代理对象 //1.获取目标对象 //2.编写增强逻辑 InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; try { loger.beforMetod(); //调用目标对象的的方法逻辑 //如果目标对象的方法有返回值,则返回,没有则返回值是null obj = method.invoke(accountService, args); loger.afterMetod(); } catch (Exception e) { loger.exceptionMetod(); } return obj; } }; //3.创建代理对象 AccountService instance = (AccountService) Proxy.newProxyInstance( accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), invocationHandler ); //4.调用代理对象的方法 instance.save(); } }
1.2.cglib代理
需要代理对象继承原始对象;

package com.zhanggen; import com.zhanggen.config.SpringConfig; import com.zhanggen.log.Loger; import com.zhanggen.service.impl.AccountServiceImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.InvocationHandler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.lang.reflect.Method; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { //日志对象 @Autowired private Loger loger; //目标对象 @Autowired private AccountServiceImpl accountService; @Test public void testSaveAndFind() { //产生代理对象 //1.获取目标对象 //2.编写增强逻辑 InvocationHandler invocationHandler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; try { loger.beforMetod(); //调用目标对象的的方法逻辑 //如果目标对象的方法有返回值,则返回,没有则返回值是null obj = method.invoke(accountService, args); loger.afterMetod(); } catch (Exception e) { loger.exceptionMetod(); } return obj; } }; //3.使用cglib创建代理对象 //3-1:创建增强器 Enhancer enhancer = new Enhancer(); //3-2:设置父类 enhancer.setSuperclass(AccountServiceImpl.class); //3-3:设置增强逻辑 enhancer.setCallback(invocationHandler); //3-4:得到代理对象 AccountServiceImpl instance = (AccountServiceImpl) enhancer.create(); //4.调用代理对象的方法 instance.save(); } }
2.AOP术语
目标对象: 被代理对象
连接点: 目标对象中的所有方法;(全部方法)
切入点: 目标对象中要进行功能增强的方法,可以有1个也可以有多个;(一部分方法)
增强(通知)对象: 需要增强的功能(日志 事务),一般是1个增强对象,增强对象中有增强方法;
织入(动词):将切点方法和增强方法拼接过程
代理对象:经过功能增强之后的对象
切面(名词):切点+增强,切面是一种描述,描述了这样一件事情: 一个什么样的增强方法加入到了 一个什么样的切点方法的 什么位置,本质上就是在描述增强方法和切点方法的执行顺序;
2.AOP实现过程
开发阶段分别开发,运行阶段组装运行;
3.AOP的优势
二、AOP日志案例
使用SpringAop完成在业务(Service)层类中的方法上打印日志
1.

<?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"> <parent> <artifactId>spring</artifactId> <groupId>com.zhanggen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>day03-04-springaop-xml</artifactId> <dependencies> <!--spring核心--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--切点表达式解析坐标--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> <!--测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.6.RELEASE</version> </dependency> </dependencies> </project>
2.

package com.zhanggen.service; import java.util.List; public interface AccountService { //保存 void save(Object obj); //查询所有 List findAll(); //主键查询 Object findById(Integer id); }
定义service层实现类

package com.zhanggen.service.impl; import com.zhanggen.service.AccountService; import org.springframework.stereotype.Service; import java.util.List; @Service public class AccountServiceImpl implements AccountService { @Override public void save(Object obj) { System.out.println("save"); } @Override public List findAll() { System.out.println("findAll"); return null; } @Override public Object findById(Integer id) { System.out.println("findById"); return "哈哈哈"; } }
3.

package com.zhanggen.log; import org.springframework.stereotype.Component; @Component public class Loger { public void beforMethod() { System.out.println("进入方法前"); } public void afterMethod() { System.out.println("进入方法后"); } }
4.

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--表示这是一段aop设置--> <aop:config> <!-- 配置切点:通过规则表达式选择出1个切点方法 id="标识" expression="execution(切点表达式)" --> <aop:pointcut id="pt" expression="execution(java.util.List com.zhanggen.service.impl.AccountServiceImpl.findAll())"></aop:pointcut> <!-- 配置1个切面:增强方法和切点方法的执行顺序 增强方法:ref="loger"+ method="beforMethod" 切点方法:pointcut-ref="pt" 执行顺序: aop:before 执行顺序(增强方法在切点方法之前运行) --> <aop:aspect ref="loger"> <aop:before method="beforMethod" pointcut-ref="pt"></aop:before> <aop:after method="afterMethod" pointcut-ref="pt"></aop:after> </aop:aspect> </aop:config> </beans>
5.测试

package com.zhanggen; import com.zhanggen.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void Test1() { accountService.findAll(); } }
1.1.匹配1个切点
切点表达式(expression):通过规则表达式,从目标对象中选择出1个切点方法
id="标识"
<aop:pointcut id="pt" expression="execution(java.util.List com.zhanggen.service.impl.AccountServiceImpl.findAll())">
</aop:pointcut>
1.2.模糊匹配多个切点
*占位符:表示匹配1个或多个
..占位符:表示匹配0个或多个
<aop:pointcut id="pt" expression="execution(* com.zhanggen.service.impl.AccountServiceImpl.*(..))"></aop:pointcut>
- 前置通知(before) :增强方法在切点运行之前执行
- 后置通知(after-returning):增强方法在切点正常运行结束之后执行
- 异常通知(after-throwing):增强方法在切点发生异常的时候执行
- 最终通知(after):增强方法在切点的最终执行
try { 前置通知(before) :增强方法在切点运行之前执行 // 切点方法执行 后置通知(after-returning):增强方法在切点正常运行结束之后执行 }catch (Exception e){ 异常通知(after-throwing):
增强方法在切点发生异常的时候执行 }finally { 最终通知(after):增强方法在切点的最终执行 }

package com.zhanggen.log; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.stereotype.Component; @Component public class Loger { public void beforMethod() { System.out.println("进入方法前"); } public void afterMethod() { System.out.println("进入方法后"); } //环绕通知方法:可以替代以上所有方法 public Object aroundMethod(ProceedingJoinPoint pjp) { Object obj = null; try { System.out.println("进入方法前"); obj = pjp.proceed(); System.out.println("进入方法后"); } catch (Throwable throwable) { System.out.println("方法出现异常结束"); } finally { System.out.println("方法正常结束"); } return obj; } }

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--表示这是一段aop设置--> <aop:config> <!-- 配置切点:通过规则表达式选择出1个切点方法 id="标识" expression="execution(切点表达式)" --> <aop:pointcut id="pt" expression="execution(* com.zhanggen.service.impl.AccountServiceImpl.*(..))"></aop:pointcut> <!-- 配置1个切面:增强方法和切点方法的执行顺序 增强方法:ref="loger"+ method="beforMethod" 切点方法:pointcut-ref="pt" 执行顺序: aop:before 执行顺序(增强方法在切点方法之前运行) --> <aop:aspect ref="loger"> <!--<aop:before method="beforMethod" pointcut-ref="pt"></aop:before>--> <!--<aop:after method="afterMethod" pointcut-ref="pt"></aop:after>--> <aop:around method="aroundMethod" pointcut-ref="pt"></aop:around> </aop:aspect> </aop:config> </beans>
3.3.环绕通知获取切点执行细节
pjp对象代表当前切点,可以通过这个对象获取到正在调用类的方法、参数、返回值、异常信息、调用时间,作为日志信息的一部分;

package com.zhanggen.log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.Date; @Component public class Loger { public void beforMethod() { System.out.println("进入方法前"); } public void afterMethod() { System.out.println("进入方法后"); } //环绕通知方法:可以替代以上所有方法 //pjp对象代表切点:可以通过这个对象获取到正在调用类的 方法、参数、返回值、异常信息、调用时间; public Object aroundMethod(ProceedingJoinPoint pjp) { //日志字符串可变长度字符串 StringBuilder stringBuilder = new StringBuilder(); Object obj = null; try { System.out.println("进入方法前"); obj = pjp.proceed(); stringBuilder.append("类:" + pjp.getTarget().getClass().getName()); Signature signature = (MethodSignature) pjp.getSignature(); stringBuilder.append("方法名称:" + signature.getName()); stringBuilder.append("方法参数:" + Arrays.toString(pjp.getArgs())); stringBuilder.append("方法返回值:" + obj); stringBuilder.append("调用时间:" + new Date()); System.out.println("进入方法后"); } catch (Throwable e) { System.out.println("方法出现异常结束"); stringBuilder.append("异常信息:" + e.getMessage()); } finally { System.out.println("方法正常结束"); } //打印日志 System.out.println(stringBuilder.toString()); return obj; } }
使用注解的方式替换xml配置文件中的配置,AOP的注解配置在增强方法上;
1.激活切面自动代理
配置文件中配置激活切面自动代理

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--激活切面自动代理--> <aop:aspectj-autoproxy/> </beans>
配置类中配置激活切面自动代理

package com.zhanggen.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; //注解扫描 @ComponentScan("com.zhanggen") //激活切面自动代理 @EnableAspectJAutoProxy public class SpringConfig { }
2.四大通知

package com.zhanggen.log; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Aspect //切面:增强方法和切点方法的执行顺序 @Component public class Loger { // 定义切点:默认id是方法名称 @Pointcut("execution(* com.zhanggen.service.impl.AccountServiceImpl.*(..))") public void pt() { } //代表此注解标注的方法会在切点之前运行 @Before("pt()") public void beforMethod() { System.out.println("进入方法前"); } @AfterReturning("pt()") public void afterReturningMethod() { System.out.println("方法正常运行结束后"); } @AfterReturning("pt()") public void afterThrowingMethod() { System.out.println("方法出现异常后"); } @After("pt()") public void afterMethod() { System.out.println("方法运行到最后"); } //环绕通知方法:可以替代以上所有方法 //pjp对象代表切点:可以通过这个对象获取到正在调用类的 方法、参数、返回值、异常信息、调用时间; // public Object aroundMethod(ProceedingJoinPoint pjp) { // //日志字符串可变长度字符串 // StringBuilder stringBuilder = new StringBuilder(); // Object obj = null; // try { // System.out.println("进入方法前"); // obj = pjp.proceed(); // stringBuilder.append("类:" + pjp.getTarget().getClass().getName()); // Signature signature = (MethodSignature) pjp.getSignature(); // stringBuilder.append("方法名称:" + signature.getName()); // stringBuilder.append("方法参数:" + Arrays.toString(pjp.getArgs())); // stringBuilder.append("方法返回值:" + obj); // stringBuilder.append("调用时间:" + new Date()); // System.out.println("进入方法后"); // } catch (Throwable e) { // System.out.println("方法出现异常结束"); // stringBuilder.append("异常信息:" + e.getMessage()); // } finally { // System.out.println("方法正常结束"); // } // //打印日志 // System.out.println(stringBuilder.toString()); // return obj; // } }
注解版四大通知一起使用,执行顺序有问题,不建议使用
3.环绕通知

package com.zhanggen.log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MemberSignature; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.Date; @Aspect //切面:增强方法和切点方法的执行顺序 @Component public class Loger { // 定义切点:默认id是方法名称 @Pointcut("execution(* com.zhanggen.service.impl.AccountServiceImpl.*(..))") public void pt() { } //环绕通知注解配置 @Around("pt()") public Object aroundMethod(ProceedingJoinPoint pjp) { //日志字符串可变长度字符串 StringBuilder stringBuilder = new StringBuilder(); Object obj = null; try { System.out.println("进入方法前"); obj = pjp.proceed(); stringBuilder.append("类:" + pjp.getTarget().getClass().getName()); Signature signature = (MemberSignature) pjp.getSignature(); stringBuilder.append("方法名称:" + signature.getName()); stringBuilder.append("方法参数:" + Arrays.toString(pjp.getArgs())); stringBuilder.append("方法返回值:" + obj); stringBuilder.append("调用时间:" + new Date()); System.out.println("进入方法后"); } catch (Throwable e) { System.out.println("方法出现异常结束"); stringBuilder.append("异常信息:" + e.getMessage()); } finally { System.out.println("方法正常结束"); } //打印日志 System.out.println(stringBuilder.toString()); return obj; } }
4.测试

package com.zhanggen; import com.zhanggen.config.SpringConfig; import com.zhanggen.service.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration("classpath:applicationContext.xml") //配置文件 @ContextConfiguration(classes = SpringConfig.class) //配置类 public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void Test1() { accountService.save(1); System.out.println("===========================>"); accountService.findAll(); System.out.println("===========================>"); accountService.findById(1); } }
五、转账案例
两个用户可以相互转账功能,利用AOP的环绕通知,手动实现service层事务操作;

<?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"> <parent> <artifactId>spring</artifactId> <groupId>com.zhanggen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>day05-01-transfer</artifactId> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.15</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies> </project>

package com.zhanggen.domain; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Data public class Account { private Integer aid; private String name; private Float balance; }

package com.zhanggen.dao; public interface AccountDao { //减钱 void diff(String name, Float amount); //加钱 void add(String name, Float amount); }

package com.zhanggen.dao.impl; import com.zhanggen.dao.AccountDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class AccountDaoImpl implements AccountDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public void diff(String name, Float amount) { System.out.println(name); jdbcTemplate.update("update account set balance=balance-? where name=?", amount, name); } @Override public void add(String name, Float amount) { jdbcTemplate.update("update account set balance=balance+? where name=?", amount, name); System.out.println(name); } }

package com.zhanggen.service; public interface AccountService { //转账 void transfer(String sourceAcountName,String targetAcountName,Float amount); }

package com.zhanggen.service.impl; import com.zhanggen.dao.AccountDao; import com.zhanggen.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public void transfer(String sourceAcountName, String targetAcountName, Float amount) { accountDao.diff(sourceAcountName, amount); accountDao.add(targetAcountName, amount); } }

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--数据源--> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8"></property> <property name="username" value="zhanggen"></property> <property name="password" value="123.com"></property> </bean> <!--jdbcTemplate对象--> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="druidDataSource"></constructor-arg> </bean> </beans>

package com.zhanggen; import com.zhanggen.service.AccountService; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.junit.runner.RunWith;; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") //配置文件 public class AcountTransferTest { @Autowired private AccountService accountService; @Test public void testTransfer() { accountService.transfer("张根", "刘龙华", 20F); } }
-
编程式事务就是将业务代码和事务代码放在一起书写,它的耦合性太高,开发中基本不使用;
-
声明式事务其实就是将事务代码和业务代码隔离开发,然后通过一段配置让他们组装运行,最后达到事务控制的目的(使用);
其中声明式事务就是通过AOP原理实现的
1.2.
TransactionDefinition这个API是用来做事务定义的;
1.2.1.
事务传播行为指的就是当一个业务方法【被】另一个业务方法调用时,应该如何进行事务控制
a(){// a会将当前自己是否开启了事务这样一个状态传递给b b();//转账 b必须有事务 required b();//记录日志 b有无事务都行 supports } b(){ //自己 事务如何控制 }
传播行为配置项
只读事务(增 删 改不能使用,只能查询使用)
换句话说,只读事务只能用于查询方法
1.2.4.超时时长
事务超时时间, 此属性需要底层数据库的支持
目标对象:service对象(已完成)
增强对象:事务控制功能,DataSourceTransactionManager(Spring已经提供)但是这个事务管理器要正常工作,需要我们传递参数:事务隔离级别 事务传播行为 事务超时时间 事务是否只读
配置切面:增强对象(开启 提交 回滚 关闭)中各个方法跟切点方法的执行顺序(顺序是固定的),我们仅仅需要指定增强对象和切点方法;
2.

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--数据源--> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8"></property> <property name="username" value="zhanggen"></property> <property name="password" value="123.com"></property> </bean> <!--jdbcTemplate对象--> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="druidDataSource"></constructor-arg> </bean> <!--事务管理器:这个id就写transactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druidDataSource"></property> </bean> <!--配置事务管理(transactionManager)的参数,所有参数都使用默认值--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <!--配置切点:com.zhanggen.service.impl包下的所有类的所有方法--> <aop:pointcut id="pt" expression="execution(* com.zhanggen.service.impl.*.*(..))"/> <!-- 配置切面:advisor:这是为声明式事务专门准备的1个切面 advice-ref="带有参数的增强对象(事务管理器)”pointcut-ref="切点"" --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config> </beans>
2.2.指定事务管理器参数
name=“方法名”: 指的是事务参数作用方法的名称,支持模糊匹配,只要匹配成功1个,下一条就不再向下继续匹配
isolation="DEFAULT":事务隔离级别,DEFAULT表示根据数据库来定
propagation="REQUIRED":事务传播行为
timeout="-1":事务超时时长,-1永远不超时
read-only="false":事务是否只读
no-rollback-for:指定遇到什么异常时,无需回滚
rollback-for:指定遇到什么异常时,需回滚

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--数据源--> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8"></property> <property name="username" value="zhanggen"></property> <property name="password" value="123.com"></property> </bean> <!--jdbcTemplate对象--> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="druidDataSource"></constructor-arg> </bean> <!--事务管理器:这个id就写transactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druidDataSource"></property> </bean> <!--配置事务管理(transactionManager)的参数, name=“方法名”: 指的是事务参数作用方法的名称,支持模糊匹配,只要匹配成功1个,下一条就不再向下继续匹配 isolation="DEFAULT": 事务隔离级别,DEFAULT表示根据数据库来定 propagation="REQUIRED":事务传播行为 timeout="-1":事务超时时长,-1永远不超时 read-only="false":事务是否只读 no-rollback-for:指定遇到什么异常时,无需回滚 rollback-for:指定遇到什么异常时,需回滚 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false" no-rollback-for=""/> <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false" no-rollback-for=""/> </tx:attributes> </tx:advice> <aop:config> <!--配置切点:com.zhanggen.service.impl包下的所有类的所有方法--> <aop:pointcut id="pt" expression="execution(* com.zhanggen.service.impl.*.*(..))"/> <!-- 配置切面:advisor:这是为声明式事务专门准备的1个切面 advice-ref="带有参数的增强对象(事务管理器)”pointcut-ref="切点"" --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config> </beans>
3.
3.1.配置文件

<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置注解扫描--> <context:component-scan base-package="com.zhanggen"></context:component-scan> <!--数据源--> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8"></property> <property name="username" value="zhanggen"></property> <property name="password" value="123.com"></property> </bean> <!--jdbcTemplate对象--> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="druidDataSource"></constructor-arg> </bean> <!--事务管理器:这个id就写transactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druidDataSource"></property> </bean> <!--事务注解驱动--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
3.2.注解
@Transactional可以标注在类上,也可以标注在方法上,标注在方法上优先级高于类上;
@Transactional //支持事务控制
4.
纯注解就是去xml文件;

package com.zhanggen.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; @Configuration @EnableTransactionManagement //开启事务管理 public class TranscationConfig { // 注意方法名transactionManager固定 @Bean public DataSourceTransactionManager transactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南