责任链模式-实现和Servlet、Spring Aop、Zuul中的责任链模式分析

设计模式-总览icon-default.png?t=LBL2https://mp.csdn.net/mp_blog/creation/editor/122202507目录

一、责任链实现

二、Servlet、Spring Aop、Zuul中的责任链模式分析

1、Servlet中的Filter

2、Zuul中的责任链模式分析

1)、FilterFileManager

2)、ZuulServlet

3)、Spring Aop责任链模式


   责任链模式定义:使多个对象都有处理请求的机会,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象串成一条链,并沿着这条链一直传递该请求,直到有对象处理它为止。有两个角色:

抽象任务角色:1)、当前执行者、下一个任务者的链接(可以调用下一个任务方法)

具体任务角色:一大堆抽象任务角色的实现类

    个人理解:类似filter等使用责任链模式,或者一大堆的if else if... 等,有一大堆同一类(或者同级的事)需要处理,也就是上面说的具体任务角色。使用责任链模式,每个请求(或者调用)的时候肯定不是把一大堆都全部执行,那么就有两种方式。第一、把所有任务角色组成责任链模式,每个请求进来全部去走一遍,要处理就处理,不处理就调用下一个进行处理。 第二、每个请求把自己会执行的具体任务觉得挑选起来组成责任链,然后只管调用,每个处理完之后就调用下一个。

    当然责任链有几个变种的方式,一个是抽象每一个具体的任务角色,在具体的实现类中再去实现,还有就是在抽象任务角色中维护一个列表(数组或者链表),调用的时候将当前对象this传递进行。

一、责任链实现

1)、先定义责任链抽象

public abstract class ChainHandler {
    /**
     *  下一个处理者
     */
    private ChainHandler nextHandler;

    public ChainHandler() {
    }

    public ChainHandler(ChainHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    /**
     *  当前处理任务
     */
    protected abstract void handle();

    public ChainHandler getNextHandler() {
        return nextHandler;
    }
}

2)、责任链抽象实现(一系列)

public class AChainHandler extends ChainHandler {

    public AChainHandler() {
    }

    public AChainHandler(ChainHandler nextHandler) {
        super(nextHandler);
    }

    @Override
    protected void handle() {
        System.out.println("我执行了方法A!");
        if (getNextHandler() != null) {
            System.out.println("----- 我呼叫了下一个任务!");
            getNextHandler().handle();
        }
    }
}
public class BChainHandler extends ChainHandler {

    public BChainHandler() {
    }

    public BChainHandler(ChainHandler nextHandler) {
        super(nextHandler);
    }

    @Override
    protected void handle() {
        System.out.println("我执行了方法B!");
        if (getNextHandler() != null) {
            System.out.println("----- 我呼叫了下一个任务!");
            getNextHandler().handle();
        }
    }
}

3)、测试责任链实现

public class ChainHandlerTest {

    public static void main(String[] args) {
        // 任务一放入链
        AChainHandler a = new AChainHandler();
        // 任务二放入链
        BChainHandler b = new BChainHandler(a);
        // 再放一个任务一到链中
        AChainHandler a1 = new AChainHandler(b);
        // 执行任务
        a1.handle();
    }
}

执行结果:

我执行了方法A!
----- 我呼叫了下一个任务!
我执行了方法B!
----- 我呼叫了下一个任务!
我执行了方法A!

二、Servlet、Spring Aop、Zuul中的责任链模式分析

责任链模式的应用场景,还是直接分析别人是怎么写的吧。

  1. servlet中的Filter
  2. zuul中的Filter
  3. dubbo中的Filter
  4. mybatis中的Plugin

1、Servlet中的Filter

定义接口为:FilterChain,里面维护的是Filter,只定义了一个接口。刚开始在想为什么不定义一个抽象就可以了。后面看了一下FIlter本身也是一个非常庞大的体系,并且抽象了三个接口需要自己的子类实现。

public interface Filter {

