动态代理+SPI+责任链实现拦截器效果
前言
今天依然要重构小破司云平台部分业务,经过困难重重的调试,需求分析,并且考虑到每个业务模块都有类似的拦截需求,以及需求经常修改包括添加新的拦截需求,所以利用动态代理以及责任链的形式,重构这一部分功能,利用SPI形式作字节码插入,最后封装成jar包供其他开发人员引用。
场景
经过横向分析公司产品各个模块需求,遂总结出相当一部分拦截器与业务代码耦合在一起,此处拦截器并不是作为HTTP
请求的拦截器,而是每一次业务处理需要先执行一部分规则,来进行匹配校验,例如数据监控平台的告警规则,就是由页面上独立配置后,后台在收到数据巡检请求时需要执行的。
再加上开发人员代码规范及能力限制,导致此处业务功能代码堆积如山,完全无法维护及新增功能,所以本次重构旨在将这一部分拦截效果抽象出来,与业务代码解耦,并将其组件化(jar包),避免后续开发人员维护,二次开发举步维艰。
实现
需要拦截的接口
首先是需要被层层拦截的接口及实现类:
接口:
public interface EchoService {
String echo(String message);
}
实现类:
public class EchoServiceImpl implements EchoService{
@Override
public String echo(String message) {
System.out.println("[ECHO] : " + message);
return "[ECHO] : " + message;
}
}
拦截器(SPI)
定义3个拦截器,分别为InterceptorA
,InterceptorB
,InterceptorC
,以及拦截器的抽象实现AbstractInterceptor
,
为了方便后续注解的拓展,以及责任链的实现,拦截器入参为
javax.interceptor.InvocationContext
,其原理类似Servlet的Filter
的实现。
AbstractInterceptor:
import javax.interceptor.InvocationContext;
public abstract class AbstractInterceptor{
public void execute(InvocationContext invocationContext) throws Exception {
invocationContext.proceed();
};
}
拦截器:
public class InterceptorA extends AbstractInterceptor{
@Override
public void execute(InvocationContext invocationContext) throws Exception {
System.out.println("InterceptorA");
invocationContext.proceed();
}
}
public class InterceptorB extends AbstractInterceptor{
@Override
public void execute(InvocationContext invocationContext) throws Exception {
System.out.println("InterceptorB");
invocationContext.proceed();
}
}
public class InterceptorC extends AbstractInterceptor{
@Override
public void execute(InvocationContext invocationContext) throws Exception {
System.out.println("InterceptorC");
invocationContext.proceed();
}
}
SPI
其中,SPI机制需要在src/main/resources/META-INF/services
目录下新建com.my.myproject.chainable.AbstractInterceptor
文件,文件内容为:
com.my.myproject.chainable.InterceptorB
com.my.myproject.chainable.InterceptorC
com.my.myproject.chainable.InterceptorA
通过SPI加载拦截器的方法为:
private AbstractInterceptor[] loadInterceptors() {
ServiceLoader<AbstractInterceptor> loadedService = ServiceLoader.load(AbstractInterceptor.class);
return StreamSupport.stream(loadedService.spliterator(), Boolean.FALSE).toArray(AbstractInterceptor[]::new);
}
责任链实现
DelegateInvocationContext:
public class DelegateInvocationContext implements InvocationContext {
/**
* 需要执行方法的对象
*/
private final Object target;
/**
* 方法对象
*/
private final Method method;
/**
* 方法参数
*/
private Object[] params;
/**
* 用于保存调用链上下文数据的容器
*/
private final Map<String, Object> contextData;
public DelegateInvocationContext(Object target, Method method, Object... params) {
this.target = target;
this.method = method;
this.setParameters(params);
this.contextData = new HashMap<>();
}
@Override
public Object proceed() throws Exception {
return method.invoke(target, params);
}
@Override
public Object getTarget() {
return target;
}
@Override
public Object getTimer() {
return null;
}
@Override
public Method getMethod() {
return method;
}
@Override
public Constructor<?> getConstructor() {
return null;
}
@Override
public Object[] getParameters() {
return params;
}
@Override
public void setParameters(Object[] params) {
this.params = params != null ? params : new Object[0];
}
@Override
public Map<String, Object> getContextData() {
return contextData;
}
}
ChainableInvocationContext:
public class ChainableInvocationContext implements InvocationContext {
/**
* 最后需要执行的方法,即EchoService里的echo()方法,此处是将方法委托给InvocationContext
**/
private final InvocationContext delegateContext;
/**
* 拦截器数组,利用SPI读取
*/
private final Object[] interceptors;
/**
* 拦截器数组长度
*/
private final int length;
/**
* 拦截器数组下标与方法的对应关系,这里方法全是com.my.myproject.chainable.AbstractInterceptor#execute(javax.interceptor.InvocationContext)
*/
private final Map<Integer, Method> indexedAroundInvokeMethods;
/**
* 偏移量,没执行完成一个拦截器会+1,直到length
*/
private int pos;
public ChainableInvocationContext(InvocationContext delegateContext, Object... interceptors) {
this.delegateContext = delegateContext;
this.interceptors = interceptors;
this.length = interceptors.length;
this.indexedAroundInvokeMethods = initIndexedAroundInvokeMethods();
this.pos = 0;
}
/**
* 解析拦截器中执行的拦截方法
*/
private Map<Integer, Method> initIndexedAroundInvokeMethods() {
Map<Integer, Method> indexedMethods = new HashMap<>();
for (int i = 0; i < length; i++) {
Object interceptor = interceptors[i];
Method invokedMethod = findInvokeMethod(interceptor);
indexedMethods.put(i, invokedMethod);
}
return indexedMethods;
}
/**
* 这里简化了,根据方法名来过滤,实际需要考虑方法各个属性来过滤
* @param interceptor
* @return
*/
private Method findInvokeMethod(Object interceptor) {
Class interceptorClass = interceptor.getClass();
return Stream.of(interceptorClass.getMethods()).filter(method -> "execute".equals(method.getName())).findFirst().get();
}
/**
* 责任链执行关键
* @return
* @throws Exception
*/
@Override
public Object proceed() throws Exception {
if (pos < length){
int currentPos = pos++;
Object interceptor = interceptors[currentPos];
Method method = indexedAroundInvokeMethods.get(currentPos);
//每次执行将当前对象传递下去(当前对象为InvocationContext的实现,这也是execute方法参数是InvocationContext的原因)
return method.invoke(interceptor,this);
}else{
return delegateContext.proceed();
}
}
@Override
public Object getTarget() {
return delegateContext.getTarget();
}
@Override
public Object getTimer() {
return delegateContext.getTimer();
}
@Override
public Method getMethod() {
return delegateContext.getMethod();
}
@Override
public Constructor<?> getConstructor() {
return delegateContext.getConstructor();
}
@Override
public Object[] getParameters() {
return delegateContext.getParameters();
}
@Override
public void setParameters(Object[] params) {
delegateContext.setParameters(params);
}
@Override
public Map<String, Object> getContextData() {
return delegateContext.getContextData();
}
}
测试责任链效果
新建单元测试:
@Test
public void testChainable() throws Exception {
EchoService echoService = new EchoServiceImpl();
Method method = echoService.getClass().getMethod("echo", String.class);
DelegateInvocationContext delegateInvocationContext = new DelegateInvocationContext(echoService, method, "testChainable");
ChainableInvocationContext invocationContext = new ChainableInvocationContext(delegateInvocationContext, loadInterceptors());
invocationContext.proceed();
}
预期控制台打印顺序为: InterceptorB
->InterceptorC
->InterceptorA
->echo()方法
SPI加载机制是会按
src/main/resources/META-INF/services
目录下新建的com.my.myproject.chainable.AbstractInterceptor
文件里的实现类的顺序加载,所以当加载的资源互相有依赖关系时,尤其要注意加载顺序。
实际控制台输出:
结果符合预期。
动态代理(JDK和CGLIB兼容)
按上述的步骤,我们已经实现了责任链效果,并且需要添加新的拦截功能时,只用在com.my.myproject.chainable.AbstractInterceptor
文件添加新的实现就行,同时我们也能控制拦截器被执行的顺序,
但是,我们调用方法时还是采用硬编码形式,将方法名echo写死在代码里,这不利于我们拓展,也不利于外部调用,所以我们考虑利用动态代理来实现。
这里又会出现一个问题,我们需要代理的可能是interface
也可能是class
,这就需要我们兼容JDK动态代理和Cglib动态代理。
InterceptorEnhancer
首先我们定义代理类的规则,添加接口InterceptorEnhancer
:
public interface InterceptorEnhancer {
default <T> T enhance(T source){
return enhance(source,(Class<? extends T>) source.getClass());
}
default <T> T enhance(T source, Class<? extends T> type) {
return enhance(source,type, Streams.stream(ServiceLoader.load(AbstractInterceptor.class)).toArray());
}
default <T> T enhance(T source, Object... interceptors) {
return enhance(source, (Class<? extends T>) source.getClass(), interceptors);
}
<T> T enhance(T source, Class<? extends T> type, Object... interceptors);
}
- source:被代理对象
- type:被代理对象类型
- interceptors:拦截器数组,利用SPI加载
JDK动态代理实现
InvocationHandlerAdapter
public class InvocationHandlerAdapter implements InvocationHandler {
private final Object target;
private final Object[] interceptors;
public InvocationHandlerAdapter(Object target, Object[] interceptors) {
this.target = target;
this.interceptors = interceptors;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
DelegateInvocationContext delegateInvocationContext = new DelegateInvocationContext(target, method, args);
ChainableInvocationContext invocationContext = new ChainableInvocationContext(delegateInvocationContext, interceptors);
return invocationContext.proceed();
}
}
DynamicProxyInterceptorEnhancer
public class DynamicProxyInterceptorEnhancer implements InterceptorEnhancer {
@Override
public <T> T enhance(T source, Class<? extends T> type, Object... interceptors) {
return (T)Proxy.newProxyInstance(getClassLoader(type),new Class[]{type},new InvocationHandlerAdapter(source,interceptors));
}
private ClassLoader getClassLoader(Class<?> type){
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if(cl == null){
cl = type.getClassLoader();
if(cl == null){
cl = ClassLoader.getSystemClassLoader();
}
}
return cl;
}
}
CGLIB动态代理实现
这里与JDK动态代理不同的是,CGLIB动态代理需要利用方法代理
MethodProxy
来执行被代理类的方法,所以CGLIB动态代理需要在CglibMethodInvocationContext
里执行方法,即proxy.invokeSuper(Object obj, Object[] args)
,与此相反的是JDK动态代理直接在InvocationHandler.invoke(Object proxy, Method method, Object[] args)
里利用原始对象及方法对象执行就行了。
CglibMethodInvocationContext
这里需要对DelegateInvocationContext
包装一层,将代理类传过来执行
public class CglibMethodInvocationContext extends DelegateInvocationContext {
private final MethodProxy proxy;
public CglibMethodInvocationContext(Object target, Method method, MethodProxy proxy, Object... params) {
super(target, method, params);
this.proxy = proxy;
}
@Override
public Object proceed() throws Exception {
try {
return proxy.invokeSuper(getTarget(),getParameters());
} catch (Throwable throwable) {
throw new Exception(throwable);
}
}
}
MethodInvocationAdapter
public class MethodInvocationAdapter implements MethodInterceptor {
private final Object[] interceptors;
public MethodInvocationAdapter(Object[] interceptors) {
this.interceptors = interceptors;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//这里与JDK动态代理不同,需要将代理方法与被代理类传递进去
DelegateInvocationContext delegateInvocationContext = new CglibMethodInvocationContext(obj, method, proxy, args);
ChainableInvocationContext invocationContext = new ChainableInvocationContext(delegateInvocationContext, interceptors);
return invocationContext.proceed();
}
}
CglibProxyInterceptorEnhancer
public class CglibProxyInterceptorEnhancer implements InterceptorEnhancer {
@Override
public <T> T enhance(T source, Class<? extends T> type, Object... interceptors) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(type);
enhancer.setCallback(new MethodInvocationAdapter(interceptors));
return (T) enhancer.create();
}
}
DefaultInterceptorEnhancer
加入一种默认实现,即根据被代理对象是否为接口分别调用JDK动态代理和CGLIB动态代理:
public class DefaultInterceptorEnhancer implements InterceptorEnhancer {
private final InterceptorEnhancer jdkInterceptorEnhancer = new DynamicProxyInterceptorEnhancer();
private final InterceptorEnhancer cglibInterceptorEnhancer = new CglibProxyInterceptorEnhancer();
@Override
public <T> T enhance(T source, Class<? extends T> type, Object... interceptors) {
if (type.isInterface()) {
return jdkInterceptorEnhancer.enhance(source, type, interceptors);
} else {
return cglibInterceptorEnhancer.enhance(source, type, interceptors);
}
}
}
测试效果
添加测试方法:
测试JDK动态代理
@Test
public void testProxy() {
InterceptorEnhancer defaultInterceptorEnhancer = new DefaultInterceptorEnhancer();
EchoService echoService = (EchoService) defaultInterceptorEnhancer.enhance(new EchoServiceImpl(), EchoService.class);
echoService.echo("JDK动态代理执行完成");
}
执行结果:
测试CGLIB动态代理
@Test
public void testProxy() {
InterceptorEnhancer defaultInterceptorEnhancer = new DefaultInterceptorEnhancer();
EchoService echoService = new EchoServiceImpl();
echoService = defaultInterceptorEnhancer.enhance(echoService);
echoService.echo("CGLIB动态代理执行完成");
}
执行结果:
总结
以上就是责任链的实现模式,相似的Filter
也是这种实现方式,只不过JDK将其封装的更加完善与通用。