它是一种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。用于切入到指定类指定方法的代码片段叫做切面,而切入到哪些类中的哪些方法叫做切入点
AOP编程允许把遍布应用各处的功能分离出来形成可重用的组件
实现一个AOP可以分成下面几个步骤:
1.引入依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
2.自定义注解 @Permission,其中的 @Target 和 @Retention 元注解参考:元注解(Annotation)
1 2 3 4 5 6 7 8 9
| import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Permission { }
|
参考
3.编写切点
常用的Aspect指示器:@execution,@within,@annotation
@execution:用于在方法执行时触发,如:在com.example.demo.controller.XXXController.register方法在任意参数(..)下执行,返回任意类型(*)的情况下执行
1 2
| @Pointcut("execution(* com.example.demo.controller.XXXController.register(..))")
|
@within:用于在方法执行时触发,如:在com.example.demo.controller包下的任意方法执行
1 2
| @Pointcut("within(com.example.demo.controller.*)")
|
@annotation:用于注解对象,如:指定 com.example.xxx 包下及其所有子目录下的所有带有 @hello 注解的方法体为切点
1 2
| @Pointcut("within(com.example.xxx.*) && @annotation(hello)")
|
4.编写切点checkTokenCut,以及定义切面Aspect,简单检查request请求的cookie中是否包含token,且切点设置在添加了 @Permission 注解的 方法上
此外还可以在aop类上添加 @Order注解,来定义Spring IOC容器中Bean的执行顺序的优先级,值越小拥有越高的优先级,可为负数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest;
@Slf4j @Aspect @Component public class PermissionAspect {
@Pointcut("@annotation(com.example.demo.annotation.Permission) && within(com.example.demo.controller.*)") public void checkTokenCut() { }
@Around("checkTokenCut()") public Object doCheckToken(ProceedingJoinPoint point) throws Throwable { ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = attr.getRequest(); Cookie[] cookies = request.getCookies(); int len = cookies.length; for (int i = 0; i < len; i++) { Cookie cookie = cookies[i]; if ("token".equals(cookie.getName())) { log.info("has token"); break; } if (i == len - 1) { log.info("no token"); } } //执行代理目标方法 return point.proceed(); }
}
|
5.注入AspectJ切面 @Permission
1 2 3 4 5 6
| @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @RequestMapping(path = "/fail", method = RequestMethod.GET) @Permission public ControllerResponseT fail() { return ControllerResponseT.ofFail(50001, "error", null); }
|
请求接口的时候将会执行check token逻辑
参考:SpringBoot使用AOP+注解实现简单的权限验证
前后端分离:使用spring的Aop实现Token验证
其他aop的例子:
使用aop实现数据脱敏:改造了以前写的数据脱敏插件,更好用了
使用aop实现接口响应时间记录:Spring Boot 2实践系列(四十七):Spring AOP 实现API接口处理请求耗时监控
使用aop实现全局异常处理以及统一打印接口请求入参和返回结果日志,打印接口访问性能日志,处理sql注入攻击以及处理入参特殊字符等问题:Springboot项目全局异常统一处理