基础设计模式-03 从过滤器(Filter)校验链学习职责链模式

1.职责链路模式

1.1UML图

1.2 职责链路模式的概念

 为了解耦的任务校验,将校验对象连成一个链,沿着这个链进行访问,直到有一个对象处理位置;

1.3 优点

1.按照一定的顺序执行判断;

2.避免校验对象之间耦合关系;

3.不用担心没有代码没有执行到;

2.职责链路模式在过滤器(Filter)中的使用

1.源码查看

1.ApplicationDispatcher

这段代码总共做了三件事:1.过滤器链创建;2.过滤链逐个过滤;3.释放过滤链资源;

 private void invoke(ServletRequest request, ServletResponse response,
            State state) throws IOException, ServletException {

        //。。。。。。。。前面的代码省略
        // Get the FilterChain Here
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); //创建过滤器校验链条

        // Call the service() method for the allocated servlet instance
        try {
            // for includes/forwards
            if ((servlet != null) && (filterChain != null)) {
               filterChain.doFilter(request, response); //进行过滤器校验
             }
            // Servlet Service Method is called by the FilterChain
        } catch (ClientAbortException e) {
        //。。。。。。。省略中间错误判断代码
    }

        // Release the filter chain (if any) for this request
        try {
            if (filterChain != null)
                filterChain.release();//释放过滤器资源
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            wrapper.getLogger().error(sm.getString("standardWrapper.releaseFilters",
                             wrapper.getName()), e);
            // FIXME: Exception handling needs to be similar to what is in the StandardWrapperValue
        }
       //。。。。。。。。。后面的代码省略
 
    }

2.ApplicationFilterFactory(过滤链条创建过程)

从下面可以看出主要是一下操作:

  1.初始化ApplicatFilterChain 过滤器校验链;

  2.从上下文环境中,获取之前配置的过滤器数据;

  3.将符合URL,serveletName的过滤器配置到ApplicationFilterChain中

 public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // If there is no servlet to execute, return null
        if (servlet == null)
            return null;

        // Create and initialize a filter chain object  初始化链式对象
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
                 filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // Acquire the filter mappings for this Context   获取过滤器配置的上下文
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return filterChain;

        // Acquire the information we will need to match filter mappings
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain   将符合需求的过滤器加入到过滤链中
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Return the completed filter chain
        return filterChain;
    }

3.ApplicationFilterChain(过滤链增加的具体过程)

这个方法比较简单:1.数组扩容;2.增加新的过滤器;

private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];//过滤器存储的实体类
private int pos = 0;//当前过滤位置
private int n = 0;//存储的过滤器的总数
public static final int INCREMENT = 10;
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;

    }

4.ApplicationFilterChain 的doFilter方法

处理过程:

  1.获取pos位置的过滤器;

  2.Filter执行,将当前过滤链对象,作为参数进行传递;

  3.pos过滤器后移1位进行调用,直到pos大于总过滤器位置;

   @Override
    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);
        }
    }
    //实际处理过滤任务的方法
    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];//pos默认是从0开始的,调用后+1
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
                } else {
                    filter.doFilter(request, response, this);//这里是最重要的一点,过滤器将过滤链对象作为一个参数向下传递,从而可以自动的进行链式校验
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
            return;
        }
      //。。。。。。。。。。省略部分代码
      



    }

2.UML图(上面的链式调用的图,如有错误还请指出)

3.手写一个通用校验链

业务需求:前端传来数据,动态SQL拼接,判断SQL各个部分是否数据有问题;

1.定义接口

/**
 * 参数校验锁链管理
 * @param
 * @author lpf
 */
public interface CheckChain{
    public abstract void doCheck(Param param) throws Exception;
}

2.对过滤参数进行约束

public interface Param<T extends Param> {
    public abstract <T> T get();
}

3.定义过滤接口

@Service
public interface CheckFilter<T extends Param> {
    /**
     * 参数校验方法
     * @param chain
     * @return
     */
    public abstract void checkParam(Param<T> param, CheckChain chain) throws Exception;
}

4.默认链式校验实现类

/**
 * 默认链式检查
 */
public class DefaultCheckChain implements CheckChain {
    /**
     *
     */
    private ParamCheckWapper[] wappers = new ParamCheckWapper[0];

    private static final int INCREMENT = 10;

    private int n = 0;

    private int pos = 0;

    //进行链式检查
    @Override
    public void doCheck(Param filed) throws Exception {
        if(pos < n){
            ParamCheckWapper wapper = wappers[pos++];
            CheckFilter paramCheck = wapper.getParamCheck();
            Assert.notNull(paramCheck,"链式类不能为空");
            paramCheck.checkParam(filed,this);
        }
    }

    /**
     * 增加要进行过滤处理的类
     * @param checkWapper
     */
    public void addCheck(ParamCheckWapper checkWapper){

        for(ParamCheckWapper wapper : wappers){
            if(wapper == checkWapper){return;} ;
        }

        if(n == wappers.length){
            ParamCheckWapper[] newWappers = new ParamCheckWapper[n + INCREMENT];
            System.arraycopy(wappers, 0, newWappers, 0, n);
            wappers = newWappers;
        }
        wappers[n++] = checkWapper;

    }

}

5.过滤实现类(可以有多个)

/**
 * select参数校验
 * @author lpf
 * @since 2019-11-08
 */
public class SelectParamCheck implements CheckFilter<CheckParam> {

    /**
     * 参数校验
     * @param param
     * @param chain
     */
    @Override
    public void checkParam(Param<CheckParam> param, CheckChain chain) throws Exception{
        CheckParam checkParam = param.get();
        List<SelectField> selects = checkParam.getSelect();
        List<String> columns = checkParam.getColumnList();
        //对select参数进行校验
        selects.forEach(select -> {
            String filed = select.getFiled().toLowerCase();
            boolean flag = columns.contains(filed);
            if(!flag) throw new RuntimeException(select.getFiled()+"不存在,请刷新页面重新选择查询字段!!!");
        });

    }

6.过滤类注册(可以通过yml配置反射生成,或者通过手动注册)

@Service
public class SearchConfigService {

    /**默认检查链*/
    private static DefaultCheckChain checkChain ;
    /**过滤链路表配置*/
    static{
        checkChain = new DefaultCheckChain();
        //参数检查器
        ParamCheckWapper selectParamCheck = new ParamCheckWapper(new SelectParamCheck(),"SelectParamCheck");
        ParamCheckWapper groupParamCheck = new ParamCheckWapper(new GroupbyParamCheck(), "groupParamCheck");
        ParamCheckWapper conditionParamCheck = new ParamCheckWapper(new ConditionParamCheck(), "conditionParamCheck");
        ParamCheckWapper orderbyParamCheck = new ParamCheckWapper(new OrderbyParamCheck(), "orderbyParamCheck");

        //参数链表增加过滤类
        checkChain.addCheck(selectParamCheck);
        checkChain.addCheck(groupParamCheck);
        checkChain.addCheck(conditionParamCheck);
        checkChain.addCheck(orderbyParamCheck);
    }

    /**
     * 参数校验
     */
    public void doCheck(Param param) throws Exception {
        checkChain.doCheck(param);
    }

  以上,就是职责链路模式的简单使用,可以通过泛型进行代码剥离,后续涉及到链式校验的时候就可以通过限制参数进行多样使用。降低代码的耦合度;

至此,职责链路设计模式的介绍就结束了;