    public default void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

public interface FilterChain {

    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;
}

FilterChain的只有ApplicationFilterChain是在Servlet规范中实现的,其他的都是Spring实现的。所以就分析ApplicationFilterChain和测试框架的MockFilterChain。

1)、ApplicationFilterChain

其中维护了一个调用链,并不是Filter数组。

private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

有一个添加ApplicationFilterConfig的方法:

void addFilter(ApplicationFilterConfig filterConfig) {

    // Prevent the same filter being added multiple times
    for(ApplicationFilterConfig filter:filters)
        if(filter==filterConfig)
            return;

    if (n == filters.length) {
        ApplicationFilterConfig[] newFilters =
                new ApplicationFilterConfig[n + INCREMENT];
        System.arraycopy(filters, 0, newFilters, 0, n);
        filters = newFilters;
    }
    filters[n++] = filterConfig;
}

其实Filter是ApplicationFilterConfig内部的一个字段,并且提供了getFilter方法来或者当前的Filter。其构造方法如下:

ApplicationFilterConfig(Context context, FilterDef filterDef)
        throws ClassCastException, ReflectiveOperationException, ServletException,
        NamingException, IllegalArgumentException, SecurityException {

    super();

    this.context = context;
    this.filterDef = filterDef;
    // Allocate a new filter instance if necessary
    if (filterDef.getFilter() == null) {
        getFilter();
    } else {
        this.filter = filterDef.getFilter();
        context.getInstanceManager().newInstance(filter);
        initFilter();
    }
}

那么ApplicationFilterConfig与Filter是一对一的关系,即ApplicationFilterChain也相当于维护了一个Filter数组。

再看看ApplicationFilterChain的doFilter方法:

public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

    if( Globals.IS_SECURITY_ENABLED ) {
        final ServletRequest req = request;
        final ServletResponse res = response;
        try {
            java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction<Void>() {
                        @Override
                        public Void run()
                                throws ServletException, IOException {
                            internalDoFilter(req,res);
                            return null;
                        }
                    }
            );
        } catch( PrivilegedActionException pe) {
            Exception e = pe.getException();
            if (e instanceof ServletException)
                throw (ServletException) e;
            else if (e instanceof IOException)
                throw (IOException) e;
            else if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            else
                throw new ServletException(e.getMessage(), e);
        }
    } else {
        internalDoFilter(request,response);
    }
}

执行方法在internalDoFilter(request,response);中:

重要的是pos++(pos为当前的位置),即获取到了下一个Filter,后面执行了下一个Filter的doFilter方法。

2、Zuul中的责任链模式分析

    在分析之前需要知道,Spring Cloud Zuul中Filter的大致情况,可以参见:Spring Cloud Zuul的Api网关实现Spring Cloud Zuul详细说明

其中用到了大量的单利模式,但是先不考虑这个。两个关键的类是 ZuulServlet 和 FilterFileManager,太多的细节不用进行考虑,因为这里是研究责任链模式。

1)、FilterFileManager

没有找到FilterFileManager的init方法在哪里初始化的,传入了轮训间隔时间和一个文件目录,进行初始化:

public static void init(int pollingIntervalSeconds, String... directories) 
    throws Exception, IllegalAccessException, InstantiationException {

    if (INSTANCE == null) {
        INSTANCE = new FilterFileManager();
    }

    INSTANCE.aDirectories = directories;
    INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds;
    INSTANCE.manageFiles();
    INSTANCE.startPoller();
}

但是在manageFiles方法中对目录下的文件进行了获取,并且将其注册到FilterLoader(同样是单利模式)中,后续会使用。

void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
    List<File> aFiles = this.getFiles();
    this.processGroovyFiles(aFiles);
}
void processGroovyFiles(List<File> aFiles) throws Exception, 
    InstantiationException, IllegalAccessException {

    Iterator var2 = aFiles.iterator();
    while(var2.hasNext()) {
        File file = (File)var2.next();
        FilterLoader.getInstance().putFilter(file);
    }

}

