而AOP面向方法编程,就可以做到在不改动这些原始方法的基础上,针对特定的方法进行编程,可以是功能增强也可以是功能的改变
AOP的作用:在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性: 解耦)
AOP的优势:
减少重复代码
提高开发效率
维护方便
一. 怎么实现
1. 在pom文件中导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2.创建一个aop包用于管理aop
package com.xxxx.aop; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Component @Slf4j @Aspect //标志当前类是一个aop类 public class TimeAspect { //下面代码作用在那些方法上 当执行service层的.*(接口或者类)中的*(..)方法时都会执行下面共性的逻辑 @Around("execution(* com.itheima.service.*.*(..))") //切入点表达式 public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable { //1.记录开始时间 long begin = System.currentTimeMillis(); //2 调用原始方法执行 //原始方法的返回值 Object result = joinPoint.proceed(); //3 记录书记兼,计算方法执行耗时 //获取原始方法的签名----joinPoint.getSignature() log.info(joinPoint.getSignature()+"方法执行耗时:{}", System.currentTimeMillis()-begin); return result; } }
二 . AOP核心概念
1. 连接点:JoinPoint,可以被AOP控制的方法,连接点指的是可以被aop控制的方法。这里就是service中接口或者类中的所有方法
就是控制哪个包下的方法
2. 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
就是这一块,将所有方法中重复的逻辑集中到一起
3. 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用---就是切入点表达式所有方法中实际被应用的方法
4. 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
5. 目标对象:Target,通知所应用的对象
目标对象指的就是通知所应用的对象,我们就称之为目标对象。
三. 执行流程
一旦我们进行了基于aop程序的开发,最开始执行的就不是原始的对象,而是基于动态代理技术的动态代理对象
四. AOP 通知类型
Spring中AOP的通知类型:
@Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
@Before:前置通知,此注解标注的通知方法在目标方法前被执行
@After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
@AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
@AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
1,抽取切入点表达式
@Slf4j @Component @Aspect public class MyAspect1 { //切入点方法(公共的切入点表达式) 如果想要别的方法也可以使用该表达式方法的修饰符要改成 public @Pointcut("execution(* com.itheima.service.*.*(..))") private void pt(){ } //前置通知(引用切入点) @Before("pt()") public void before(JoinPoint joinPoint){ log.info("before ..."); } //环绕通知 @Around("pt()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { log.info("around before ..."); //调用目标对象的原始方法执行 Object result = proceedingJoinPoint.proceed(); //原始方法在执行时:发生异常 //后续代码不在执行 log.info("around after ..."); return result; } //后置通知 @After("pt()") public void after(JoinPoint joinPoint){ log.info("after ..."); } //返回后通知(程序在正常执行的情况下,会执行的后置通知) @AfterReturning("pt()") public void afterReturning(JoinPoint joinPoint){ log.info("afterReturning ..."); } //异常通知(程序在出现异常的情况下,执行的后置通知) @AfterThrowing("pt()") public void afterThrowing(JoinPoint joinPoint){ log.info("afterThrowing ..."); } }
@Slf4j @Component @Aspect public class MyAspect2 { //引用MyAspect1切面类中的切入点表达式 @Before("com.itheima.aspect.MyAspect1.pt()") public void before(){ log.info("MyAspect2 -> before ..."); } }
五,多个切面类执行顺序
切面方法类名排名越靠前 before执行越靠前
after执行顺序越靠后
使用注解 在切面类上加上order来改变顺序
@Order(2) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)