1.什么是AOP
这个问题应该在很多面试场景都会遇到,一般是热身问题。AOP是对OOP的补充。一些比较通用的逻辑,我们可以使用切面编程去解决,提高代码的可用性和提高效率。常见的场景如下:
- 事务
- 日志收集
- 权限校验
- …
2.AOP的底层技术
实现方式 | 差别 |
---|---|
jdk动态代理(默认) | 基于接口 |
cglib | 基于继承 |
3.AOP的概念
在使用AOP的时候,会有一些概念上的是需要知道的。
- aspect:切面的类,使用
@Aspect
注解标识。我的理解是给Spring扫描的一个类,他集合了下面所有的通知、切面等等… - pointcut:连接点的集合。
- Joinpoint:连接点。这个表示我们要作用在什么区域,一般是方法。
- Weaving :织入。将代理逻辑加入到原来的逻辑的过程叫做织入。
- target :目标对象
- aop Proxy:代理对象。
- advice:通知。包括两方面(位置+逻辑)。位置:指的是在哪实现我们的逻辑;逻辑:就是我们真正写业务代码的地方。
advice的通知类型:
Before:连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常。
After :连接点正常执行之后,执行过程中正常执行返回退出,非异常退出。
After throwing:执行抛出异常的时候
After (finally):无论连接点是正常退出还是异常退出,都会执行
Around advice:环绕通知可以在方法调用之前和之后执行自定义行为。
Proceedingjoinpoint 和JoinPoint的区别:Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。JoinPoint的方法
1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
2.Signature getSignature() :获取连接点的方法签名对象;
3.java.lang.Object getTarget() :获取连接点所在的目标对象;
4.java.lang.Object getThis() :获取代理对象本身;
proceed()有重载,有个带参数的方法,可以修改目标方法的的参数
4.结语
SpringAOP还有很多概念。个人感觉平时使用掌握上面的已经基本足够了,如果有特殊的使用场景可以参考Spring的官网,里面非常详细。
5.SpringAOP案例
@RestController
public class TestController {
@RequestMapping("/test")
@Idempotent(limit = 1,timeUnit = TimeUnit.SECONDS)
public String test(@RequestParam("userId") String userId) {
return userId + "hello world";
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
/**
* 多少时间以内不可以访问
* @return 时间
*/
int limit() default 1;
/**
* 时间单位
* @return 时间单位
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
切面:
@Aspect
@Component
public class IdempotentAspect {
public static final Map<String,String> CHECK_MAP = new HashMap<>(16);
/**
* 切点
*/
@Pointcut("@annotation(Idempotent)")
private void annotationPointCut() {
}
@Around("annotationPointCut()")
public Object processJoinCut(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
String className = proceedingJoinPoint.getTarget().getClass().getName();
MethodSignature signature = (MethodSignature)proceedingJoinPoint.getSignature();
String methodNAme = signature.getMethod().getName();
Idempotent idempotent = signature.getMethod().getAnnotation(Idempotent.class);
int limit = idempotent.limit();
TimeUnit timeUnit = idempotent.timeUnit();
String targetMethodParams= Arrays.toString(proceedingJoinPoint.getArgs());
String value = className + methodNAme + limit;
if (CHECK_MAP.get(targetMethodParams) != null) {
throw new RuntimeException("您操作太快了");
}
CHECK_MAP.put(targetMethodParams,value);
System.out.println(CHECK_MAP.toString());
//调用下一个切面或目标方法
return proceedingJoinPoint.proceed();
}
}
6.参考:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html