Spring Aop
看这个分享的应该都用过Spring Aop,这里就不再过多介绍了它是什么了。
我抽取了Spring Aop的部分源码,通过它实现请求参数可变拦截,同时apisdk离开Spring框架,仍然可以正常运行。
讲拦截也好,通知也罢,大家知道是什么意思就行了,不需要纠结这个叫法。
核心拓展类
利用Aop的前置通知,拓展了前置参数可变通知,原理是在运行的过程中,动态封装请求上下文SdkContext参数,变更请求参数。
上面圈的类是参照Aop的代码结构,拓展出的通知,描述如下:
- MethodBeforeArgsChangeableAdvice:需要开发者实现的接口,在apisdk中唯一实现SdkContextArgsBeforeChangeableAdvice,实现了SdkContext的构建
- MethodBeforeChangeableAdviceAdapter:适配器,把开发者实现的MethodBeforeArgsChangeableAdvice交给底层去执行,是框架执行开发者代码的入口
- MethodBeforeChangeableAdviceInterceptor:执行开发者的MethodBeforeArgsChangeableAdvice,最后把结果添加到方法参数上
- SdkContextArgsBeforeChangeableAdvice:MethodBeforeArgsChangeableAdvice的唯一实现,实现了SdkContext的关键代码
核心代码
SdkContext自动封装
public class SdkContextArgsBeforeChangeableAdvice implements MethodBeforeArgsChangeableAdvice {
@Override
public Object before(Method method, Object[] args, Object target) throws Throwable {
// 判断执行的方法是否需要自动注入 SdkContext
// 如果用户的接口声明的方法参数有 SdkContext,则说明用户要手动创建,否则底层自动创建
boolean isAutoType = MethodWrapperCache.isAutoType(method);
if (isAutoType) {
// SdkContextManager 封装了域名、secret、token等服务的调用
SdkContextManager instance = SdkContextManager.getInstance();
SdkContext sdkContext = instance.getSdkContext();
return sdkContext;
}
return null;
}
}
动态调整请求参数
MethodBeforeChangeableAdviceInterceptor
public class MethodBeforeChangeableAdviceInterceptor implements MethodInterceptor, BeforeAdvice {
private MethodBeforeArgsChangeableAdvice advice;
public MethodBeforeChangeableAdviceInterceptor(MethodBeforeArgsChangeableAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 调用开发者的 MethodBeforeArgsChangeableAdvice
Object arg = this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
if (arg != null && mi instanceof ReflectiveMethodInvocation) {
// 将 MethodBeforeArgsChangeableAdvice 返回的结果,添加到调用方法
((ReflectiveMethodInvocation) mi).appendArgument(arg);
}
return mi.proceed();
}
}
ReflectiveMethodInvocation#appendArgument
public class ReflectiveMethodInvocation implements MethodInvocation {
// 方法参数
protected Object[] arguments;
// 调整方法请求参数
public void appendArgument(Object obj) {
if (this.arguments != null && this.arguments.length > 0) {
int length = this.arguments.length;
Object[] newArgs = new Object[length + 1];
System.arraycopy(this.arguments, 0, newArgs, 0, length);
newArgs[length] = obj;
this.arguments = newArgs;
} else {
this.arguments = new Object[1];
this.arguments[0] = obj;
}
}
}
Jdk 动态代理
原始接口A和增强接口B,他们的实例化必须由动态代理支持。apisdk有SdkManager和JdkDynamicAopProxy两个代理对象生成器,SdkManager是原代码中就有的,JdkDynamicAopProxy是我参考Spring aop拓展的。
二开后,SdkManager用于生成B的代理对象,JdkDynamicAopProxy用于生成A的代理对象,并且配置了一系列的拦截动作。
开发逻辑:开发者使用A的代理对象,调用方法,底层执行A方法,执行拦截动作,再拿到B的代理对象,执行B的方法。
JdkDynamicAopProxy代码如下:
public class JdkDynamicAopProxy implements InvocationHandler {
// 包装被代理的对象,每个被代理的对象都有一组Advice
private AdvisedSupport advised;
public JdkDynamicAopProxy(AdvisedSupport advised) {
this.advised = advised;
}
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getProxiedInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取被代理的对象
Object target = advised.getTarget();
// 获取 Method 匹配的所有 Advice
List<Object> chain = this.advised.getInterceptors(method, target.getClass());
Object retVal;
if (chain.isEmpty()) {
// 没有 Advice,直接执行 Method
retVal = method.invoke(target, args);
} else {
// 递归调用 Advice
ReflectiveMethodInvocation reflectiveMethodInvocation = new ReflectiveMethodInvocation(proxy, target, method, args, chain);
retVal = reflectiveMethodInvocation.proceed();
}
return retVal;
}
}