目录
2.3JointPoint和ProceedingJoinPoint区别必知:
1.什么是 AOP
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
2.前置知识
2.1关于注解
@Aspect:告诉spring这是一个切面;
@Component:将切面交由spring来管理;
@Pointcut:切入点,直白点就是指定你需要从哪个地方切入,再直白点就是你想增强的目标方法,这里需要了解下execution表达式,可以通过这里来指定你需要切入的方法,可以指定单个方法、整个类的所有方法、类的某些方法、整个包下所有类的所有方法等;
@Before:目标方法执行前需要做的事;
@After:目标方法执行后需要做的事还有几个常用注解:
@Around(能自由的指定在目标方法执行前后做增强逻辑,需要手动调用ProceedingJoinPoint的proceed方法来执行目标方法,不调用则目标方法不会执行,如果目标方法有返回值,还需手动返回)
@AfterReturning(在目标方法正常执行完成后做增强,如果你需要获取方法返回值就用它)
@AfterThrowing(当目标方法执行过程中抛出异常时执行)
2.2执行时机:
切入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around,再织入AfterReturning,最后才织入After
2.3JointPoint和ProceedingJoinPoint区别必知:
JointPoint
通过JpointPoint对象可以获取到下面信息
# 返回目标对象,即被代理的对象
Object getTarget();
# 返回切入点的参数
Object[] getArgs();
# 返回切入点的Signature
Signature getSignature();
# 返回切入的类型,比如method-call,field-get等等,感觉不重要
String getKind();
ProceedingJoinPoint
Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的
暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,,这也是环绕通知和前置、后置通知方法的一个最大区别。这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。建议看一下 JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。
3.闲话不扯,基于Springboot实现一个切面
3.1基于注解实现切面,并且获取切点的返回值和参数等
自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface onlyqi {
String value() default "" ;
}
切面:
@Aspect
@Component
@Slf4j
public class RequestLimitContract {
@AfterReturning(value = "@annotation(com.onlyqi.upup01.annotation.onlyqi)",returning="returnValue")
private void EventEditMethod(JoinPoint joinPoint, Object returnValue) {
Teacher teacher= (Teacher) joinPoint.getArgs()[0];
log.info("********切点的参数********"+teacher.toString());
log.info("********切点的返回值********"+returnValue);
log.info("********切点的签名********"+joinPoint.getSignature());
log.info("********目标对象,即被代理的对象********"+joinPoint.getTarget());
}
}
Controller中的切点:
@PostMapping("/test1")
@onlyqi
public String test1(@RequestBody Teacher teacher) {
return "***我是切面1的返回结果*******";
}
请求和运行结果:
3.2 基于切点路径实现切面
切面:
@Aspect
@Component
@Slf4j
public class RequestLimitContract {
@Pointcut(value = "execution(* com.onlyqi.upup01.controller.TestController.test2(..))")
public void pointcut1(){
}
@Before("pointcut1()")
public void aspect1(){
log.info("****************切面2++++++++++++++++++++++++++********");
}
}
Controller中的切点:
@GetMapping("/test2")
public String test2(HttpServletRequest request) {
return "***切面2*******";
}
运行结果: