8:AOP
8.1:入门案例
- 导入相关依赖,
AOP
的依赖在Spring-context中已经被包含了,所以只需要再导入切面依赖即可
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!--切面依赖--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
- 自定义一个通知类(通知类必须配置成Spring管理的bean)
- 设置通知类为切面类
- 在Spring配置类中开启注解开发
AOP
功能 - 设置要作为增强功能的方法和切入点
//通知类必须配置成Spring管理的bean @Component //设置当前类为切面类类 @Aspect public class MyAdvice { //设置切入点,要求配置在方法上方 @Pointcut("execution(void com.ysj.dao.BookDao.update())") private void pt(){} //设置在切入点pt()的前面运行当前操作(前置通知) // @Before("pt()") public void method(){ System.out.println(System.currentTimeMillis()); } }
8.2:AOP
工作流程
- Spring容器启动
- 读取页面所有的切入点
- 初始化bean,判断bean中是否有方法匹配到任意切入点
- 匹配到,创建代理对象
- 未匹配到,创建初始的对象
- 获取bean的执行方法
- 获取bean,调用方法执行,完成操作
- 获取的bean是代理对象时,执行是按照代理对象的运行方式运行原生方法和
AOP
定义的增强方法
Eg
:在AOP
中,AOP
对最终的对象的ToString
方法进行了重写,如果直接输出对象则是类似下面这样
`com.ysj.dao.impl.BookDaoImpl@4988d8b8`
8.3:AOP
切入点表达式
- *** ** :单个独立的任意符号,可独自出现,也可作为前缀或后缀的匹配符出现
- … :多个连续的任意符号,可独立出现,用于简化包名与参数的书写
+
:专门用于匹配子类类型
//表示:任意返回值类型,在为顶层的包结构下任意包中的以Dao结尾的接口或类的子类中以up开头的无参方法 @Pointcut("execution(* com.*..*Dao+.up*())")
8.4:AOP通知类型
- @before(“原始方法”):在原始方法运行之前
- @after():在原始方法运行之后
- @Around():环绕原始方法运行,可定义位置
- 环绕通知必须依赖形参
ProceedingJoinPoint
才能在方法内部对原始方法进行调用 - 未使用
ProceedingJoinPoint
将跳过原始方法的执行 - 对原始方法接收返回值的话必须设置返回值类型为Object,不接收的话可void,也可Object
public Object around(ProceedingJoinPoint pjp)
- 因为无法预知原始方法是否抛出异常,所以环绕通知必须抛出
Throwable
异常
public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around before advice ..."); //表示对原始操作的调用 Object ret = pjp.proceed(); System.out.println("around after advice ..."); return ret; //返回原始方法执行的返回值 }
- 另外俩(了解):
//@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象 // @AfterReturning("pt2()") public void afterReturning() { System.out.println("afterReturning advice ..."); } //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行 @AfterThrowing("pt2()") public void afterThrowing() { System.out.println("afterThrowing advice ..."); }
8.5:测试业务层接口执行效率
//设置环绕通知,在原始操作的运行前后记录执行时间 @Around("ProjectAdvice.servicePt()") public void runSpeed(ProceedingJoinPoint pjp) throws Throwable { //获取执行的签名对象 Signature signature = pjp.getSignature(); //获取类类型(带路径) String className = signature.getDeclaringTypeName(); //获取类名 String methodName = signature.getName(); long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { pjp.proceed(); } long end = System.currentTimeMillis(); System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms"); }
8.6:通知获取数据
ProceedingJonPoint
:专用于环绕通知,是JoinPoint
子类,可以实现对原始方法的调用JoinPoint
:用来描述切入点对象,但必须配置成通知方法中的第一个参数,可以实现对原始方法的调用
getArgs():用来获取原始方法中的参数,返回值是一个数组
- 在原始方法return后和抛出异常后
//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同 @AfterReturning(value = "pt()",returning = "ret") public void afterReturning(JoinPoint jp,String ret) { System.out.println("afterReturning advice ..."+ret); } //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同 @AfterThrowing(value = "pt()",throwing = "t") public void afterThrowing(Throwable t) { System.out.println("afterThrowing advice ..."+t); }