一、SpringAOP概述
什么事springaop? 面向切面编程应用场景:权限控制、事务管理、日志打印、性能统计
项目中什么地方使用AOP 代码重复
在不同的方法中,但是需要相同的操作。
AOP
关注点:重复代码
切面:抽取重复代码
切入点:拦截哪些方法
springaop方式:注释、xml
二、SpringAop注解方式
spring.xml中开启事务权限:
<aop:aspectj-autoproxy />
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!--<bean id="userService" class="cn.aop.service.UserService"></bean>--> <context:annotation-config /> <context:component-scan base-package="cn.aop"/> <aop:aspectj-autoproxy /> </beans>
pom.xml
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.2.3.RELEASE</spring.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> <!-- spring aop 包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <!-- @Transactional 事务注解包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <!-- JdbcTemplate --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
@Service public class UserService { public void add(){ //int i =1/0; System.out.println("UserService.add..."); } }
import cn.aop.service.UserService; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; /** * spring aop 通知有哪些? * 前置通知(方法前处理)、后置通知(方法后处理)、异常通知(方法发生异常时处理)、环绕通知(方法前后一起通知) * 使用注解方式 * -- @Aspect 定义切面 * -- @Component 注入spring容器 * Created by yz on 2018/03/18. */ @Aspect @Component public class AOP { /** * 前置通知 在add()方法前执行 */ @Before("execution(* cn.aop.service.UserService.add(..))") public void bean(){ System.out.println("...前置通知...在add()方法前执行"); } /** * 后置通知 在add()方法后执行 */ @After("execution(* cn.aop.service.UserService.add(..))") public void commit(){ System.out.println("...后置通知...在add()方法后执行"); } /** * 运行通知 在后置通知后执行 */ @AfterReturning("execution(* cn.aop.service.UserService.add(..))") public void afterRun(){ System.out.println("...运行通知...在后置通知后执行"); } /** * 异常通知 在add()方法发生异常后执行 */ @AfterThrowing("execution(* cn.aop.service.UserService.add(..))") public void afterThrowing(){ System.out.println("...异常通知...在add()方法发生异常后执行"); } /** * 环绕通知 在add()方法之前之后执行 */ @Around("execution(* cn.aop.service.UserService.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("我是环绕通知--前"); // 这个方法表示执行add()方法 proceedingJoinPoint.proceed(); System.out.println("我是环绕通知--后"); } public static void main(String[] args) { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) app.getBean("userService"); userService.add(); } }
三、SpringAopXML方式
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!--<bean id="userService" class="cn.aop.service.UserService"></bean>--> <context:annotation-config /> <context:component-scan base-package="cn.aop"/> <aop:aspectj-autoproxy /> <!-- 定义切面 --> <bean id="aop" class="cn.aop.AopForXml"/> <!-- Aop配置 --> <aop:config> <!-- 定义一个切入点表达式:拦截哪些方法 当前配置拦截UserService里所有的方法--> <aop:pointcut id="pt" expression="execution(* cn.aop.service.UserService.*(..))"/> <!-- 切面 --> <aop:aspect ref="aop"> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pt"/> <!-- 前置通知,在目标方法前执行 --> <aop:before method="before" pointcut-ref="pt"/> <!-- 后置通知,在目标方法后执行 --> <aop:after method="after" pointcut-ref="pt"/> <!-- 返回后通知也叫运行通知--> <aop:after-returning method="afterRun" pointcut-ref="pt"/> <!-- 异常通知--> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> </aop:aspect> </aop:config> </beans>
import cn.aop.service.UserService; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * spring aop 通知有哪些? * 前置通知(方法前处理)、后置通知(方法后处理)、异常通知(方法发生异常时处理)、环绕通知(方法前后一起通知) * 使用xml方式 * Created by yz on 2018/03/18. */ public class AopForXml { public void before(){ System.out.println("...前置通知...在add()方法前执行"); } public void after(){ System.out.println("...后置通知...在add()方法后执行"); } public void afterRun(){ System.out.println("...运行通知...在后置通知后执行"); } public void afterThrowing(){ System.out.println("...异常通知...在add()方法发生异常后执行"); } public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("我是环绕通知--前"); // 这个方法表示执行add()方法 proceedingJoinPoint.proceed(); System.out.println("我是环绕通知--后"); } public static void main(String[] args) { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) app.getBean("userService"); userService.add(); } }
四、Spring事物概述
什么是事务?
Spring事务
Spring传播行为,即一个方法里面又嵌套了一个方法,事务是怎么进行管理?
分布式事务
事务的目的:保证数据的一致性
事务的隔离级别:
原子性:要么全部成功,要么全部失败。
一致性:转账 A用户5000 B用户0 A给B转5000后 A用户0 B用户5000 A+B=5000
隔离性:多个事务之间互不影响,A事务,B事务互不关联
持久性:表示数据一旦到数据库之后,就不能再做其他操作了,比如保存到数据库就保存了。
Spring事务(面试题)
用spring的时候怎么去管理事务
class UserDao{
public void add(){
操作数据库
}
}
@Service
class UserServiceImpl implements IUserService{
private UserDao userDao;
public void add(){
userDao.add();
int i = 1/0; // 回滚
}
}
spring事务分类:
编程事务:手动事务
声明事务:xml和注解
手动事务 -- 自己去begin、commit
声明事务 -- xml 定义切入点 execution(* cn.aop.service.UserService.*(..)) 进行事务管理
注解方式 -- @Transactional
五、Spring事物环境搭建
编程事务(手动事务)
原理:获取到该数据源api,数据源api中,会自动封装手动begin、commit、rollback
/** * -- @Component 注入到spring 容器 * Created by yz on 2018/03/18. */ @Component public class TransactionUtils { private DataSourceTransactionManager dataSource; //开启事务 public TransactionStatus begin(){ TransactionStatus transaction = dataSource.getTransaction(new DefaultTransactionDefinition()); return transaction; } //提交事务 public void commit(TransactionStatus transaction){ dataSource.commit(transaction); } //回滚事务 public void rollback(TransactionStatus transaction){ dataSource.rollback(transaction); } }
/** * spring aop 通知有哪些? * 前置通知(方法前处理)、后置通知(方法后处理)、异常通知(方法发生异常时处理)、环绕通知(方法前后一起通知) * 使用xml方式 * Created by yz on 2018/03/18. */ public class AopForXml { private TransactionStatus begin ; @Autowired private TransactionUtils transactionUtils; @Transactional public void before(){ //System.out.println("...前置通知...在add()方法前执行"); } public void after(){ //System.out.println("...后置通知...在add()方法后执行"); } public void afterRun(){ //System.out.println("...运行通知...在后置通知后执行"); } /** * 发生异常回滚事务 */ public void afterThrowing(){ System.out.println("...异常通知...在add()方法发生异常后执行"); transactionUtils.rollback(begin); } /** * 环绕通知 * @param proceedingJoinPoint * @throws Throwable */ public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("我是环绕通知--前"); begin = transactionUtils.begin(); // 这个方法表示执行add()方法 proceedingJoinPoint.proceed(); System.out.println("我是环绕通知--后"); transactionUtils.commit(begin); } public static void main(String[] args) { ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) app.getBean("userService"); userService.add(); } }
/** * Created by yz on 2018/03/18. */ @Repository("userDao") public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void add(String name,Integer age){ String sql = "insert into users(name,age)values(?,?)"; int update = jdbcTemplate.update(sql, name, age); System.out.println("add:"+update); } }
/** * Created by yz on 2018/03/18. */ public interface IUserService { public void add(); }
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; /** * 手动事务 * 拦截方法,方法之前开启事务,方法之后提交事务,发生异常回滚事务 * Created by yz on 2018/03/18. */ public class UserServiceImpl implements IUserService{ @Autowired private UserDao userDao; @Autowired private TransactionUtils transactionUtils; public void add() { TransactionStatus begin = null; try { // 开启事务 begin = transactionUtils.begin(); userDao.add("小明",18); int i = 1/0; // 提交事务 , 如果发生异常,commit不执行,一百个添加相当于100个事务没被释放, // 一直占内存,需要手动回滚事务。 transactionUtils.commit(begin); } catch (Exception e) { e.printStackTrace(); // 发生异常事务回滚 transactionUtils.rollback(begin); } } /** * 使用AOP声明式事务 * 使用声明事务方式,方法一定不要try,将异常抛出去。或者在catch中throw 异常 * 不然在try中内部消化异常,AOP无法捕获异常,就无法执行回滚语句。 */ public void add2() { userDao.add("小明",18); int i = 1/0; } /** * 使用注解方式 * rollbackFor = Exception.class 这种设置是因为Spring的默认回滚RuntimeException, * 如果想要回滚Exception时,要设置@Transactional(rollbackFor = Exception.class) */ @Transactional(rollbackFor = Exception.class) public void add3() { userDao.add("小明",18); int i = 1/0; } }
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!--<bean id="userService" class="cn.aop.service.UserService"></bean>--> <context:annotation-config /> <context:component-scan base-package="cn.aop"/> <aop:aspectj-autoproxy /> <!-- 定义切面 --> <bean id="aop" class="cn.aop.AopForXml"/> <!-- Aop配置 --> <aop:config> <!-- 定义一个切入点表达式:拦截哪些方法 当前配置拦截UserService里所有的方法--> <aop:pointcut id="pt" expression="execution(* cn.aop.service.UserService.*(..))"/> <!-- 切面 --> <aop:aspect ref="aop"> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pt"/> <!-- 前置通知,在目标方法前执行 --> <aop:before method="before" pointcut-ref="pt"/> <!-- 后置通知,在目标方法后执行 --> <aop:after method="after" pointcut-ref="pt"/> <!-- 返回后通知也叫运行通知--> <aop:after-returning method="afterRun" pointcut-ref="pt"/> <!-- 异常通知--> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> </aop:aspect> </aop:config>
<import resource="classpath:db.xml"/></ beans >
db.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="123456" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 开启注解事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
六、Spring编程事物
事务开启后,一定要释放,提交释放,或者异常后手动回滚。七、事物底层原理分析
手动事务需要在每个方法之前之后手动去开启 提交 回滚,代码非常冗余。
使用AOP管理事务 -- 声明式事务(xml或注解)
声明事务原理剖析:
AOP编程 环绕通知 方法之前或者之后进行执行。
使用声明事务方式,方法一定不要try,将异常抛出去。
业务逻辑层不要使用try,将异常抛出给上一层。或者在catch中throw 异常。
不然在try中内部消化异常,AOP无法捕获异常,就无法执行回滚语句。
控制层进行捕获
事务原理:AOP编程+环绕通知+异常通知
环绕通知主要做begin和commit 异常通知主要做rollback
八、声明事物(XML与注解方式)
@Transactional public void add3() { userDao.add("小明",18); int i = 1/0; }
九、Spring事物传播行为
什么是事务传播行为?事务传递
事务传播行为分7种。
@Service public class LogService { @Autowired private JdbcTemplate jdbcTemplate; public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); } }
/** * 操作两张表,默认日志跟着一起回滚 */ @Transactional public void add3() { logService.addLog(); userDao.add("小明",18); int i = 1/0; }
事务默认传播行为:REQUIRED
1.@Transactional(propagation = Propagation.REQUIRED)表示支持当前事务,如果当前没有事务,就新建一个事务。logService.addLog()没有事务,但是在add3()事务内,所以一起回滚。
2.@Transactional(propagation = Propagation.REQUIRES_NEW) 表示新建一个事务,不用外面的事务,互不影响。addLog添加成功,add事务回滚。
@Transactional(propagation = Propagation.REQUIRES_NEW) public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); }
@Transactional(propagation = Propagation.REQUIRED) public void add() { logService.addLog(); userDao.add("明",18); int i = 1/0; }
3.@Transactional(propagation = Propagation.SUPPORTS) 表示支持当前事务,如果当前有事务,就用当前事务,如果当前没有事务,就以非事务方式执行。
@Transactional(propagation = Propagation.SUPPORTS) public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); }
@Transactional(propagation = Propagation.REQUIRED) // 当前有事务,按当前事务执行,addLog()会回滚 public void add() { logService.addLog(); userDao.add("明",18); int i = 1/0; }
public void add() { // 当前没有事务,按非事务方式执行,addLog()不会回滚 logService.addLog(); userDao.add("明",18); int i = 1/0; }
4.@Transactional(propagation = Propagation.NOT_SUPPORTED) 以非事务方式执行操作,如果当前有事务,就把当前事务挂起。addLog()不会回滚。
@Transactional(propagation = Propagation.NOT_SUPPORTED) public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); }
@Transactional(propagation = Propagation.REQUIRED) public void add() { logService.addLog(); userDao.add("明",18); int i = 1/0; }
5.@Transactional(propagation = Propagation.MANDATORY) 表示支持当前事务,如果当前没有事务,就抛异常。执行会抛异常。
@Transactional(propagation = Propagation.MANDATORY) public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); }
public void add() { logService.addLog(); userDao.add("明",18); int i = 1/0; }6.@Transactional(propagation = Propagation.NEVER) 以非事务方式执行,如果当前存在事务,就抛异常。执行抛异常。
@Transactional(propagation = Propagation.NEVER) public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); }
@Transactional(propagation = Propagation.REQUIRED) public void add() { logService.addLog(); userDao.add("明",18); int i = 1/0; }
7.@Transactional(propagation = Propagation.NESTED) 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。外层失败,addLog()会回滚。
@Transactional(propagation = Propagation.NESTED) public void addLog(){ jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')"); System.out.println("addLog添加完毕"); }
@Transactional(propagation = Propagation.REQUIRED) public void add() { logService.addLog(); userDao.add("明",18); int i = 1/0; }