Spring框架学习笔记(三)(AOP,事务管理)

九、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表达式

  1. 支持逻辑运算符(||&&!
  2. 支持匹配语法:
    • *:匹配任意数量的字符
    • ..:匹配任意数量重复的字符(可以匹配任意数量的子包,匹配方法中任意数量任意类型的参数)

定义切入点:

	@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可被用于 packagestypes(类、接口、枚举、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术语

  1. 连接点:增强代码织入的点,类中哪些方法被增强(不用PonitCut定义,直接在@Before等注解里直接写execution语句,后面跟的类路径就是连接点)
  2. 切入点(PonitCut):每个程序类中都有很多的连接点,多个连接点就组成了切入点
  3. 增强类型(Advice):增强代码织入目标连接点上的代码(前置、后置、返回、异常、环绕)
  4. 目标对象(Target):增强逻辑的织入的目标类(被代理类)
  5. 代理对象(Proxy):一个类被AOP织入增强后,产生的一个结果类(子类、实现类)动态代理
  6. 织入(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);
	}
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月色夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值