FilterLoader继承自ConcurrentHashMap的结构如下:

public class FilterLoader {
    static final FilterLoader INSTANCE = new FilterLoader();
    private static final Logger LOG = LoggerFactory.getLogger(FilterLoader.class);
    private final ConcurrentHashMap<String, Long> filterClassLastModified = new ConcurrentHashMap();
    private final ConcurrentHashMap<String, String> filterClassCode = new ConcurrentHashMap();
    private final ConcurrentHashMap<String, String> filterCheck = new ConcurrentHashMap();
    private final ConcurrentHashMap<String, List<ZuulFilter>> hashFiltersByType = new ConcurrentHashMap();
    private FilterRegistry filterRegistry = FilterRegistry.instance();
    static DynamicCodeCompiler COMPILER;
    static FilterFactory FILTER_FACTORY = new DefaultFilterFactory();

    public FilterLoader() {
    }

    public void setCompiler(DynamicCodeCompiler compiler) {
        COMPILER = compiler;
    }

    public void setFilterRegistry(FilterRegistry r) {
        this.filterRegistry = r;
    }

    public void setFilterFactory(FilterFactory factory) {
        FILTER_FACTORY = factory;
    }

    public static FilterLoader getInstance() {
        return INSTANCE;
    }

    public ZuulFilter getFilter(String sCode, String sName) throws Exception {
        if (this.filterCheck.get(sName) == null) {
            this.filterCheck.putIfAbsent(sName, sName);
            if (!sCode.equals(this.filterClassCode.get(sName))) {
                LOG.info("reloading code " + sName);
                this.filterRegistry.remove(sName);
            }
        }

        ZuulFilter filter = this.filterRegistry.get(sName);
        if (filter == null) {
            Class clazz = COMPILER.compile(sCode, sName);
            if (!Modifier.isAbstract(clazz.getModifiers())) {
                filter = FILTER_FACTORY.newInstance(clazz);
            }
        }

        return filter;
    }

    public int filterInstanceMapSize() {
        return this.filterRegistry.size();
    }

    public boolean putFilter(File file) throws Exception {
        String sName = file.getAbsolutePath() + file.getName();
        if (this.filterClassLastModified.get(sName) != null && file.lastModified() != (Long)this.filterClassLastModified.get(sName)) {
            LOG.debug("reloading filter " + sName);
            this.filterRegistry.remove(sName);
        }

        ZuulFilter filter = this.filterRegistry.get(sName);
        if (filter == null) {
            Class clazz = COMPILER.compile(file);
            if (!Modifier.isAbstract(clazz.getModifiers())) {
                filter = FILTER_FACTORY.newInstance(clazz);
                List<ZuulFilter> list = (List)this.hashFiltersByType.get(filter.filterType());
                if (list != null) {
                    this.hashFiltersByType.remove(filter.filterType());
                }

                this.filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);
                this.filterClassLastModified.put(sName, file.lastModified());
                return true;
            }
        }

        return false;
    }

    public List<ZuulFilter> getFiltersByType(String filterType) {
        List<ZuulFilter> list = (List)this.hashFiltersByType.get(filterType);
        if (list != null) {
            return list;
        } else {
            List<ZuulFilter> list = new ArrayList();
            Collection<ZuulFilter> filters = this.filterRegistry.getAllFilters();
            Iterator iterator = filters.iterator();

            while(iterator.hasNext()) {
                ZuulFilter filter = (ZuulFilter)iterator.next();
                if (filter.filterType().equals(filterType)) {
                    list.add(filter);
                }
            }

            Collections.sort(list);
            this.hashFiltersByType.putIfAbsent(filterType, list);
            return list;
        }
    }
}

putFileter就是根据一个名词,从private FilterRegistry filterRegistry = FilterRegistry.instance();中获取出来就是一个Filter,或者使用反射进行实例化。

2)、ZuulServlet

