1.AOP简介
AOP是在不改原有代码的前提下对代码进行增强
1.1 AOP概念
AOP(Aspect Oriented Programming) 面向切面编程,一种编程范式,指导开发者如何组织程
序结构。
1.2 AOP的作用
在不改原有代码的前提下对代码进行增强。
1.3 AOP的实现方式
JDK动态代理(默认):运行时创建接口的代理实例。
CGLib代码生成库动态代理:采用底层的字节码技术,当目标对象不存在接口时,创建子类代理的实例。
1.4 AOP的核心概念
为了能更好的理解 AOP 的相关概念,我们准备了一个环境,整个环境的内容我们暂时可以不用关
注,最主要的类为 : BookDaoImpl
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
//记录程序当前执行执行(开始时间)
Long startTime = System.currentTimeMillis();
//业务执行万次
for (int i = 0;i<10000;i++) {
System.out.println("book dao save ...");
}
//记录程序当前执行时间(结束时间)
Long endTime = System.currentTimeMillis();
//计算时间差
Long totalTime = endTime-startTime;
//输出信息
System.out.println("执行万次消耗时间:" + totalTime + "ms");
}
public void update(){
System.out.println("book dao update ...");
}
public void delete(){
System.out.println("book dao delete ...");
}
public void select(){
System.out.println("book dao select ...");
}
}
相关依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
Spring 配置类
@Configuration
@ComponentScan("priv.dandelion")
@EnableAspectJAutoProxy
public class SpringConfig {
}
AOP
package priv.dandelion.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void priv.dandelion.dao.BookDao.*te())")
private void pt() {
}
@Around("pt()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
//记录程序当前执行执行(开始时间)
Long startTime = System.currentTimeMillis();
//业务执行万次
for (int i = 0;i<10000;i++) {
// 表示对原始操作的调用
pjp.proceed();
}
//记录程序当前执行时间(结束时间)
Long endTime = System.currentTimeMillis();
//计算时间差
Long totalTime = endTime-startTime;
//输出信息
System.out.println("执行万次消耗时间:" + totalTime + "ms");
}
}
测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.save();
// bookDao.update();
// bookDao.delete();
// bookDao.select();
}
}
AOP的相关术语:
(1)连接点:在 AOP 中,将所以原始方法称为连接点(如右侧的 save,update,delete和select 方法)。
(2)切入点:在上述四个方法中,除 save 方法之外,仅 update 和 delete 方法执行了万次,即追加了功能。这些追加了同一种功能的方法(可以是一个也可以是多个,此处为两个)称为一个切入点,反之,如 save, select 方法就不是切入点。
切点表达式的写法
语法:
execution([修饰符]返回值类型包名.类名.方法名(参数))
例如:
execution(public void com.iflytek.aop.Target.method()) execution(void com.iflytek.aop.Target.* ( ..)) execution(* com.iflytek.aop.*.*( ..)) execution(* com.iflytek.aop..*.* (..)) execution(* *..*.*(..))
-
访问修饰符可以省略
-
返回值类型、包名、类名、方法名可以使用星号*代表任意
-
包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
-
参数列表可以使用两个点..表示任意个数,任意类型的参数列表
(3)通知:共性功能,即左边的方法。
通知的类型
通知的配置语法:
<!--配置文件方式--> <aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>
//注解方式 @通知注解("切点表达式")
名称 | 标签 | 注解 | 说明 |
---|---|---|---|
前置通知 | <aop:before> | @Before | 用于配置前置通知。指定增强的方法在切入点方法之前执行 |
后置通知 | <aop:AfterReturning> | @AfterReturning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
环绕通知 | <aop:Around> | @Around | 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 | <aop:AfterThrowing> | @AfterThrowing | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
最终通知 | <aop:After> | @After | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
(4)通知类:通知是一个方法,必须写在一个类中。
(5)切面:通知与切入点的关系,一个通知对应一个切入点,称为一个切面。
2.AOP入门案例
2.1 需求分析
使用SpringAOP的注解方式完成在方法执行的前打印出当前系统时间。对于SpringAOP的开发有两种方式,XML 和 注解,我们使用注解。
2.2 思路分析
- 导入坐标(pom.xml)
- 制作连接点(原始操作,Dao接口与实现类)
- 制作共性功能(通知类与通知)
- 定义切入点
- 绑定切入点与通知关系(切面)
2.3 环境准备
-
创建一个Maven项目
-
项目结构如下:
-
- pom.xml添加Spring-context依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
-
添加BookDao和BookDaoImpl类
public interface BookDao {
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save ...");
}
public void update(){
System.out.println("book dao update ...");
}
}
-
创建Spring的配置类
@Configuration @ComponentScan("com.itheima") public class SpringConfig { }
- 编写测试类
-
public class App { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); BookDao bookDao = ctx.getBean(BookDao.class); bookDao.save(); } }
2.4AOP实现步骤
步骤 1:pom.xml 添加依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
步骤2:定义接口与实现类
public interface BookDao {
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save ...");
}
public void update(){
System.out.println("book dao update ...");
}
}
步骤3:aop包下定义通知类和通知
通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印。(类名和方法名没有要求,可以任意)
public class MyAdvice {
//通知,共性功能
public void method(){
System.out.println(System.currentTimeMillis());
}
}
步骤4:定义切入点@Pointcut,说明该方法待增强
BookDaoImpl中有两个方法,分别是save和update,我们要增强的是update方法,该如何定义呢?
通过@Pointcut指定切入点方法格式和位置
public class MyAdvice {
//注解描述切入点的方法格式和位置,执行到update时候运行通知。切入点是要被增强的方法
//切入点表达式的public和异常名可以省略,下面已省略
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
//一个私有空壳方法,切入点依托一个不具有实际意义(无参无返空方法)的方法进行。
private void pt(){}
//通知,共性功能
public void method(){
System.out.println(System.currentTimeMillis());
}
}
说明:
- 切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑。
步骤5:制作切面@Before或其他通知
@Before是前置通知,通知在原始方法前通知。
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
//制作切面,这里通知和切入点的关系是before,通知在切入点之前运行
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
说明:@Before翻译过来是之前,也就是说通知会在切入点方法执行之前执行,除此之外还有其他四种类型,同理。
步骤6:注解指定该类为bean和切面类@Aspect
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
步骤7:配置类注解开启AOP功能@EnableAspectJAutoProxy
enable译为开启,使能够;EnableAspectJAutoProxy译为开启aspectj自动代理。
@Configuration
@ComponentScan("com.itheima")
//注解开启AOP
@EnableAspectJAutoProxy
public class SpringConfig {
}
步骤8:测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.update();
}
}
看到在执行update方法之前打印了系统时间戳,说明对原始方法进行了增强,AOP编程成功。
3.AOP知识小结
3.1 AOP的核心概念
概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
作用:在不惊动原始设计的基础上为方法进行功能增强
核心概念如下:
-
Target(目标对象):代理的目标对象
-
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring,,这些点指的是方法,因为spring只支持方法类型的连接点
-
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
-
Advice(通知/增强):所谓通知是指拦截Joinpoint之后所要做的事情就是通知
-
Aspect(切面):是切入点和通知(引介)的结合
-
Weaving (织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
3.2 切入点表达式
语法:
execution([修饰符]返回值类型包名.类名.方法名(参数))
例如:
execution(public void com.iflytek.aop.Target.method()) execution(void com.iflytek.aop.Target.* ( ..)) execution(* com.iflytek.aop.*.*( ..)) execution(* com.iflytek.aop..*.* (..)) execution(* *..*.*(..))
-
访问修饰符可以省略
-
返回值类型、包名、类名、方法名可以使用星号*代表任意
-
包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
-
参数列表可以使用两个点..表示任意个数,任意类型的参数列表
3.3 五种通知类型
名称 | 标签 | 注解 | 说明 |
---|---|---|---|
前置通知 | <aop:before> | @Before | 用于配置前置通知。指定增强的方法在切入点方法之前执行 |
后置通知 | <aop:AfterReturning> | @AfterReturning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
环绕通知 | <aop:Around> | @Around | 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 | <aop:AfterThrowing> | @AfterThrowing | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
最终通知 | <aop:After> | @After | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
3.4 通知中获取参数
获取切入点方法的参数,所有的通知类型都可以获取参数
JoinPoint:适用于前置、后置、返回后、抛出异常后通知,设置为第一个参数
ProceedingJoinPoint:适用于环绕通知
获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
返回后通知
环绕通知
获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
抛出异常后通知
环绕通知
4.事务管理
4.1 Spring事务简介
- 事务作用:在数据层保障一系列的数据库操作同成功同失败
- Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
4.2 事务的ACID原则
-
事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。
原子性(Atomicity):一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做例如:A和B两个人一共1000元,A给B转账100元,A付款100元,B收款100元, A的付款行为和B的收款行为要么都成功,要么都失败-
一致性(Consistency):
事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
例如:A和B两个人一共1000元,无论A,B两人互相转账多少次,A和B两个人总额都应该是1000元
-
隔离性(Isolation):
事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
例如:万达影院有《叶问4》电影票100张,允许所有人同时去淘票票上购票,当第100张电影票被A,B,C3人同时购买,如果A拿到第100张电影票,但是还在犹豫要不要付钱,则B,C必须等待A的决定,如果A决定付钱,B.C就无法抢到票,如果A超时不付钱,则这第100张电影票回归票池,从新分配。
-
持久性(Durability):
一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响
例如:万达影院有《叶问4》电影票100张,100张电影票销售完毕,对于每个购买者来说,他的购买记录已经产生,即使退票,他之前的购买记录也不会消失。
-
4.3 事务隔离级别
(1)为什么要事务隔离
如果没有定义事务隔离级别:
-
脏读 :在一个事务中读取到了另外一个事务修改的【未提交的数据】,而导致多次读取同一个数据返回的结果不一致 (必须要解决的)
例如: 1.事务1,小明的原工资为1000, 财务人员将小明的工资改为了8000【但未提交事务】 2.事务2,小明读取自己的工资 ,发现自己的工资变为了8000,欢天喜地! 3.事务1,财务发现操作有误,回滚了操作,小明的工资又变为了1000 像这样,小明读取的工资数8000是一个脏数据
-
不可重复读 : 在一个事务中读取到了另外一个事务修改的【已提交的数据】,而导致多次读取同一个数据返回的结果不一致
例如: 1.事务1,小明读取了自己的工资为1000,操作还没有完成 2.事务2,这时财务人员修改了小明的工资为2000,并提交了事务. 3.事务1,小明再次读取自己的工资时,工资变为了2000
-
幻读(虚读): 一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录
例如: 1.事务1,财务统计所有工资为5000的员工有10人。 2.事务2,人事向user表插入了一条员工记录,工资也为5000 3.事务1,财务再次读取所有工资为5000的员工 共读取到了11条记录,明明刚刚是10人啊?产生幻觉了?
(2)Spring事务隔离级别
隔离级别由低到高【读未提交】=>【读已提交】=>【可重复读】=>【序列化操作】
隔离级别 | 说明 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
ISOLATION_DEFAULT | spring默认数据库的隔离级别 | – | – | – |
ISOLATION_READ_UNCOMMITTED | 读未提交 | √ | √ | √ |
ISOLATION_READ_COMMITTED | 读已提交 | × | √ | √ |
ISOLATION_REPEATABLE_READ | 可重复读 | × | × | √ |
ISOLATION_SERIALIZABLE | 序列化操作 | × | × | × |
4.4 事务传播行为
-
指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
传播行为 | 说明 |
---|---|
REQUIRED【默认】 | 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务(增、删、改) |
SUPPORTS | 当前如果有事务,Spring就会使用该事务;否则不会开始一个新事务(查询) |
MANDATORY | 当前如果有事务,Spring就会使用该事务;否则会抛出异常 |
REQUIRES_NEW | 当前如果有事务,把当前事务挂起,新建事务 |
NOT_SUPPORTED | 当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则该事务挂起 |
NEVER | 当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常 |
NESTED | 当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与REQUIRED一样 |
4.5 事务的管理方式
-
编程式事务管理 :编程式事务管理是通过编写代码实现的事务管理。可以根据需求规定事务从哪里开始,到哪里结束,拥有很高的灵活性。但是这种方式,会使业务代码与事务规则高度耦合,难以维护,因此我们很少使用这种方式对事务进行管理。
-
声明式事务管理: 采用声明的方式来处理事务,可以通过 2 种方式实现,分别是 XML和注解方式。Spring 在进行声明式事务管理时,底层使用了 AOP 。事务管理不侵入开发的组件。
5. 入门案例----转账
5.1 需求分析
A账户减钱,B账户加钱
步骤:
①:数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)
②:业务层提供转账操作(transfer),调用减钱与加钱的操作
③:提供2个账号和操作金额执行转账操作
④:基于Spring整合MyBatis环境搭建上述操作
5.2 环境搭建
步骤1:准备数据库表
创建数据库gcxy_teach,并按以下sql创建表格
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `account`
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(11) COLLATE utf8mb4_general_ci NOT NULL,
`money` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', 'messi', '1000.00');
INSERT INTO `account` VALUES ('2', 'pep', '1000.00');
步骤2:创建项目导入jar包
项目的pom.xml添加相关依赖spring相关spring-context,spring-jdbc,spring-test,测试junit,数据库相关mysql-connector-java,druid,mybatis,mybatis-spring
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
</dependencies>
步骤3:根据表创建模型类(实体类)
package com.cqgcxy.entity;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
步骤4:创建Dao接口和实现类
package com.cqgcxy.dao;
public interface AccountDao {
void inMoney(String name,Double money);
void outMoney(String name,Double money);
}
package com.cqgcxy.dao.impl;
import com.cqgcxy.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 jt;
public void inMoney(String name, Double money) {
jt.update("update account set money = money + ? where name = ?",money,name);
}
public void outMoney(String name, Double money) {
jt.update("update account set money = money - ? where name = ?",money,name);
}
}
步骤5:创建Service接口和实现类
package com.cqgcxy.service;
public interface AccountService {
/**
* 转账
* @param out 转出
* @param in 转进
* @param money 金额
*/
public void transfer(String out,String in,Double money);
}
package com.cqgcxy.service.impl;
import com.cqgcxy.dao.AccountDao;
import com.cqgcxy.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public void transfer(String out, String in, Double money) {
accountDao.outMoney(out, money);
int i=3/0;
accountDao.inMoney(in, money);
}
}
步骤6:添加jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///gcxy_teach?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
步骤7:创建JdbcConfig配置类
package com.cqgcxy.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//配置事务管理器,mybatis使用的是jdbc事务
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
步骤8:创建Spring配置类SpringConfiguration(注解方式)
package com.cqgcxy.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@ComponentScan("com.cqgcxy")
@Import({JdbcConfig.class})
//启动平台事务管理
@EnableTransactionManagement
public class SpringConfiguration {
}
步骤9:编写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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" 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
https://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.cqgcxy"/>
<context:property-placeholder location="classpath*:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置通知 事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置通知的AOP织入-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.cqgcxy.service.impl.*.*(..))"></aop:advisor>
</aop:config>
</beans>
步骤10:测试代码
package com.cqgcxy.service.impl;
import com.cqgcxy.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 static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
//1.xml配置文件方式
@ContextConfiguration("classpath:applicationContext.xml")
//2.注解配置类方式
//@ContextConfiguration(classes = {SpringConfiguration.class})
public class AccountServiceImplTest {
@Autowired
private AccountService accountService;
@Test
public void transfer() {
accountService.transfer("messi","pep",200.0);
}
}
运行结果如图: