Spring AOP
AOP概念
Aop概念:面向切面编程,可以将业务逻辑进行隔离开。
基于动态代理对原有的方法进行增强。
(1)有接口 使用jdk的动态代理 创建接口实现类的代理对象。使用Proxy类的newProxyInstance()方法创建代理对象。
- 方法参数:
(1)类加载器
(2)增强方法所在实现类的接口,可以是多个
(3)实现接口InvocationHandler()
(2)没有接口 使用cglib动态代理创建类子类的代理对象。
Enhancer.create(class type,callback)
- 方法参数
(1)class类
(2)callback 实现接口 MethodInterceptor() 重写intercept方法
详情见:https://blog.youkuaiyun.com/lgb1997/article/details/109076744
AOP常用术语
(1)连接点:可增强的方法
(2)切入点:实际增强的方法
(3)切 面:就是将通知应用到切入点的过程
(4)通 知:增强逻辑
(5)织 入:将增强添加到目标的具体连接点上的过程 。
切面
@Before 前置通知
@After 表示在方法执行之后
@AfterReturning 方法返回值之后
@Around 环绕执行 在方法之前之后都执行
@AfterThrowing 异常通知,如果被增强的方法中出现异常才会执行
Spring 事务
事务概念
- 事务是数据库操作的最基本单元,逻辑上一组操作,要么都成功,要么都失败。
- spring中事务的四个特性(ACID)
(1)原子性:操作不可分割,要么都成功,要么都失败。
(2)一致性:操作之前 和操作之后的总体是一致的。
(3)隔离性:多人操作互不影响。
(4)持久性:操作后数据会在数据库中更改。
事务隔离级别
- 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
- 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
- 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
- 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
如果没有隔离性,在并发操作中会出现 脏读、不可重复读、幻读。
- 脏读:A事务读取到B事务尚未提交的数据,即当B事务发生错误回滚时,A事务读取的回滚之前的数据就是脏数据。
- 不可重复读:A事务两次读取同一份数据,在第二次读的时候,这时事务B修改了这份数据导致两次读取的数据不一致。
- 幻读:一个未提交的事务读取到另一个事务添加的数据。
事务传播行为
propagation | 特征 |
---|---|
Require | 如果当前没有事务,就新建一个事务。如果已经存在一个事务,就加入到这个事务中。 |
Require-new | 新建事务,如果当前存在事务,把当前事务挂起。 |
Supports | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
Not Supported | 以非事务方式执行操作。如果当前存在事务,把当前事务挂起。 |
Mandatory | 使用当前的事务,如果当前没有事务,则抛出异常。 |
Never | 以非事务方式执行操作。如果当前没有事务,抛出异常。 |
Nested | 如果当前存在事务,则在嵌套事务中执行。如果当前没有事务,则执行与Required相似的操作。 |
Demo
此处测试创建的是java项目,操作数据库使用的是jdbcTemplate。需要导的包
配置类
package com.pec.aop.config;
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.annotation.EnableTransactionManagement;
import com.alibaba.druid.pool.DruidDataSource;
@Configuration //声明配置类
@ComponentScan(basePackages = {"com.pec.aop"}) //开启扫描
@EnableAspectJAutoProxy //开启生成代理对象
@EnableTransactionManagement//开启事务
public class SpringConfig {
//配置数据库连接
@Bean
public DruidDataSource getDataSource() {
DruidDataSource dataSource=new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/jpa?serverTimezone=GMT%2B8&characterEncoding=utf-8");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("Luoguibin6!");
return dataSource;
}
//创建jdbc模板对象
@Bean
public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//配置事务管理器对象
@Bean
public DataSourceTransactionManager getTransactionManager(DruidDataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager=new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
实体类
package com.pec.aop.empty;
import java.io.Serializable;
public class Mobile implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer id;
private Integer no;
private String mobileName;
private double price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getMobileName() {
return mobileName;
}
public void setMobileName(String mobileName) {
this.mobileName = mobileName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
mapper
package com.pec.aop.mapper;
import com.pec.aop.empty.Mobile;
public interface AopMapper {
Integer update(Mobile mobile);
Integer updatePrice(Double price);
}
mapper实现类
package com.pec.aop.mapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.pec.aop.empty.Mobile;
import com.pec.aop.mapper.AopMapper;
@Repository
public class AopMapperImpl implements AopMapper {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public Integer update(Mobile mobile) {
String sql ="update mobile set price=?,mobile_name=?";
Object[] args= {mobile.getPrice(),mobile.getMobileName()};
int i=jdbcTemplate.update(sql,args);
return i;
}
@Override
public Integer updatePrice(Double price) {
String sql1="update mobile set price=?";
int a=10/0;
int j=jdbcTemplate.update(sql1, price);
return j;
}
}
service
package com.pec.aop.service;
import com.pec.aop.empty.Mobile;
public interface AopService {
Integer update(Mobile mobile,Double price);
}
service实现
package com.pec.aop.serviceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.pec.aop.empty.Mobile;
import com.pec.aop.mapper.AopMapper;
import com.pec.aop.service.AopService;
@Service
public class AopServiceImpl implements AopService{
@Autowired
AopMapper aopMapper;
@Override
@Transactional
public Integer update(Mobile mobile,Double price) {
Integer i=aopMapper.update(mobile);
aopMapper.updatePrice(price);
return i;
}
}
测试类
package test;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.pec.aop.config.SpringConfig;
import com.pec.aop.empty.Mobile;
import com.pec.aop.service.AopService;
public class AopTest {
@SuppressWarnings("resource")
@Test
public void add() {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
AopService aopService=context.getBean("aopServiceImpl",AopService.class);
Mobile mobile=new Mobile();
mobile.setMobileName("xiaomi");
mobile.setPrice(3000);
double price=2000;
aopService.update(mobile,price);
}
}
测试结果
执行完后控制台:
执行后表数据:
可以看到由于在实现类上加了@Transactional注解,mobile_name也没有变化。
@Transactional注解参数
参数名称 | 功能描述 |
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation | 该属性用于设置事务的传播行为,具体取值可参考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |