SpringBoot的AOP实践
导读
之前写过一篇,利用AOP记录用户操作日志:点我直达。
核心概念
横切关注点
- 对那些方法进行拦截,拦截后怎么处理,这些就叫横切关注点
- 比如:权限认证、日志、事务
通知 Advice
- 在特定的切入点上执行的增强处理,有5种通知
- 用途:记录日志、控制事务、提前编写好通用的模块,需要的地方直接调用
连接点 JointPoint
- 要用通知的地方,业务流程在运行过程中需要插入切面的具体位置
- 一般是方法的调用前后,全部方法都可以是连接点
- 只是概念,没啥特殊
切入点 Pointcut
- 不能全部方法都是连接点,通过特定的规则来筛选连接点,就是Pointcut,选中那几个你想要的方法
- 在程序中主要体现为书写切入点表达式(通过通配、正则表达式)过滤出特定的一组JointPoint连接点
- 过滤出相应的Advicce将要发生的joinPoint地方
切面 Aspect
- 通常是一个类,里面定义 切入点+通知,定义在什么地方;什么时间点,做什么事情
- 通知 advice致命类时间和做的事情(前置、后置等)
- 切入点pointcut指定在什么地方干这个事情
- web接口设计中,web层-》网关层-》服务层-》数据层,每一层之间也是一个切面,对象和对象,方法和方法之间都是一个个切面
目标 target
- 目标类,真正的业务逻辑,可以在目标类不知情的条件下,增加新的功能到目标类的链路上
织入 Weaving
- 把切面(某个类)应用到目标函数的过程成为织入
AOP 代理
- AOP框架创建的对象,代理就是目标对象的加强
- Spring中的AOP代理可以使用JDK动态代理,也可以是CGLIB代理
添加依赖
<!-- AOP依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
项目结构

