zoukankan      html  css  js  c++  java
  • 基础设计模式-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);
        }
    

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

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

  • 相关阅读:
    在日本被禁止的コンプガチャ設計
    Starling常见问题解决办法
    Flixel引擎学习笔记
    SQLSERVER中修复状态为Suspect的数据库
    T4 (Text Template Transformation Toolkit)实现简单实体代码生成
    创建Linking Server in SQL SERVER 2008
    Linq to Sql 与Linq to Entities 生成的SQL Script与分页实现
    Linq to Entity 的T4 模板生成代码
    在VisualStudio2008 SP1中调试.net framework 源代码
    使用HttpModules实现Asp.net离线应用程序
  • 原文地址:https://www.cnblogs.com/perferect/p/13296522.html
Copyright © 2011-2022 走看看