AOP是什么
AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,将某些公共模块抽取出来放到一个可重用模块里,减少系统的重复代码,降低系统的耦合度,增强代码的可操作性和可维护下
使用场景
一般场景下,AOP被广泛应用在权限认证、日志、事务处理、增强处理、安全监控等等。
AOP的特性
Advice-通知:AOP中的增强处理,通知描述切面何时执行以及如何执行增强处理
Join Point - 连接点:连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法调用、异常抛出。不过在Spring AOP中,Join point是方法调用的连接点
PointCut - 切点:可以插入增强处理的连接点
Aspect - 切面:切面是通知和切点的结合
Introduction - 引入:允许我们向现有的类添加新的方法或者属性
Weaving - 织入:将增强处理添加到目标对象中,并创建一个被增强的代理对象
切点指示器
Aspect 指示器 | 描述 |
arg() | 限制连接点匹配参数为指定类型的执行方法 |
@arg() | 限制连接点匹配参数由指定注解标注的执行方法 |
execution | 匹配连接点的执行方法 |
this() | 限制连接点匹配AOP代理的bean引用为指定类型的类 |
target() | 限制连接点匹配目标对象为指定类型的类 |
@target() | 限制连接点匹配特定的执行对象,这些对象对应类要具备指定类型的注解 |
within() | 限制连接点匹配指定的类型 |
@within() | 限制连接点匹配指定注解所标注的类型 |
@annotation | 限制匹配带有指定注解的连接点 |
通知注解描述
通知 | 描述 |
@Before | 在调用前执行 |
@After | 目标方法返回或异常后调用 |
@AfterReturning | 方法返回后调用 |
@AfterThrowing | 抛出异常后调用 |
@Around | 环绕调用 |
代码实现
@Aspect
@Component
public class TestAop {
@Pointcut("execution(* cloud2.aop.Filter.after(..))")
public void point() {
}
@Before("point()")
public void before() {
System.out.println("before ...");
}
@After("point()")
public void after() {
System.out.println("After ...");
}
@AfterReturning("point()")
public void afterReturning() {
System.out.println("AfterReturning ...");
}
@Around("point()")
public void around(ProceedingJoinPoint pj) {
try {
System.out.println("Around aaa ...");
pj.proceed();
System.out.println("Around bbb ...");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
实现一个USER_ID限流
@Slf4j
@Order(-1)
@Aspect
@Component
public class ApiFilterAop implements InitializingBean, DisposableBean, ApplicationContextAware
{
@Around("@within(org.springframework.web.bind.annotation.RestController)")
public Object doFilter(ProceedingJoinPoint joinPoint) throws Throwable
{
Object[] args = joinPoint.getArgs();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Object target = joinPoint.getTarget();
Object[] finalArgs = args;
for(Object arg : args) {
// 定义一个父类,参数属于AppRequest 才会进入
if(arg instanceof AppRequest)
{
isAppApi = true;
AppRequest request = (AppRequest) arg;
//初始化上下文 - 可扩展request
//before正序执行
for(int i = 0; i < filters.size(); i++)
{
// Filter filter 为自定义的一个类
Filter filter = filters.get(i);
finalArgs = filter.before(target, method, args);
}
break;
}
}
}
}
利用redis实现
@Component
public class SentinelFilter implements Filter {
@Override
public Object[] before(Object target, Method method, Object... args) throws Exception {
for (Object arg : args) {
if (arg instanceof AppRequest) {
AppRequest request = (AppRequest) arg;
// 自定义的注解,需要的话联系我
ApiFilter apiFilter = method.getAnnotation(ApiFilter.class);
if (apiFilter != null) {
long intervalLimit = apiFilter.intervalLimit();
String type = apiFilter.type();
if (intervalLimit > 0) {
String methodName = method.getName();
switch (type) {
case "IP":
// TODO 可扩展IP限流
break;
case "USERID":
//可扩展USERID限流
String phone = request.getUserId();
//过滤请求
String key = CommonKey.phoneRequestSentinel(phone,methodName);
String value = JodisUtils.getInstance().get(key);
// 这里写一个断言,存在时则返回
JodisUtils.getInstance().setStringEx(key, intervalLimit, "true");
break;
case "USER_TOKEN":
// TODO 可扩展USER_TOKEN限流
break;
case "DEVICE_NO":
// TODO 可扩展DEVICE_NO限流
break;
default:
break;
}
}
}
break;
}
}
return args;
}
}
AOP执行流程
- 首先ApplicationContext会调用AdvisedSupport
- AdvisedSupport 主要是为了完成配置文件的解析以及去构建切面、以及区完成切面与切点之间的关系,可以理解为工具类
- 解析完成后,调用AopConfig类保存AOP的配置信息
- 保存完成通过调用Advice类,发送通知,完成切面的回调。
- 最后通过JDKDynamicAopProxy类或者CglibAopProxy生成代理类。
实现原理
Spring AOP的底层实现原理其实就是动态代理,目前Spring中有两种动态代理方式,一种是JDK反射,一种是cglib方式。
当目标类是接口类时,就会直接使用jdk的动态代理方式,其余的都是使用cglib动态代理。所以aop的实现有两个关键步骤,一个是要得到代理对象,一个是要利用递归责任链执行前后置通知目标方法。