自定义注解
package com.ybchen.annotation; import java.lang.annotation.*; /** * 自定义注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CustomAnnotation { String value() default ""; }
Aspect
package com.ybchen.component; import com.ybchen.annotation.CustomAnnotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Arrays; /** * 自定义日志 AOP * * @Author:chenyanbin */ @Aspect @Component public class CustomLogAspect { private Logger logger = LoggerFactory.getLogger(CustomLogAspect.class); /** * 定义切入点 */ @Pointcut("execution(public * com.ybchen.controller.*.*(..))") public void pointCut() { } /** * 被自定义注解修饰的切入点 */ @Pointcut("@annotation(com.ybchen.annotation.CustomAnnotation)") public void pointCutAnnotation() { } /** * 前置通知 * * @param joinPoint * @throws Throwable */ @Before("pointCut()") public void before(JoinPoint joinPoint) throws Throwable { // 接收到请求,记录请求内容 logger.info("【注解:Before】------------------切面 before"); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("【注解:Before】浏览器输入的网址=URL : " + request.getRequestURL().toString()); logger.info("【注解:Before】HTTP_METHOD : " + request.getMethod()); logger.info("【注解:Before】IP : " + request.getRemoteAddr()); logger.info("【注解:Before】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("【注解:Before】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs())); } /** * 被自定义注解修饰的,前置通知 * * @param joinPoint * @throws Throwable */ @Before("pointCutAnnotation()") public void beforeAnnotation(JoinPoint joinPoint) throws Throwable { // 接收到请求,记录请求内容 logger.info("【自定义注解】-【注解:Before】------------------切面 before"); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("【自定义注解】【注解:Before】浏览器输入的网址=URL : " + request.getRequestURL().toString()); logger.info("【自定义注解】【注解:Before】HTTP_METHOD : " + request.getMethod()); logger.info("【自定义注解】【注解:Before】IP : " + request.getRemoteAddr()); logger.info("【自定义注解】【注解:Before】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("【自定义注解】【注解:Before】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs())); } /** * 后置返回通知 * * @param ret * @throws Throwable */ @AfterReturning(returning = "ret", pointcut = "pointCut()") public void afterReturning(JoinPoint joinPoint, Object ret) throws Throwable { // 处理完请求,返回内容 logger.info("【注解:AfterReturning****】这个会在切面最后的最后打印,方法的返回值 : " + ret); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("【注解:AfterReturning****】浏览器输入的网址=URL : " + request.getRequestURL().toString()); logger.info("【注解:AfterReturning****】HTTP_METHOD : " + request.getMethod()); logger.info("【注解:AfterReturning****】IP : " + request.getRemoteAddr()); logger.info("【注解:AfterReturning****】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("【注解:AfterReturning****】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs())); } /** * 被自定义注解修饰的,后置返回通知 * * @param ret * @throws Throwable */ @AfterReturning(returning = "ret", pointcut = "pointCutAnnotation()") public void afterReturningAnnotation(JoinPoint joinPoint, Object ret) throws Throwable { // 处理完请求,返回内容 logger.info("【自定义注解】【注解:AfterReturning】这个会在切面最后的最后打印,方法的返回值 : " + ret); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("【自定义注解】【注解:AfterReturning****】浏览器输入的网址=URL : " + request.getRequestURL().toString()); logger.info("【自定义注解】【注解:AfterReturning****】HTTP_METHOD : " + request.getMethod()); logger.info("【自定义注解】【注解:AfterReturning****】IP : " + request.getRemoteAddr()); logger.info("【自定义注解】【注解:AfterReturning****】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("【自定义注解】【注解:AfterReturning****】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs())); // 从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取切入点所在的方法 Method method = signature.getMethod(); //获取方法上的自定义注解 CustomAnnotation annotation = method.getAnnotation(CustomAnnotation.class); if (annotation == null) { return; } logger.info("【自定义注解】【注解:AfterReturning****】自定义注解的值:" + annotation.value()); } /** * 后置异常通知 * * @param joinPoint */ @AfterThrowing(value = "pointCut()", throwing = "e") public void afterThrowing(JoinPoint joinPoint, Throwable e) { logger.info("【注解:AfterThrowing】方法异常时执行....."); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("【注解:AfterThrowing】浏览器输入的网址=URL : " + request.getRequestURL().toString()); logger.info("【注解:AfterThrowing】HTTP_METHOD : " + request.getMethod()); logger.info("【注解:AfterThrowing】IP : " + request.getRemoteAddr()); logger.info("【注解:AfterThrowing】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("【注解:AfterThrowing】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs())); logger.info("【注解:AfterThrowing】exception : " + e); } /** * 被自定义注解修饰的,后置异常通知 * * @param joinPoint */ @AfterThrowing(value = "pointCutAnnotation()", throwing = "e") public void afterThrowingAnnotation(JoinPoint joinPoint, Throwable e) { logger.info("【自定义注解】【注解:AfterThrowing】方法异常时执行....."); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("【自定义注解】【注解:AfterThrowing】浏览器输入的网址=URL : " + request.getRequestURL().toString()); logger.info("【自定义注解】【注解:AfterThrowing】HTTP_METHOD : " + request.getMethod()); logger.info("【自定义注解】【注解:AfterThrowing】IP : " + request.getRemoteAddr()); logger.info("【自定义注解】【注解:AfterThrowing】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("【自定义注解】【注解:AfterThrowing】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs())); logger.info("【自定义注解】【注解:AfterThrowing】exception : " + e); } /** * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 * * @param jp */ @After("pointCut()") public void after(JoinPoint jp) { logger.info("【注解:After】方法最后执行....."); } /** * 被自定义注解修饰的,后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 * * @param jp */ @After("pointCutAnnotation()") public void afterAnnotation(JoinPoint jp) { logger.info("【自定义注解】【注解:After】方法最后执行....."); } /** * 环绕通知,环绕增强,相当于MethodInterceptor * * @param pjp * @return */ @Around("pointCut()") public Object around(ProceedingJoinPoint pjp) { logger.info("【注解:Around . 环绕前】方法环绕start....."); try { //如果不执行这句,会不执行切面的Before方法及controller的业务方法 Object o = pjp.proceed(); logger.info("【注解:Around. 环绕后】方法环绕proceed,结果是 :" + o); return o; } catch (Throwable e) { e.printStackTrace(); return null; } } /** * 被自定义注解修饰的,环绕通知,环绕增强,相当于MethodInterceptor * * @param pjp * @return */ @Around("pointCutAnnotation()") public Object aroundAnnotation(ProceedingJoinPoint pjp) { logger.info("【自定义注解】【注解:Around . 环绕前】方法环绕start....."); try { //如果不执行这句,会不执行切面的Before方法及controller的业务方法 Object o = pjp.proceed(); logger.info("【自定义注解】【注解:Around. 环绕后】方法环绕proceed,结果是 :" + o); return o; } catch (Throwable e) { e.printStackTrace(); return null; } } }
vo类
package com.ybchen.vo; /** * @Author:chenyanbin */ public class userVo { private String account; private String password; public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "userVo{" + "account='" + account + '\'' + ", password='" + password + '\'' + '}'; } }
控制器
package com.ybchen.controller; import com.ybchen.vo.userVo; import org.springframework.web.bind.annotation.*; /** * @Author:chenyanbin */ @RestController @RequestMapping("user") public class UserController { @GetMapping("login") public Object login(String accout, String password) { if ("".equalsIgnoreCase(accout)) { throw new NullPointerException(); } if (accout.equalsIgnoreCase(password)) { return "登录成功"; } else { return "登录失败"; } } @PostMapping("login2") public Object login2(@RequestBody userVo userVo) { if ("".equalsIgnoreCase(userVo.getAccount())) { throw new NullPointerException(); } if (userVo.getAccount().equalsIgnoreCase(userVo.getPassword())) { return "登录成功"; } else { return "登录失败"; } } }
package com.ybchen.other; import com.ybchen.annotation.CustomAnnotation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author:chenyanbin */ @RestController @RequestMapping("annotation") public class AnnotationController { @GetMapping("getName") @CustomAnnotation(value = "获取我的名字") public Object getName(String name) { return "我叫:" + name; } @GetMapping("getAge") @CustomAnnotation(value = "获取我的年龄") public void getAge(int age) { System.out.println("我今年:" + age + "岁啦"); } }
演示


浙公网安备 33010602011771号