Spring框架学习笔记(三)
九、AOP
Spring是IoC(DI)和AOP的容器框架,IoC是Spring的核心思想。
AOP(Aspect Oriented Programming
):面向切面编程,是对面向对象编程的补充(面向接口编程、面向函数编程)
AOP的理解:在不改变原来代码的基础上,增加新的功能,底层是通过Java的动态代理实现(Java动态代理或者CGLIB动态代理)
IoC的理解:让别人为我们服务(Bean的实例化和初始化-依赖关系)
9.1 AOP的注解配置
(1) 新建计算器核心功能(模拟:不能在改动核心代码)
package com.yue.core;
import org.springframework.stereotype.Component;
@Component//实例化该对象,模拟核心代码,不能改变
public class Calculator {
public double div(double a,double b ){
return a/b;
}
public float mul(long a , int b){
return a*b;
}
public float mul(int a , int b){
return a*b;
}
public long add(int a , int b){
return a+b;
}
public int sub(int a,int b){
return a-b;
}
}
(2) 建立一个普通的Java类写增强代码(面向切面编程),使用Spring封装好的动态代理的工具
@Aspect//此类是一个切面
- 引入aspects第三方jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
- 切面类
package com.yue.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect//此类是一个切面
public class Aspect01 {
}
(3)织入增强代码的位置,当执行哪个类中的哪个方法的时候,将增强的代码织入
@Before(value = "execution(修饰符 返回值类型 执行的类全路径 . 该类某一方法(告知传递参数的个数与类型))")
其中,value和修饰符可以省略
package com.yue.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect//此类是一个切面
public class Aspect01 {
@Before( "execution(float com.yue.core.Calculator.mul(long,int) )")//可以省略“value=”
public void m1(){
System.out.println("前置增强=====织入位置:代码执行之前,无论代码是否正确,都会执行");
}
}
(4)启动AOP的代理(注解)
package com.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Configuration
@ComponentScan("com.yue")
/*
proxyTargetClass(默认值为false):
false的说明:
如果你的类不是实现类,那么默认启动CGLIB
如果你的类有实现类,那么就是默认启动Java动态代理
true:CGLIB
*/
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
(5)测试
package com.yue.test;
import com.config.AopConfig;
import com.yue.core.Calculator;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AOPTest {
private Calculator calculator;
@Before
public void init(){
//加载配置类
ApplicationContext ac = new AnnotationConfigApplicationContext(AopConfig.class);
//获取对象
calculator = ac.getBean(Calculator.class);
}
@Test
public void test01(){
float result01 = calculator.mul(10, 8);
System.out.println("result01 = " + result01);
}
@Test
public void test02(){
float result02 = calculator.mul(10L, 8);
System.out.println("result02 = " + result02);
}
}
运行结果:
当传输为两个int类型的时候,切面类类型未对应上,所以未触发增强代码
后者传入long和int 的时候,触发
9.2 增强类型和连接信息
JoinPoint
对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint
参数,就可以获取到封装了该方法信息的JoinPoint
对象.
- 增强类型
package com.yue.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect//此类是一个切面
public class Aspect01 {
@Before( "execution(double com.yue.core.Calculator.div(double ,double) )")//可以省略“value=”
public void m1(){
System.out.println("前置增强=====织入位置:代码执行之前,无论代码是否正确,都会执行");
}
@After("execution(double com.yue.core.Calculator.div(double ,double))")
public void m2(JoinPoint joinPoint){
System.out.println("后置增强,织入位置,代码执行之后,无论执行的代码是否正确,都是织入");
System.out.println("被代理类:"+joinPoint.getTarget().getClass().getName());
}
@AfterReturning(value = "execution(double com.yue.core.Calculator.div(double ,double))",returning = "result")
public void m3(JoinPoint joinPoint,Object result){
System.out.println("返回增强,织入位置,代码执行正确才会织入");
System.out.println("运行后的数据:"+result);
}
@AfterThrowing(value = "execution(double com.yue.core.Calculator.div(double ,double))",throwing = "ex")
public void m4(JoinPoint joinPoint,Exception ex){
System.out.println("异常增强,织入位置,代码执行错误才会织入");
System.out.println("异常信息 = " + ex.getMessage());
}
}
注意名称对应
结果:
还有一个环绕增强.
@Around("execution(double com.yue.core.Calculator.div(double ,double))")
public Object m5(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("前置增强.....");
Object result = -1;
try {
result = proceedingJoinPoint.proceed();
System.out.println("返回增强....");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常增强....");
}
System.out.println("后置增强....");
return result;
}
9.3 切入点定义
因为每次都要写execution
表达式,所以讲表达式提取出来,自定义切点@Pointcut
表达式中可以支持逻辑运算符
&&
,||
,!
@Component
@Aspect
public class Aspect02 {
@Pointcut("execution(double com.yue.core.Calculator.div(double ,double))")
public void pointcut01(){}
@Pointcut("execution(float com.yue.core.Calculator.mul(long ,int))")
public void pointcut02(){}
@Pointcut("execution(long com.yue.core.Calculator.add(int ,int)) || execution(int com.yue.core.Calculator.sub(int ,int))")
public void pointcut03(){}
@Pointcut("pointcut01()||pointcut02()||pointcut03()")
public void pointcut04(){}
//execution表达式
@Before("pointcut04()")
public void m1(JoinPoint joinPoint){//自动装配
System.out.println("前置增强,织入位置,代码执行之前,无论执行的代码是否正确,都是织入");
System.out.println("执行方法名称:"+joinPoint.getSignature().getName());
System.out.println("参数的数据 :" + Arrays.toString(joinPoint.getArgs()));
}
}
9.4 execution表达式
- 支持逻辑运算符(
||
、&&
、!
) - 支持匹配语法:
*
:匹配任意数量的字符..
:匹配任意数量重复的字符(可以匹配任意数量的子包,匹配方法中任意数量任意类型的参数)
定义切入点:
@Pointcut("execution(* com.yue..core.Calculator.*(..))")
public void pointcut05(){}
代码解释:返回值为任意类型,Calculator在com.yue开头,core结尾,中间是任意层次的该类下的任意方法,包含任意参数
- execution表达式
@Before("pointcut04()")
public void m1(JoinPoint joinPoint){//自动装配
System.out.println("前置增强,织入位置,代码执行之前,无论执行的代码是否正确,都是织入");
System.out.println("执行方法名称:"+joinPoint.getSignature().getName());
System.out.println("参数的数据 :" + Arrays.toString(joinPoint.getArgs()));
}
@After("pointcut05()")
public void m2(JoinPoint joinPoint){//自动装配
System.out.println("后置,织入位置,代码执行之前,无论执行的代码是否正确,都是织入");
}
9.5 注解方式
- 自定义注解
@Target
说明了Annotation
所修饰的对象范围:Annotation
可被用于packages
、types
(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation
类型的声明中使用了target
可更加明晰其修饰的目标。
@Retention
作用是定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中.
1.source
:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
2.class
:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
3.runtime
:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
package com.yue.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
String value() default "未知";
}
@Before("@annotation(com.yue.annotation.MyLog)")
public void m3(JoinPoint joinPoint){
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();//获取目标对象方法
MyLog myLog = method.getDeclaredAnnotation(MyLog.class);//获取注解
if(myLog!=null){
System.out.println("myLog = " + myLog.value());
}
}
9.6 AOP术语
- 连接点:增强代码织入的点,类中哪些方法被增强(不用
PonitCut
定义,直接在@Before
等注解里直接写execution
语句,后面跟的类路径就是连接点) - 切入点(PonitCut):每个程序类中都有很多的连接点,多个连接点就组成了切入点
- 增强类型(Advice):增强代码织入目标连接点上的代码(前置、后置、返回、异常、环绕)
- 目标对象(Target):增强逻辑的织入的目标类(被代理类)
- 代理对象(Proxy):一个类被AOP织入增强后,产生的一个结果类(子类、实现类)动态代理
- 织入(Weaving):织入就是将增强代码添加到目标类具体连接点的过程
十、事务管理
10.1 案例引入
示例:项目经理跟某个书店的王店长进行软件方面业务洽谈,王店长希望解决书店面临的问题,项目经理会根据店长提出各种需求形成一个需求文档。
假设需求如下:
该书店是会员制管理,每次只能购买一本书并且只能使用会员卡进行购买(会员表、书信息表、书库存表)
项目经理根据需求,设计了表结构,考虑业务方面:购买一本书业务需要几个步骤?
- 通过书的标识(主键-ISBN),获取书的价格
- 更新会员卡中的余额(用户余额-书的价格)
- 更新书的库存数量(库存数量-1)
这三个步骤构成了这一本书的业务
10.2 详细设计
(1)设计数据库中的表结构
(2)新建项目,导入依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--连接池数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
(3)配置类
package config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration//配置类=beans.xml配置文件
@ComponentScan("com.book")
public class SpringConfig {
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring_tx");
druidDataSource.setUsername("root");
druidDataSource.setPassword("");
return druidDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
(4)数据访问层代码:持久层代码
@Repository
:用在持久层的接口上,将接口的一个实现类交给spring管理。和@Controller、@Service、@Component的作用差不多
package com.book.dao.impl;
import com.book.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int getBookPrice(String isbn) {
String sql = "SELECT price FROM tx_book WHERE isbn=?";
return this.jdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Override
public void updateUserBalance(String account, String bookPrince) {
String sql = "UPDATE tx_user SET balance = balance-? WHERE account=?";
this.jdbcTemplate.update(sql,bookPrince,account);
}
@Override
public void updateStock(String isbn) {
String sql = "UPDATE tx_book_stock SET stock = stock-1 WHERE isbn = ?";
this.jdbcTemplate.update(sql,isbn);
}
}
(5)业务逻辑层代码:一个业务是由多个动作组成
package com.book.dao.service.impl;
import com.book.dao.BookDao;
import com.book.dao.service.BookOneService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookOneServiceImpl implements BookOneService {
@Autowired
private BookDao bookDao;
@Override
public void buyOneBook(String account, int isbn) {
//通过书的标识(主键-ISBN),获取书的价格
int bookPrice = this.bookDao.getBookPrice(isbn);
//更新会员卡中的余额(用户余额-书的价格)
this.bookDao.updateUserBalance(account,bookPrice);
//更新书的库存数量(库存数量-1)
this.bookDao.updateStock(isbn);
}
}
(6)测试,查看代码是否运行成功,代码是否合理
package com.yue.test;
import com.book.dao.service.BookOneService;
import config.SpringConfig;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class BookTest {
private BookOneService bookOneService;
@Before
public void init(){
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
bookOneService = ac.getBean(BookOneService.class);
}
@Test
public void test01(){
bookOneService.buyOneBook("bajie", 1002);
}
}
问题:
当资金小于0还能继续购买
当库存为0依旧可以购买
不合理
(7)经过测试,代码没有通过,重构代码,重新分析情况:
- 通过书的标识(主键-ISBN),获取书的价格
- 判断用户余额是否买书购书的需求
- 更新会员卡中的余额(用户余额-书的价格)
- 判断书库存是否满足
- 更新书的库存数量(库存数量-1)
package com.book.dao.service.impl;
import com.book.dao.BookDao;
import com.book.dao.service.BookOneService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookOneServiceImpl implements BookOneService {
@Autowired
private BookDao bookDao;
@Override
public void buyOneBook(String account, int isbn) {
//通过书的标识(主键-ISBN),获取书的价格
int bookPrice = this.bookDao.getBookPrice(isbn);
//判断用户余额是否满足购书需求
int userBalance = this.bookDao.getUserBalance(account);
if (userBalance<bookPrice){
throw new RuntimeException("用户余额不足,请尽快充值!");
}
//更新会员卡中的余额(用户余额-书的价格)
this.bookDao.updateUserBalance(account,bookPrice);
//判断书库存是否满足
int bookStock = this.bookDao.getStock(isbn);
if (bookStock==0){
throw new RuntimeException("书库存不足,请等待");
}
//更新书的库存数量(库存数量-1)
this.bookDao.updateStock(isbn);
}
}
经过测试,发现问题:库存不足的时候,是抛出异常,但是由于之前有数据的变更操作并且JDBC是自动提交事务,所以用户余额发生改变,这个是不合理。
10.3 事务、事务管理和事务管理器
什么事务?一个业务功能由多个动作组成,一个动作错误,那么之前所有的动作都不应该起作用(回滚
)
**数据库事务:**数据库管理系统,在运行过程中执行一个逻辑单元(一次执行多条SQL语句),由于一个有序的数据库操作集合,当其中某条SQL语句运行错误的时候,之前涉及变更数据库数据的操作都应进行“回滚”操作
面试题:事务4特性:ACID
- 原子性(
Atomicity
):事务作为一个整体被执行,要么全部执行成功
,要么全部都不执行成功
- 一致性(
Consistency
):当数据从一个一致性状态转换为另一个一致性状态的过程中,数据库中的数据应该满足完整性的约束 - 隔离性(
Isolation
):当出现多个事务并发执行的时候,一个事务的执行不应该影响其他事务的执行(但是,不是并发状态,一个事务可能会影响另一个事务,涉及事务的传播性) - 持久性(
Durability
):已经被提交的时候,对数据的变更操作,应该永久性的保存到数据库当中
**什么是事务管理?**事务管理顾名思义就是管理事务,为了保证数据的完整性和一致性。(理解:理论,接口)
Spring框架实现事务管理,称为事务管理器,Spring从不同的事务管理的API(数据库、JDBC)中抽取一条完整的机制,开发人员不用再了解底层的事务API,直接利用Spring提供的事务机制,就可以轻松完成对事务的管理。
Spring提供事务管理器中的核心接口Interface PlatformTransitionManager
,需要实现该接口(具体事务管理器),无论使用哪种事务管理器策略(编程式Annotation和声明式XML),实现一套独立的方法。
DataSourceTransitionManager
:只能处理一个数据源,并且底层是通过JDBC的方式操作数据库(JdbcTemplate或者MyBatis底层都是用它做的)HibernateTransitionManager
:专门用于Hibernate持久层框架(ORM框架)事务管理器JtaTransitionManager
:分布式事物管理器
10.4 点石成金(普通方法变事务方法)
将一个普通方法变成事务方法,该事务方法就会被事务管理器进行管理,从而保证数据的完整性和一致性(数据源有关)。
(1)启动事务注解
@Configuration//配置类=beans.xml配置文件
@ComponentScan("com.book")//扫描包
@EnableTransactionManagement(proxyTargetClass = true)//启动事务注解
public class SpringConfig {
(2)创建事务管理器,管理数据的完整性和一致性
/*创建事务管理器*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
(3)点石成金
@Autowired
private BookDao bookDao;
@Transactional//(transactionManager = "transactionManager")===>默认值
@Override
public void buyOneBook(String account, int isbn) {
这两个值对应,当为默认值时候,可以省略不写
@Transactional
的部分源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
可以写在类或方法上
经测试,发生回滚
10.5 事务的传播性
假设书店店长增加了需求,现在我们书店可以一次性购买多本书,底层核心业务还是一本一本的购买一起结算(类似于超市)
当一个事务方法调用另外事务方法的时候,检查被被调用事务方法的传播性
Propagation.REQUIRED
:默认值,如果有事务在运行,当前方法就会在这个事务内运行,否则,就启动一个新的事务并且在自己的事务内运行
Propagation.REQUIRES_NEW
:当前方法必须开启新的事务并且在自己的事务内运行,如果有其他事务在运行,那么其他事务需要被挂起
。
10.6 事务注解的属性
@Transactional(readOnly = true)
:默认值为false
.该事务注解标准的方法内部只能执行查询操作,无法执行修改数据的操作
否则就会报错:Connection is read-only. Queries leading to data modification are not allowed
@Transactional
源码:
public @interface Transactional {
//设置事务管理器
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
//设置事务传播性
Propagation propagation() default Propagation.REQUIRED;
//设置事务的隔离级别
Isolation isolation() default Isolation.DEFAULT;
//设置事务超时(-1意思是永远不超时)
int timeout() default -1;
//设置只读事务
boolean readOnly() default false;
//以下两种设置回滚异常
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
//以下两种设置不回滚
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
isolation
设置事务的隔离级别(需要记忆)
@Transactional(isolation = Isolation.DEFAULT)
:使用底层数据库的默认隔离级别READ_COMMITTED
@Transactional(isolation = Isolation.READ_COMMITTED)
:只是运行事务读取已经被事务提交的变更的数据,可以避免脏读,但是不能避免重复读和幻读的问题会依然存在@Transactional(isolation = Isolation.READ_UNCOMMITTED)
:运行事务可以读取未被其他事务提交的变更的数据,容易造成脏读、重复读和幻读的问题@Transactional(isolation = Isolation.REPEATABLE_READ)
:确保事务可以多次从一个字段中读取相同的值,在事务运行期间,禁止对该字段进行变更操作,可以避免脏读和重复读的问题,但是幻读的问题会依然存在@Transactional(isolation = Isolation.SERIALIZABLE)
(最安全,但是对性能消耗太大,一般用默认):确保事务可以从一个表中读取相同的行记录,并且在事务运行期间,禁止对行进行变更操作,所有并发问题都可以避免,但是性能消耗巨大,不推荐使用。
10.7 AOP+事务管理简化操作
编程式事务,需要在每个方法或者每个类上加入@Transactional
才能起作用,代码量相对来说比较多。所以简化操作:
(1)AOP的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
(2)启动AOP的支持
@Configuration
@ComponentScan("com.book")
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableTransactionManagement(proxyTargetClass = true)
public class SpringConfig {
(3)创建事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
(4)告知哪些方法被事务管理器拦截管理:设置方法约定
@Bean
//对事务管理器进行管理
public TransactionInterceptor txMethodAdvice(PlatformTransactionManager transactionManager){
NameMatchTransactionAttributeSource attributeSource = new NameMatchTransactionAttributeSource();
//设置规则:只读事务,不错更新操作
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
readOnlyTx.setReadOnly(true);
//设置事务传播性
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//默认值
//设置规则:当事务存在就在该事务运行,如果不存在创建新的事务,默认值(变更操作)
RuleBasedTransactionAttribute requriedTx = new RuleBasedTransactionAttribute();
//设置回滚规则
requriedTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));//默认值RuntimeException
requriedTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//设置哪些方法的管理规则
Map<String, TransactionAttribute> methodMap = new HashMap<>();
methodMap.put("get*",readOnlyTx);
methodMap.put("load*",readOnlyTx);
methodMap.put("find*",readOnlyTx);
methodMap.put("list*",readOnlyTx);
methodMap.put("query*",readOnlyTx);
methodMap.put("select*",readOnlyTx);
methodMap.put("check*",readOnlyTx);
methodMap.put("valid*",readOnlyTx);
methodMap.put("login*",readOnlyTx);
methodMap.put("*",requriedTx);
//对规则管理
attributeSource.setNameMap(methodMap);
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
//拦截指定方法
transactionInterceptor.setTransactionAttributeSource(attributeSource);
//这些方法被transactionManager管理器管理
transactionInterceptor.setTransactionManager(transactionManager);
return transactionInterceptor;
}
(5)AOP设置哪个层次下的方法被事务管理器管理
@Bean
public Advisor serviceAdvisor(TransactionInterceptor txMethodAdvice){
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
//拦截service层以Service结尾的任意参数的任意方法
pointcut.setExpression("execution(* com.book..service.*Service.*(..))");
return new DefaultPointcutAdvisor(pointcut,txMethodAdvice);
}
附录:
package config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.*;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Configuration
@ComponentScan("com.book")
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableTransactionManagement(proxyTargetClass = true)
public class SpringConfig {
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring_tx");
druidDataSource.setUsername("root");
druidDataSource.setPassword("");
return druidDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionInterceptor txMethodAdvice(PlatformTransactionManager transactionManager){
NameMatchTransactionAttributeSource attributeSource = new NameMatchTransactionAttributeSource();
//设置规则:只读事务,不错更新操作
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
readOnlyTx.setReadOnly(true);
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//默认值
//设置规则:当事务存在就在该事务运行,如果不存在创建新的事务,默认值(变更操作)
RuleBasedTransactionAttribute requriedTx = new RuleBasedTransactionAttribute();
requriedTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));//默认值RuntimeException
requriedTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//设置哪些方法的管理规则
Map<String, TransactionAttribute> methodMap = new HashMap<>();
methodMap.put("get*",readOnlyTx);
methodMap.put("load*",readOnlyTx);
methodMap.put("find*",readOnlyTx);
methodMap.put("list*",readOnlyTx);
methodMap.put("query*",readOnlyTx);
methodMap.put("select*",readOnlyTx);
methodMap.put("check*",readOnlyTx);
methodMap.put("valid*",readOnlyTx);
methodMap.put("login*",readOnlyTx);
methodMap.put("*",requriedTx);
attributeSource.setNameMap(methodMap);
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionAttributeSource(attributeSource);
transactionInterceptor.setTransactionManager(transactionManager);
return transactionInterceptor;
}
@Bean
public Advisor serviceAdvisor(TransactionInterceptor txMethodAdvice){
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.book..service.*Service.*(..))");
return new DefaultPointcutAdvisor(pointcut,txMethodAdvice);
}
}