目录
直接使用@Aspect
定义切面逻辑
模拟业务代码
测试输出
自定义注解方式
自定义切面注解
定义切入点逻辑
模拟业务代码
测试输出
面向切面(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的上下日期打印是自定义注解的实现。