一、AOP面向切面编程
1、AOP的概念
AOP (AspectOrientedProgramming),意为:面向切面编程 ,通过 预编译方式 和运行期间动态代理 实现程序功能的统一维护的一种技术。 AOP是OOP的延续 ,是软件开发中的一个热点,是java开发中的一个重要内容。利用AOP可以对业务逻辑和非业务逻辑进行隔离,从而使得各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
用白话来讲 ,面向切面编程是面向对象编程的补充延续,其思想是将程序中的非业务代码(提交事务、打印日志、权限验证、统一异常处理 这四个使用场景)提取分离出来,在调用业务代码时通过代理对象帮助调用这些提取出来的非业务代码,这样在业务代码中就不用显示调用的非业务代码,做到了业务代码和非业务代码的分离,降低了耦合度
面向切面编程的好处就是: 降低业务耦合度 ,减少重复,专注业务;
核心原理: 使用动态代理的方式在执行方法前后或者出现异常的时候做加入相关的逻辑
AOP编程思想是Java中的而不是spring专有的,是spring使用aop这一编程思想
AOP的基本概念
连接点(Joinpoint):类中可以被增强的方法,这个方法就被称为连接点
切入点(pointcut):类中有很多方法可以被增强,但实际中只有add和update 被增了,那么add和update方法就被称为切入点(实际实现的连接点)
通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通知分为方法执行前通知,方法执行后通知,环绕通知等.
目标(Target): 代理的目标对象(连接点,切入点所在类)
代理(Proxy): 向目标对象应用通知时创建的代理对象
2、AOP的实现
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一, 可以完成面向切面编程。
(1)导入jar包
AspectJ 是一个基于Java语言的AOP框架,它提供了强大的AOP功能,且其实 现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又 将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.2.RELEASE</version> </dependency>
(2)配置
1、基于注解方式的实现 启动AspectJ支持开启自动代理注解(在spring全局配置文件中):
<aop:aspectj-autoproxy/>
AspectJ中常用的通知有五种类型: 前置通知,后置通知,环绕通知,异常通知,返回通知.
@Before前置通知:方法执行之前
@After后置通知:方法执行之后,无论是否出现异常
@AfterReturnning返回通知:方法成功执行之后通知,出现异常不执行
@AfterThrowing异常通知:抛出异常之后通知
@Around环绕通知:方法执行前后都有通知
@Before 前置通知 在业务方法执行之前执行 @After 后置通知 在业务执行完之后执行通知 即使业务代码中出现了异常也会执行 @AfterReturning 返回通知 在业务执行完之后执行通知 当业务代码出现异常便不再执行 @AfterThrowing 异常通知 当业务代码中出现异常时执行通知
// @Before("execution(* com.wbc.SpringPro.dao.AdminDao.*(..))") // @After("execution(* com.wbc.SpringPro.dao.AdminDao.*(..))") // @AfterReturning("execution(* com.wbc.SpringPro.dao.AdminDao.*(..))") // @AfterThrowing("execution(* com.wbc.SpringPro.dao.AdminDao.*(..))") // @AfterThrowing(value = "execution(* com.wbc.SpringPro.dao.AdminDao.*(..))",throwing = "throwable")//告知异常是什么
环绕通知---一节更比四节强(一个通知可以完成四个通知的功能)
@Around(value = "execution(* com.wbc.SpringPro.dao.AdminDao.*(..))") public void around(ProceedingJoinPoint proceedingJoinPoint){ //proceedingJoinPoint表示增强的方法 spring封装过的反射机制中的method //通过反射机制中 getMethod()方法拿到 System.out.println("前置通知"); try { //拿到所有参数 相当于反射机制中getFields()方法 Object[] objects = proceedingJoinPoint.getArgs(); //调用目标业务方法 相当于反射机制中的invoke()方法 Object object = proceedingJoinPoint.proceed(); System.out.println("返回通知"); } catch (Throwable e) { e.printStackTrace(); System.out.println("异常通知"); } System.out.println("后置通知"); }
2、在通知所在的类上方添加标签,在通知上方添加标签
在通知所在的类上方添加@Aspect标签,用于定义切面
通知上方添加标签定义通知的类型
第一个*表示任意类型返回值
第二个*表示任意的方法
..表示任意参数
3.1、通过代理对象调用方法
java
package com.wbc.SpringPro.test;
import com.wbc.SpringPro.service.AdminService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test3 {
public static void main(String[] args) {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
AdminService adminService = (AdminService) applicationContext.getBean("adminService", AdminService.class);
adminService.insertAmin();
adminService.updateAmin();
adminService.deleteAmin();
}
}
这里可以看到,生成的是代理对象而不是AdminDao类的对象
3.2通过代理对象调用方法测试around环绕通知
在AdminDao中模拟一个异常
java
package com.wbc.SpringPro.dao;
import com.wbc.SpringPro.model.Admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository(value = "adminDao")
public class AdminDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void insert(){
System.out.println("新增");
System.out.println(10/0);
}
public void update(){
System.out.println("修改");
}
public void delete(){
System.out.println("删除");
}
}
测试
java
package com.wbc.SpringPro.test;
import com.wbc.SpringPro.service.AdminService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test3 {
public static void main(String[] args) {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
AdminService adminService = (AdminService) applicationContext.getBean("adminService", AdminService.class);
adminService.insertAmin();
adminService.updateAmin();
adminService.deleteAmin();
}
}
结果
二、spring事物管理
1、spring事物管理的定义
事物管理本质上是数据库提供的一种管理机制。
数据库事物管理是对一次数据操作 中的多条sql语句 进行统一管理,确保在一次操作过程中,要么都执行要么都不执行,从而保证数据一致性。
例如转账这一操作中,sql1从A账户中减少5百,sql2从B账户中增加500,事物管理可以保证两条sql都执行或都不执行,保障数据的一致性
spring事务管理 是spring框架对事物提交这一功能进行的封装 。程序员在业务开发中不需要显示的提交事务
2、spring事物管理有两种方式
(1)编程式事务管理
需要程序员在代码中自己控制事务提交和回滚,现在在项目中很少使用,
(2)声明式事务管理
声明式事物在底层使用了AOP思想,可以为方法添加事物功能,他的控制是方法级别的
在spring框架中,提交事物管理实现类是DataSourceTransactionManager,基于注解实现的spring事务管理
1、配置
将事务管理器添加到spring全局配置文件xml
<!-- 配置spring 事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druidDataSource"></property> </bean>
开启注解事务管理器
<!-- 开启注解事务管理--> <tx:annotation-driven transaction-manager="transactionManager"/>
2、使用注解进行事务管理
在类或方法上使用@Transactional标签即可
java
package com.wbc.SpringPro.dao;
import com.wbc.SpringPro.model.Admin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository(value = "adminDao")
public class AdminDao {
@Autowired
JdbcTemplate jdbcTemplate;
/*
连接点:类中可以被增强(加功能:提交事物,打印日志,统一异常处理,权限验证)的方法就被称为连接点
切入点:类中实际被增强的方法
目标:代理的目标类(连接点和切入点所在的类)、
代理: 向目标对象应用通知时创建的代理对象
*/
@Transactional
public void insert(){
System.out.println("新增");
jdbcTemplate.update("insert into admin(account,password,gender) values(?,?,?)","张四","111","女");
//模拟出异常
System.out.println(10/0);
jdbcTemplate.update("insert into admin(account,password,gender) values(?,?,?)","张五","111","女");
}
public void update(){
System.out.println("修改");
}
public void delete(){
System.out.println("删除");
}
}
执行后,第一个sql执行后由于有异常第二个sql无法执行,事物不提交
补充:
- @Transactional事务管理标签一般添加到service业务逻辑层上,在service层一般会调用多个dao层方法,@Transactional添加到service后,当在service层调用时中途遇到异常时,也可以通过事物管理进行回滚事物,防止事物的不一致性
2)在学习期间推荐将@Transactional添加到类名上,这样可以为整个类添加事务管理
3、声明式事务不生效的场景
我们在adminDao中写下两个方法
1)@Transactional 应用在非 public 修饰的方法上
由于Spring事物管理也运用了反射机制,底层使用的是获取公共成员方法的方法,导致事务管理无法添加到封装的方法中,进而导致声明式事物不生效
没有进行回滚仍然将张6添加到了sql数据库
2)异常被catch捕获导致失效
当方法中的异常被捕获,spring事物管理会认为方法没有出现异常,从而错误提交事物
仍然会将数据添加到数据库,但由于异常的数据可能会在下面的sql中用到,存入的sql就有可能是错误的数据
3)出现编译期异常
当方法中出现了编译期异常,仍然回提交事物。
我们可以通过在标签@Transactional添加(rollbackFor = Exception.class)属性进行控制,当出现编译期异常时,进行回滚@Transactional(rollbackFor = Exception.class)
4)数据库引擎不支持事务
mySql中只有innodb支持事物,当使用其他引擎时,事务不生效