很显然是一个Servlet,重要的两个方法就是init和service方法,

  • 1、初始化的时候会调用init方法
public void init(ServletConfig config) throws ServletException {
    super.init(config);
    String bufferReqsStr = config.getInitParameter("buffer-requests");
    boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true");
    this.zuulRunner = new ZuulRunner(bufferReqs);
}

初始化了内部对象,ZuulRunner,其结构比较复杂后续再看。

  • 2、容器请求进来会调用service方法
public void service(ServletRequest servletRequest, ServletResponse servletResponse) 
    throws ServletException, IOException {

    try {
        this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
        RequestContext context = RequestContext.getCurrentContext();
        context.setZuulEngineRan();

        try {
            this.preRoute();
        } catch (ZuulException var13) {
            this.error(var13);
            this.postRoute();
            return;
        }

        try {
            this.route();
        } catch (ZuulException var12) {
            this.error(var12);
            this.postRoute();
            return;
        }

        try {
            this.postRoute();
        } catch (ZuulException var11) {
            this.error(var11);
        }
    } catch (Throwable var14) {
        this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + 
            var14.getClass().getName()));
    } finally {
        RequestContext.getCurrentContext().unset();
    }
}

void postRoute() throws ZuulException {
    this.zuulRunner.postRoute();
}

void route() throws ZuulException {
    this.zuulRunner.route();
}

void preRoute() throws ZuulException {
    this.zuulRunner.preRoute();
}

void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
    this.zuulRunner.init(servletRequest, servletResponse);
}

void error(ZuulException e) {
    RequestContext.getCurrentContext().setThrowable(e);
    this.zuulRunner.error();
}

执行顺序:preRoute方法、route方法、postRoute方法,但是只要异常都会执行error方法。很有意思的是,只要任何一步有异常error路由就会执行;不管怎么样postRoute都会执行。

每一个步骤执行的时候过程都大致一样,就分析preRoute类型。

this.zuulRunner.preRoute();
public void preRoute() throws ZuulException {
    FilterProcessor.getInstance().preRoute();
}
public void preRoute() throws ZuulException {
    try {
        this.runFilters("pre");
    } catch (ZuulException var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new ZuulException(var3, 500,
           "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
    }
}
public Object runFilters(String sType) throws Throwable {
    if (RequestContext.getCurrentContext().debugRouting()) {
        Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
    }

    boolean bResult = false;
    List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
    if (list != null) {
        for(int i = 0; i < list.size(); ++i) {
            ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
            Object result = this.processZuulFilter(zuulFilter);
            if (result != null && result instanceof Boolean) {
                bResult |= (Boolean)result;
            }
        }
    }
    return bResult;
}

最后还是根据类型,在之前初始化的FilterLoader中进行获取的,获取到了之后就for循环全部执行一遍,调用processZuulFilter方法。个人理解不完全是责任链吧,要是这样的化,我之前写过很多动态配置到一个lIst中,执行的时候从List中拿到任务进行执行,都算责任链了?但是网上很多都说这就是。

3)、Spring Aop责任链模式

    Spring Aop实现时需要先解析增强(Advice),最好封装到ProxyFactory中。当从Spring容器中getBean的时候获取到的其实是代理对象,再代用切入点方法的时候,则会调用JdkDynamicAopProxy的invoke方法,或者CglibAopProxy的intercept方法。更多细节可以参考:SpringAop源码-EnableAspectJAutoProxy实现原理(调用)- invoke或者intercept(代理方法执行)

其内部实现方式都一样

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
 
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;
 
    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
 
        Object retVal;
 
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
 
        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
 
        // Get the interception chain for this method.
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 
        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            retVal = invocation.proceed();
        }
 
        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

先从ProxyFactory中获取到 获取到一个List<Object>  chain其实是InterceptorAndDynamicMethodMatcher类型。

再调用ReflectiveMethodInvocation的proceed方法:

@Override
@Nullable
public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

执行当前interceptor,传入this。知道责任链执行完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值