1、AOP
1.1 什么是AOP
-
概念:面向切面编程,AOP是OOP的一个延伸
-
OOP:面向对象编程
-
三大特征:封装、继承、多态
-
特点:纵向抽取和继承体系
例如: Animal类:动物类 eat()吃饭 run()奔跑 Dog类:继承Animal类 Pig类:继承Animal类
-
-
AOP特点:横向切面编程
日志记录 权限校验 性能监控 事务
1.2 什么是横向逻辑
-
出现原因:多个纵向流程中出现的相同的子流程逻辑
-
存在问题:
1.横切逻辑往往在很多方法中,它是重复的 2.横切逻辑和业务代码混杂在一起,让业务代码变得很臃肿,业务代码应该只处理业务逻辑 3.OOP已经不能处理横切逻辑的问题了
-
解决方法
1.AOP独辟蹊径的提出了横向抽取机制,将业务逻辑和横切逻辑分离 2.难点:分离之后怎么把横切逻辑再融合到原有业务逻辑上,达到和原来一样的效果
1.3 AOP特性
1.3.1 AOP思想
面向切面编程:
1.要解决问题:在不改变业务逻辑基础上增强横切逻辑
2.解决过程:业务逻辑不能改变,只能面向横切逻辑
3.具体方式:增强横切逻辑
1.3.2 AOP的实现方式
- 动态代理技术
1.3.3 AOP的优势
- 作用:不改变业务逻辑的情况下进行方法增强
- 优势:减少重复代码,提高开发效率,业务逻辑和增强的横切逻辑分离便于维护
2、AOP的具体应用
案例:模拟转账(并且模拟转账异常)
汇款人账户减少一定的金额
收款人账户增加一定的金额
计算之后,更新数据库
问题:模拟转账异常(人为制造异常,在两次update之间造了异常)
问题:模拟转账异常(人为制造异常,在两次update之间造了异常)
2.1 转账编码
步骤:
2.1.1 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>lyq.com</groupId>
<artifactId>Spring_day03_01_transfer</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
2.1.2 持久层
@Repository
public class AccountDaoImpl implements AccountDao {
//依赖注入
@Autowired
private QueryRunner queryRunner;
@Override
public Account findByName(String name) {
String sql = "select * from account where name=?";
try {
return queryRunner.query(sql, new BeanHandler<Account>(Account.class),name);
} catch (SQLException e) {
throw new RuntimeException();
}
}
@Override
public void update(Account account) {
String sql = "update account set name=?,money=? where id=?";
try {
Object[] objects = {account.getName(), account.getMoney(), account.getId()};
queryRunner.update(sql, objects);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.1.3 业务逻辑层
@Service
public class AccountServiceImpl implements AccountService {
//依赖注入
@Autowired
private AccountDao accountDao;
//转账
public void transfer(String forName, String toName, Float money) {
try {
//开启事务
//获得转账人,收取人
Account inName = accountDao.findByName(forName);
Account outName = accountDao.findByName(toName);
//转账
inName.setMoney(inName.getMoney() - money);
outName.setMoney(outName.getMoney() + money);
//更新账户余额
accountDao.update(inName);
accountDao.update(outName);
//提交事务
} catch (Exception e) {
//回滚事务
e.printStackTrace();
}finally {
//释放资源
}
}
}
2.1.4 配置文件ApplicationContext.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描注解-->
<context:component-scan base-package="com"></context:component-scan>
<!--创建QueryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<!--依赖注入druid-->
<constructor-arg name="ds" ref="druidDataSource"></constructor-arg>
</bean>
<!--Druid数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!--赋值-->
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--读取数据文件-->
<context:property-placeholder location="druid.properties"></context:property-placeholder>
</beans>
2.1.5 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class AccountServiceImplTest {
//依赖注入
@Autowired
private AccountService accountService;
@Test
public void transfer() {
accountService.transfer("bbb", "aaa", 1F);
}
}
2.1.6 问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMLUkYNz-1582266849798)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571627852683.png)]
2.2 解决问题
2.2.1 引入工具类Connection.java
步骤:
1.创建线程对象
2.创建数据库对象
3.获取连接对象
- 获取线程中的连接对象
- 判断连接对象是否为空
- 如果为空,获取链接,并将连接存入到线程中
- 如果不为空,抛出异常
4.关闭资源
public class ConnectionUtils {
//创建线程对象
private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
//数据库
@Autowired
private DataSource dataSource;
//获取连接对象
public Connection getThreadConnection(){
//获取线程中的连接对象
Connection connection = threadLocal.get();
//判断连接对象是否为空
if (connection == null){
//从连接池中获取对象
try {
connection = dataSource.getConnection();
//将连接存入线程
threadLocal.set(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
//关闭资源
public void close(){
if (threadLocal.get() != null){
try {
//关闭连接,将连接归还给连接池
threadLocal.get().close();
//移除线程中绑定的对象
threadLocal.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2.2.2 引入事务管理工具类TransactionManager.java
步骤:
1.开启事务
2.提交事务
3.回滚事务
4.释放资源
@Component
public class TransactionManager {
//依赖注入
@Autowired
private ConnectionUtils connectionUtils;
//开启事务
public void beginTransaction(){
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事务
public void commit(){
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚事务
public void rollback(){
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//释放资源
public void release(){
try {
connectionUtils.getThreadConnection().setAutoCommit(true);
connectionUtils.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.2.4 持久层
@Repository
public class AccountDaoImpl implements AccountDao {
//依赖注入
@Autowired
private QueryRunner queryRunner;
@Autowired
private ConnectionUtils connectionUtils;
@Override
public Account findByName(String name) {
String sql = "select * from account where name=?";
try {
return queryRunner.query(connectionUtils.getThreadConnection(), sql, new BeanHandler<Account>(Account.class),name);
} catch (SQLException e) {
throw new RuntimeException();
}
}
@Override
public void update(Account account) {
String sql = "update account set name=?,money=? where id=?";
try {
Object[] objects = {account.getName(), account.getMoney(), account.getId()};
queryRunner.update(connectionUtils.getThreadConnection(), sql, objects);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.2.3 业务层
@Service
public class AccountServiceImpl implements AccountService {
//依赖注入
@Autowired
private AccountDao accountDao;
@Autowired
private TransactionManager transactionManager;
//转账
@Override
public void transfer(String forName, String toName, Float money) {
try {
//开启事务
transactionManager.beginTransaction();
//获得转账人,收取人
Account inName = accountDao.findByName(forName);
Account outName = accountDao.findByName(toName);
//转账
inName.setMoney(inName.getMoney() - money);
outName.setMoney(outName.getMoney() + money);
//更新账户余额
accountDao.update(inName);
int i = 1/0;
accountDao.update(outName);
//提交事务
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
//回滚事务
transactionManager.rollback();
}finally {
//释放资源
transactionManager.release();
}
}
}
2.2.4 测试结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQIJkMU5-1582266849800)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571630872177.png)]
2.2.5 问题
1.业务层代码臃肿
2.技术与业务整合,比较混乱
2.3 动态代理
2.3.1 jdk动态代理
-
三要素:真实对象(被代理对象)、代理对象、共同的接口
-
特点:
1.jdk基于接口的动态代理 2.java自带的代理 3.创建好的代理对象时接口的一个实现类
-
代理对象的创建:Proxy
创建代理对象: 参数1:类加载器 参数2:真实对象实现的所有接口 new Class(){Sale.class} 参数3:实现InvocationHandler接口的实现类对象 返回值, 代理对象 例如: SaleDao saleProxy = (SaleDao) Proxy.newProxyInstance( SaleDao.getClass().getClassLoader(), SaleDao.getClass().getInterfaces(), new InvocationHandler() { //代理执行, 要执行的方法 //proxy 代理对象, 没用 //method 真实要执行的方法 //args 真实要执行方法 所接收的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代码 }); //采用代理对象执行 saleProxy.saleProduct(100F);
1. pojo实现类
public class Product {
private String name;
private Float money;
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", money=" + money +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
}
2. 产品工厂
public class ProductFactory {
//制造工厂
public Product make(){
//创建商品对象
Product product = new Product();
product.setName("小米");
product.setMoney(2999F);
return product;
}
}
3. 销售接口
public interface SaleDao {
//销售商品
public void saleProduct(Float money);
}
4. 销售接口实现类
public class SaleDaoImpl implements SaleDao {
@Override
public void saleProduct(Float money) {
//创建工厂对象
ProductFactory productFactory = new ProductFactory();
//制造产品
Product product = productFactory.make();
//以指定价格卖出
product.setMoney(money);
System.out.println("product:" + product);
}
5. 测试类
public class SaleDaoImplTest {
//销售商品
@Test
public void test() {
SaleDao saleDao = new SaleDaoImpl();
saleDao.saleProduct(3999F);
}
//jdk动态代理,销售商品
@Test
public void test01() {
//真实对象
SaleDao saleDao = new SaleDaoImpl();
/*
* 创建代理对象
* 参数1:类加载器
* 参数2:真实对象实现的所有接口 new Class(){Sale.class}
* 参数3:实现InvocationHandler接口的实现类对象
* 返回值, 代理对象
*/
SaleDao saleProxy = (SaleDao) Proxy.newProxyInstance(
saleDao.getClass().getClassLoader(),
saleDao.getClass().getInterfaces(),
new InvocationHandler() {
/*
* 代理执行, 要执行的方法
* @param proxy 代理对象, 没用
* @param method 真实要执行的方法
* @param args 真实要执行方法 所接收的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//创建工厂对象
ProductFactory productFactory = new ProductFactory();
//生产工厂
Product product = productFactory.make();
//获取商品的成本价
Float money = product.getMoney();
System.out.println(money);
//获取指定销售价格
Float price = (Float) args[0];
System.out.println(price);
//两个价格对比
if (money > price) {
System.out.println("成本价高于销售价格,赔钱,不卖!!!");
return null;
} else {
System.out.println("赚了" + (price - money) + "元,可以卖!");
return product;
}
}
});
//采用代理对象执行
saleProxy.saleProduct(3999F);
}
}
2.3.2 cglib:基于类的动态代理
1.第三方的代理,需要引入依赖
2.创建好的代理对象时真实对象的子类
-
创建cglib代理对象
1. 创建增强对象 Enhancer enhancer = new Enhancer(); 2. 指定代理对象的父类(真实对象) enhancer.setSuperclass(真实对象.getClass()); 3. 指定方法拦截器(代理执行的方法) enhancer.setCallback(new MethodInterceptor(){ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //代码 } }); 4. 创建代理对象 enhancer.create();
1. 导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
2. 测试类
public class SaleDaoImplTest {
//cglib的动态代理
@Test
public void test() {
//创建真实对象
SaleDao saleDao = new SaleDaoImpl();
/*
cglib代理对象
1. 创建增强对象
2. 指定代理对象的父类(真实对象)
3. 指定方法拦截器(代理执行的方法)
4. 创建代理对象
*/
//创建增强对象
Enhancer enhancer = new Enhancer();
//指定代理对象的父类
enhancer.setSuperclass(saleDao.getClass());
//指定方法拦截器
enhancer.setCallback(new MethodInterceptor() {
/*
* 代理执行, 要执行的方法
* @param o 代理对象, 没用
* @param method 真实要执行的方法
* @param objects 真实要执行方法 所接收的参数
* @param MethodProxy 代理方法, 没用
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//创建工厂
ProductFactory productFactory = new ProductFactory();
//生产产品
Product product = productFactory.make();
//获取成本价
Float money = product.getMoney();
//获取销售价
Float price = (Float) objects[0];
//两个价格对比
if (money > price) {
System.out.println("成本价高于销售价格,赔钱,不卖!!!");
return null;
}else {
System.out.println("赚了"+(price -money) + "元,可以卖!");
return product;
}
}
});
//创建代理对象
SaleDaoImpl proxy = (SaleDaoImpl) enhancer.create();
proxy.saleProduct(3000F);
}
}
2.4 动态代理解决问题–业务代码与技术代码分离
业务层
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String forName, String toName, Float money) {
try {
//获取转账人对象、收取人对象
Account inAccount = accountDao.findByName(forName);
Account outAccount = accountDao.findByName(toName);
//转账
inAccount.setMoney(inAccount.getMoney() - money);
outAccount.setMoney(outAccount.getMoney() + money);
//更新账户信息
accountDao.update(inAccount);
accountDao.update(outAccount);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.4.1 jdk动态代理解决问题
@Test
public void transferJdk() {
//创建jdk动态代理对象
//参数:1.类加载器 2.接口中的所有方法 3.实现类对象
AccountService proxy = (AccountService) Proxy.newProxyInstance(
accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
//参数:1.动态代理对象 2.接口中的方法transfer() 3.方法中的参数的值
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//开启事务
transactionManager.beginTransaction();
//调用方法,编写业务代码,通过反射技术调用
//参数:1.接口对象 2. 接口中方法的transfer()中的参数
Object obj = method.invoke(accountService, args);
// 提交事务
transactionManager.commitTransaction();
return obj;
} catch (Exception e) {
//回滚事务
transactionManager.rollback();
e.printStackTrace();
}finally {
//关闭事务
transactionManager.release();
}
return null;
}
});
accountService.transfer("bbb", "aaa", 1F);
}
2.4.2 cglib动态代理解决问题
//cglib动态代理
@Test
public void transferCglib(){
//创建增强对象
Enhancer enhancer = new Enhancer();
//指定真实对象的父类--->AccountServiceImpl
enhancer.setSuperclass(accountService.getClass());
//指定方法拦截器,代理要执行的方法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o,
Method method,
Object[] objects,
MethodProxy methodProxy) throws Throwable {
try {
//开启事务
transactionManager.beginTransaction();
//调用方法,编写业务代码,通过反射技术调用
//参数:1.接口对象 2. 接口中方法的transfer()中的参数
Object obj = method.invoke(accountService, objects);
// 提交事务
transactionManager.commitTransaction();
return obj;
} catch (Exception e) {
e.printStackTrace();
//回滚事务
transactionManager.rollback();
}finally {
//关闭事务
transactionManager.release();
}
return null;
}
});
//创建代理对象,执行方法
AccountServiceImpl proxy = (AccountServiceImpl) enhancer.create();
proxy.transfer("aaa", "bbb", 1F);
}
2.5 Spring关于Jdk/Cglib动态代理的选择
- jdk动态代理------->涉及到接口
- cglib动态代理------->不涉及动态代理
3、Spring中AOP的使用
3.1 术语
3.1.1 连接点---->Joinpoint
1.横切程序执行的特定位置:比如类开始初始化前,类初始化之后,类中某个方法调用前、调用后,方法抛出异常后等,这些代码中的特定点就称为“连接点”。
2.Spring仅支持方法的连接点:即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。
3.AOP:可以理解为是一个黑客(因为它要向目前类中嵌入额外的代码逻辑),连接点就是AOP向目标类打入楔子的候选点。
3.1.2 切入点---->Pointcut
1.切点:从一个类中的多个方法中定位需要使用的方法
2.注意:切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息
比如:如果把一个方法理解成数据表中的一条记录的话,那么切入点就好比你select语句的where条件 ,就可以定位到你感兴趣的方法
3.1.3 通知/增强---->Advice
1.增强的第一层意思:横切逻辑代码(增强逻辑代码)
2.在Spring中,增强除了用于描述横切逻辑外,包含另一层意思就是横切逻辑执行的方位信息。刚刚说了切点只能定位到方法,在进一步使用方位信息就可以定位到我们感兴趣的连接点了(方法调用前、方法调用后还是方法抛出异常时等)。
3.1.4 目标对象---->Target
增强逻辑的织入目标类。比如未添加任何事务控制的AccountServiceImpl类
3.1.5 织入---->Weaving
1.织入是将增强逻辑/横切逻辑添加到目标类具体连接点上的过程,AOP像一台织布机,将目标类、增强 通过AOP(其实就是动态代理技术)编织(捆绑)到一起。
2.Spring采用动态代理织入。
Proxy(代理):一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。
3.1.6 切面---->Acpect
1.切面由切点和增强组成。
2.切面=切点+增强
3.切点+方位信息+横切逻辑
4.连接点+横切逻辑
5.最终切面完成:把横切逻辑织入到哪些方法的方法前/后等
6.本质:把横切逻辑增强到连接点(切点和方位信息都是为了确定连接点)上
3.2 基于XML的AOP配置
3.2.1 AOP配置文件
-
<aop:config>
:所有aop配置的根标签 -
<aop:aspect id="" ref="">
:配置切面对象id:自定义名称 ref:横切逻辑类的<bean>标签id值
-
<aop:pointcut id="" expression>
:配置切入点id:自定义值 expression:匹配方法的表达式
-
<aop:before method="" pointcut-ref="">
:被切入的方法前执行method:printBeforeMethod切入的方法名 pointcut-ref:切入点标签的id属性值
3.2.2 增强/通知
前置增强:连接点执行前
后置增强:连接点执行后
异常增强:连接带你抛出异常
返回增强:连接正常执行完毕,当要进行方法放回时执行;如果出现异常,则不执行
环绕增强:前置增强+后置增强+异常增强+返回增强
注意:异常增强与返回增强只能执行到其中一个
代码演示:
需求:在Service层代码的不同方法的不同连接点JoinPoint织入日志
- 把Account表的service层进行crud模拟(dao层就不需要了)
1. pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
2. pojo
public class Account {
private Integer id;
private String name;
private Float money;
}
3. 业务层
//接口
public interface AccountService {
//模拟保存账户
int saveAccount(Account account);
//模拟更新账户
int updateAccountById(Account account);
//模拟删除账户
int deleteAccountById(int id);
//模拟查询账户
Account queryAccountById(int id);
}
//实现类
@Service
public class AccountServiceImpl implements AccountService {
@Override
public int saveAccount(Account account) {
int i = 1/0;
System.out.println("模拟保存账户");
return 0;
}
@Override
public int updateAccountById(Account account) {
System.out.println("模拟更新账户");
return 0;
}
@Override
public int deleteAccountById(int id) {
System.out.println("模拟删除账户");
return 0;
}
@Override
public Account queryAccountById(int id) {
System.out.println("模拟查询账户");
return null;
}
}
4. 配置文件
<!--开启注解扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!--创建增强对象,配置横切逻辑-->
<bean id="log" class="com.utils.LogUtil"></bean>
<!--配置aop-->
<aop:config>
<!--切面配置-:把增强应用到目标对象,也就是切面-->
<aop:aspect id="logAspect" ref="log">
<!--配置切入点:定义哪些方法是连接点-->
<aop:pointcut id="point" expression="execution(* com.service.impl.*.*(..))"></aop:pointcut>
<!--开始织入-->
<!--执行前-->
<aop:before method="beforeMethod" pointcut-ref="point"></aop:before>
<!--执行后-->
<aop:after method="afterMethod" pointcut-ref="point"></aop:after>
<!--异常-->
<aop:after-throwing method="afterThrowing" pointcut-ref="point"></aop:after-throwing>
<!--正常-->
<aop:after-returning method="afterReturning" pointcut-ref="point"></aop:after-returning>
</aop:aspect>
</aop:config>
5. 横切逻辑
public class LogUtil {
//前置增强
public void beforeMethod(){
System.out.println("方法执行前");
}
//后置增强
public void afterMethod(){
System.out.println("方法执行后");
}
//异常增强
public void afterThrowing(){
System.out.println("方法异常执行时");
}
//返回增强
public void afterReturning(){
System.out.println("方法正常执行时");
}
}
6. 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class TestAop {
@Autowired
private AccountService accountService;
@Test
public void test(){
Account account = new Account();
accountService.saveAccount(account);
System.out.println("--------------");
accountService.queryAccountById(1);
}
}
7. 测试结果
异常增强
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kPsDAEw6-1582266849802)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571881291404.png)]
正常增强
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yETHi1MV-1582266849802)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1571881536379.png)]
3.3 环绕增强
-
什么是环绕增强:
它是spring为我们提供的一种可以在代码中手动控制增强方法何时执行的方式,灵活度比较高,设置可以控制原业务逻辑是否执行。
-
注意:环绕通知都是独立使用,不能和上面四种通知类型混合使用
3.3.1 环绕增强AOP的配置
-
<aop:around method="" pointcut-ref=""></aop:around>
:环绕通知配置method:printBeforeMethod切入的方法名 pointcut-ref:切入点标签的id属性值
3.3.2 XML的环绕增强配置
1. 配置文件:ApplicationContext.xml
<!--环绕增强-->
<aop:around method="around" pointcut-ref="point"></aop:around>
2. 横切逻辑
//环绕增强
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
try {
System.out.println("环绕增强前--------------");
//执行原有业务方法
result = proceedingJoinPoint.proceed();
System.out.println("环绕增强后--------------");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕增强执行后,出现异常的情况: 想做的增强内容");
}
return result;
}
3.4 基于AOP的注解配置
-
扫描注解:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
AOP的注解:
1.@Aspect:配置切面对象
1.@Before:前置增强 2.@After:后置增强 ------------------------------ 异常增强与返回增强只能执行到其中一个 ------------------------------ 3.@AfterThrowing:异常增强 4.@AfterReturning:返回增强 ------------------------------ 环绕增强独立使用 ------------------------------ 5.@Around:环绕增强
1.@Pointcut:配置切入点
3.4.1 基本配置
1. 配置文件
<!--扫描AOP注解--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2. 横切逻辑
@Component @Aspect public class LogUtil { //前置增强 @Before("execution(* com.service.impl.*.*(..))") public void beforeMethod(){ System.out.println("方法执行前"); } //后置增强 @After("execution(* com.service.impl.*.*(..))") public void afterMethod(){ System.out.println("方法执行后"); } //异常增强 @AfterThrowing("execution(* com.service.impl.*.*(..))") public void afterThrowing(){ System.out.println("方法异常执行时"); } //返回增强 @AfterReturning("execution(* com.service.impl.*.*(..))") public void afterReturning(){ System.out.println("方法正常执行时"); } }
3.5 AOP注解的横切逻辑对象抽取(配置全局的切点匹配)
-
注解:
@Pointcut("execution(* 包名.*.*(..))") public void Pointcut(){} @Around("Pointcut()")
1. 横切逻辑
@Component
@Aspect
public class LogUtil01 {
//配置全局的切点匹配, 设置匹配的表达式
@Pointcut("execution(* com.service.impl.*.*(..))")
public void Pointcut(){
}
//环绕增强
@Around("Pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
try {
System.out.println("环绕增强前--------------");
//执行原有业务方法
result = proceedingJoinPoint.proceed();
System.out.println("环绕增强后--------------");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕增强执行后,出现异常的情况: 想做的增强内容");
}
return result;
}
}
intln(“方法执行后”);
}
//异常增强
@AfterThrowing(“execution(* com.service.impl..(…))”)
public void afterThrowing(){
System.out.println(“方法异常执行时”);
}
//返回增强
@AfterReturning(“execution(* com.service.impl..(…))”)
public void afterReturning(){
System.out.println(“方法正常执行时”);
}
}
### 3.5 AOP注解的横切逻辑对象抽取(配置全局的切点匹配)
- 注解:
```properties
@Pointcut("execution(* 包名.*.*(..))")
public void Pointcut(){}
@Around("Pointcut()")
1. 横切逻辑
@Component
@Aspect
public class LogUtil01 {
//配置全局的切点匹配, 设置匹配的表达式
@Pointcut("execution(* com.service.impl.*.*(..))")
public void Pointcut(){
}
//环绕增强
@Around("Pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
try {
System.out.println("环绕增强前--------------");
//执行原有业务方法
result = proceedingJoinPoint.proceed();
System.out.println("环绕增强后--------------");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕增强执行后,出现异常的情况: 想做的增强内容");
}
return result;
}
}