《SpringAOP》
目录
一、代理模式
为其他对象提供一种代理,从而实现对这个对象的访问控制。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
设计模式中有一个设计原则是开闭原则,它是Java世界里最基础的设计原则,开闭原则明确要求软件应该实现对扩展开放,对修改关闭。
二、SpringAOP简介
AOP(面向切面编程)是对传统OOP(面向对象编程)一种补充和完善,OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。
AOP并不是Spring框架独有的,不同框架都有对AOP的支持,并各自有特点,所以这里需要再次强调一下,SpringAOP是一种基于方法拦截的AOP,即SpringAOP只支持对方法拦截。
三、SpringAOP使用
操作术语
- 横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点;
- 切面(aspect)
OOP中类是对物体特征的抽象,那么切面就是AOP中对横切关注点的抽象,切面通常是一个类,里面可以定义切入点和通知。
- 连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
- 切入点(pointcut)
对连接点进行拦截的定义,通过书写切入点表达式决定究竟要把哪些方法给拦截下来。
- 通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,一般来说就是对目标方法的增强,通知分为前置(在目标方法前执行)、后置(在目标方法后执行)、异常(在目标方法抛出异常后执行)、返回(在目标方法返回结果后执行)、环绕(可以同时实现前置和后置功能)通知五种类型。
execution正则表达式
execution( aop.TestService.ptrint(…))*
- execution:表示执行对应方法的时候会触发;
- *:代表任意类型返回类型的方法;
- aop.TestService:代表类的全限定名;
- ptrint:代表被拦截的方法名字;
- (…):代表任意的参数。
案例一
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- com.hpr.aop.AopTest
package com.hpr.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
//@Aspect表示当前类是一个切面
@Aspect
@Component
public class AopTest {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//切入点
@Pointcut("execution(* com.hpr.service.impl.TeacherService.insert(..))")
public void pointcut() {
}
//返回通知(返回结果后)
@AfterReturning(pointcut = "pointcut()")
public void afterReturning() {
File logDir = new File("./logs");
if (!logDir.exists()) {
logDir.mkdir();
}
try {
FileWriter fw = new FileWriter("./logs/insert.log", true);
PrintWriter pw = new PrintWriter(fw);
pw.println("【" + sdf.format(new Date()) + "】:teacher信息新增成功!");
pw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//前置通知(方法执行前)
// @Before("pointcut()")
// public void before() {
// }
//后置通知(方法执行后)
// @After("pointcut()")
// public void after() {
//
// }
//环绕通知
// @Around("pointcut()")
// public void around(ProceedingJoinPoint pj) {
// }
//异常通知(发生异常后)
// @AfterThrowing(pointcut = "pointcut()", throwing = "e")
// public void afterTrowing(Throwable e) {
// }
}
- 启动测试
执行结果
- /logs/insert.log
四、参数传递
使用SpringAOP时,想要获取所拦截方法传入的参数及返回的结果,可进行以下操作。
- 修改切面
...
public class AopTest {
...
//切入点
@Pointcut("execution(* com.hpr.service.impl.TeacherService.insert(..))&&args(teacher)")
public void pointcut(Teacher teacher) {
}
//返回通知(返回结果后)
@AfterReturning(pointcut = "pointcut(teacher)", returning = "response")
public void afterReturning(Response<Teacher> response, Teacher teacher) {
File logDir = new File("./logs");
if (!logDir.exists()) {
logDir.mkdir();
}
try {
FileWriter fw = new FileWriter("./logs/insert.log", true);
PrintWriter pw = new PrintWriter(fw);
pw.println("【" + sdf.format(new Date()) + "】:teacher信息新增成功!");
pw.println(response.getData());
pw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
...
}
- 启动测试
执行结果
- /logs/insert.log
总结
重点
- 代理模式及开闭原则;
- SpringAOP相关术语;
- SpringAOP代码实现;
- SpringAOP方法参数及返回结果获取。
难点
- SpringAOP代码实现;
- SpringAOP方法参数及返回结果获取。