项目重构记录(一):动态代理+责任链实现拦截器

前言

今天依然要重构小破司云平台部分业务,经过困难重重的调试,需求分析,并且考虑到每个业务模块都有类似的拦截需求,以及需求经常修改包括添加新的拦截需求,所以利用动态代理以及责任链的形式,重构这一部分功能,利用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个拦截器,分别为InterceptorAInterceptorBInterceptorC,以及拦截器的抽象实现AbstractInterceptor

为了方便后续注解的拓展,以及责任链的实现,拦截器入参为javax.interceptor.InvocationContext,其原理类似ServletFilter的实现。

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将其封装的更加完善与通用。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唔知取咩名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值