SpringBoot实践(三十九):如何使用AOP
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
面向切面AOP) 是spring重要特性在功能上切面编程是面向对象编程的很好的补充面向对象强调封装和开闭原则如果多个对象有通用行为和方法将造成很多冗余代码AOP将通用功能作为切面插入到业务逻辑中如通用的日志打印异常处理license判断等抽取通用逻辑减少代码冗余并且无侵入地修改代码在实现层面上都是通过动态代理实现默认cglib方式SpringBoot简化了它的使用常用的2种方式直接使用aspect注解扫描或基于自定义的注解。
首先在springBoot的依赖管理基础上引入aop的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
直接使用@Aspect
定义切面逻辑
使用@Aspect实现一个通用的业务逻辑比如日志打印提供了几种切面增强方式前置增强@Before后置增强@After环绕返回@AfterReturning环绕增强@Around(前后都有)异常抛出增强AfterThrowing执行顺序是
- 正常情况@Around-->@Before-->目标方法执行-->@AfterReturning-->@After-->@AfterReturning-->@Around
- 异常情况@Around-->@Before-->目标方法执行-->@AfterThrowing-->@After-->AfterThrowing-->@Around
如下
@Component
@Aspect
@Slf4j
public class LoggingAspect {
@Pointcut("execution(public * com.example.hello.controller.*.*())")
public void LogAspect(){}
@Before("LogAspect()")
public void doBefore(JoinPoint joinPoint){
log.info(" -------------------------> this is before.");
}
@After("LogAspect()")
public void doAfter(JoinPoint joinPoint){
log.info(" -------------------------> this is after.");
}
@AfterReturning("LogAspect()")
public void doAfterReturning(JoinPoint joinPoint){
log.info(" -------------------------> this is afterReturning.");
}
@AfterThrowing("LogAspect()")
public void deAfterThrowing(JoinPoint joinPoint){
log.info(" -------------------------> this is deAfterThrowing.");
}
@Around("LogAspect()")
public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable{
log.info(" -------------------------> this is deAround.");
return joinPoint.proceed();
}
}
模拟业务代码
这里的切入点是controller类中方法controller的方法定义如下其中helloService模拟的是真实的业务代码这个controller不需要做任何修改测试正常输入和异常输出
@GetMapping("/hello")
public String sayHello(){
log.info("---- 模拟基于标准注解的切面过程 ---------- >running hello");
return helloService.sayHello();
}
@GetMapping("/hello2")
@LogAnnotation(param = "HelloController")
public String sayHello2(){
log.info("---- 模拟异常后切面过程 ---------- >running hello2");
throw new RuntimeException();
}
测试输出
在正常输出业务代码的前后及方法的全局都被注入了自定义的逻辑
异常输出
自定义注解方式
上面的方式可以无侵入地为我们的业务代码增加某些通用逻辑同时我们也可以自定义注解方式然后将自定义注解增加到部分业务代码中
自定义切面注解
定义注解LogAannotation能够在方法和类上标注有个参数param默认空
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface LogAnnotation {
String param() default "";
}
定义切入点逻辑
LoggingAspect2实现了切入点和自定义注解@LogAnnotation的逻辑在切入点前后执行时间打印
@Aspect
@Component
@Slf4j
public class LoggingAspect2 {
@Around("@annotation(logAnnotation)")
public Object around(ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
log.info(String.format("time now :%s",new Date()));
Object o = joinPoint.proceed();
log.info(String.format("time now :%s",new Date()));
return o;
}
}
模拟业务代码
这种方式需要修改业务代码增加对于自定义注解的传参
@GetMapping("/hello3")
@LogAnnotation(param = "HelloController")
public String sayHello3(){
log.info("---- 模拟基于自定义注解的切面 ---------- >running hello3");
return helloService.sayHello();
}
测试输出
这里因为上面的@aspect注解增加了环绕增强因此hello3的上下日期打印是自定义注解的实现。