SpringAOP自定义注解应用
一、AOP简述
什么是aop?
AOP 即 Aspect Oriented Program 面向切面编程
与OOP对比,AOP是处理一些横切行问题。这些横切性问题不会影响到主逻辑的实现,但是会散落到代码的各个部分,难以维护。AOP就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。
传统OOP是自上而下的逻辑开发如图1,AOP是一种面向切面的编程思想。这些横切性问题,把它们抽象为一个切面,关注点在切面的编程如图2。
图1 图2
如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
- 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
- 所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP
AOP 的目的
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP应用场景
日志记录,权限验证,效率检查,事务管理......
AOP 当中的概念:
- 切入点(Pointcut)
在哪些类,哪些方法上切入(where) - 通知(Advice)
在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能) - 切面(Aspect)
切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强! - 织入(Weaving)
把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
注:开发AOP有两种方式1、注解开发 2、xml配置开发 此处只对注解开发做详细笔记。
二、使用注解来开发 Spring AOP
选择连接点
Spring 是方法级别的 AOP 框架,我们主要也是以某个类额某个方法作为连接点,另一种说法就是:选择哪一个类的哪一方法用以增强功能。
@GetMapping(value = "/test1")
public String test1(HttpServletRequest request, String var1) {
LOG.info("============打印日志开始============");
LOG.info("URL: " + request.getRequestURL().toString());
LOG.info("============打印日志结束============");
return "test1";
}
创建切面
@Aspect
@Component
@Log4j2
//@Profile("dev") //指定dev环境该注解有效,其他环境无效(未测试)
public class DemoAspect {
private static final Logger logger = LoggerFactory.getLogger(DemoAspect.class);
/**
* 切入点
*
* @Pointcut("@annotation(cn.van.annotation.annotation.WebLog)")
**/
@Pointcut("execution(public * com.example.demo2.controller.*.*(..))")
public void addAdvice() {
}
@Before("addAdvice()")
public void before(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
HttpServletRequest requests = (HttpServletRequest) args[0];
log.info("============打印日志开始============");
log.info("URL: " + requests.getRequestURL().toString());
log.info("============打印日志结束============");
// LOG.info("before....");
}
}
@Aspect
注解一个类,那么 Spring IoC 容器就会认为这是一个切面了。@Component
注解标注。@Before
前置通知,在连接点方法前调用@Around
环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法,后面会讲@After
后置通知,在连接点方法后调用@AfterReturning
返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常@AfterThrowing
异常通知,当连接点方法异常时调用
定义切点
切面中用到的@Pointcut注解即为切入点。@Pointcut("execution(public * com.example.demo2.controller.*.*(..))")
环绕通知
这是 Spring AOP 中最强大的通知,因为它集成了前置通知和后置通知,它保留了连接点原有的方法的功能,所以它及强大又灵活。如下代码:
// 使用 @Around 注解来同时完成前置和后置通知
@Around("execution(* pojo.Landlord.service())")
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("开始");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("结束");
}
测试的结果为:
开始
业务层中进行的处理
结束
简单来说:环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的。
ps:
SpringBoot 配置 AOP 记录日志 可以通过切面的方式打印控制器层的日志,但是可能存在以下问题:
不够灵活,由于是以所有 Controller 方法中的方法为切面,也就是说切死了,如果说我们不想让某个接口打印出入参日志,就办不到了;
Controller 包层级过深时,导致很多包下的接口切不到。
所以,可以通过指定某些方法打印日志,即通过自定义注解打印日志。