• JavaWeb 中 过滤器和监听器,拦截器


    原生的JAVA WEB 也是一个框架呀

    过滤器

    过滤器是个啥我就不说了
    说一下我的测试
    首先写好Filter的实现,在web.xml注册好,然后启动
    预想的执行顺序是:
    浏览器 -> 过滤器 -> Servlet -> jsp -> 过滤器 -> 浏览器
    但是实际上,访问到jsp之后,没有经过过滤器,便直接发送到了浏览器
    不知道是不是我姿势不对

    但是这都无关紧要,记住就行了
    要想搞清楚Java Web的原理太难了,所以还是只简单瞟一眼就好了

    首先我写的doFilter:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("MyFilter 1 read request message : " + request.getAttribute("message"));
        chain.doFilter(request,response);
    }
    

    那这个chain是个什么东西,多个filter怎么依次执行,看来我们需要进入chain.doFilter里面看一下了

    进入这个函数,首先会判断安全性选项是否开启,目前我也不知道它是干嘛的,但是在默认情况下,它没有开启,于是我们忽略它,于是函数就是这个样子的:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
    
        if( Globals.IS_SECURITY_ENABLED ) {
            /*
            被忽略的部分
             */
        } else {
            internalDoFilter(request,response);
        }
    }
    

    在internalDoFilter这个方法里面东西也蛮多的,我就不全看了,把默认情况下会执行的代码抠出来分析一下吧:
    注意这只是很小一部分

    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            Filter filter = filterConfig.getFilter();
            filter.doFilter(request, response, this);
        }
        return;
    }
    servlet.service(request, response);
    

    第一行是判断还有没有Filter
    第二行是取出当前的ApplicationFilterConfig
    第四行是从ApplicationFilterConfig取出filter
    从这3行可以看出,配置了多个filter之后,javaweb会将其放在一个ApplicationFilterConfig数组内,然后从ApplicationFilterConfig取出Filter
    ApplicationFilterConfig是一个存放Filter相关信息的类,我们只关心其中有一个属性存放的是Filter,从getFilter方法中可以获取Filter
    等到所有的Filter执行完后,就开始执行servlet的service方法了
    值得一提的是,这句话会被调用两次,一次是调用我们指定的servlet,另外一个是调用JspServlet,返回视图。这里的方法栈把我搞懵了,debug半天也没看见调用JspServlet是从哪里开始的

    另外,经过我的测试,就算没有指定过滤器,也会进入internalDoFilter这个方法,不过不会进入循环了,而是直接调用servlet
    这里用到了大量的模板方法设计模式,使得开发web项目的程序员只需要关注业务本身就行了

    监听器

    这个东西实际上使用了模板方法模式和监听器模式
    通过一系列的定位,我们可以找到jsp内置对象的实例所属的类:StandardSession
    在这个类中有非常常用的setAttribute方法,挑几句重点:
    (当然其中还有一些其他的判断被我省略了)

    HttpSessionAttributeListener listener =(HttpSessionAttributeListener) listeners[i];
    listener.attributeAdded(event);
    

    可以看出,监听器之所以能工作,是因为javaweb在相关方法执行时,就调用了监听器实现定义好的方法
    这就是模板方法设计模式,因为不是通过反射实现的,所以想自己另起炉灶搞几个监听器是不可能的,只能实现别人定义好的接口或者继承别人的类了

    不过,这个event究竟是个啥,我们再来探索一番:

    event = new HttpSessionBindingEvent(getSession(), name, value);
    

    在执行listener.attributeAdded(event)之前,还有,event被初始化,实际上就是把session和setAttribute()的参数封装起来
    实际上这个被封装的session就是自己,但是没有使用this,因为在封装进event之前,还封装了一层

    facade = new StandardSessionFacade(this);
    

    这个facade就是getSession返回的结果

    区别

    这两个东西的区别就在于
    javaweb在执行某个操作时,先给过滤器看,过滤器修改放行了,javaweb才去执行,执行完之后再告诉监听器
    过滤器只能操作request和session,监听器从设计上来说应该啥都能干,但是目前我只发现能管理request和session的

    拦截器

    这东西不是原生的javaweb定义的,而是springMVC里的内容
    他和拦截器起到的作用是相差无几的
    在springMVC中有一个接口:HandlerInterceptor,这个接口中有三个方法:

    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    			@Nullable ModelAndView modelAndView)
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
    			@Nullable Exception ex)
    

    这三个方法分别在:controller方法执行前,页面解析之后,前两者完成之后
    其中preHandle的返回值为,是否放行
    我们发现与Filter相比,HandlerInterceptor的方法更加细化了,传递进来的参数也更多了
    这也许就是二者的差别了吧

    在controller方法运行之前,springMVC会执行以下方法

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //或取配置好的拦截器
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                //执行每个拦截器的preHandle方法
                if (!interceptor.preHandle(request, response, this.handler)) {
                      //有任意一个拦截器不放行,立即执行afterCompletion方法
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }
    

    这个方法返回false,会导致DispatchServlet的doDispatch方法直接返回,不会执行controller和视图渲染
    从这个方法我们可以看到,拦截器和过滤器放行请求的方式还是有所不同的,拦截器不放行需要返回false,而过滤器只需要啥也不干就行了

    在视图渲染完成之后,会执行以下方法
    注意这个方法的循环
    从记录的preHandle执行位置,逆着执行

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
            throws Exception {
    
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = this.interceptorIndex; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                }
                catch (Throwable ex2) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
                }
            }
        }
    }
    

    在doDispatch方法最后,会执行triggerAfterCompletion方法,这个方法和triggerAfterCompletion几乎一样,就不贴出来了

  • 相关阅读:
    N95当手柄玩游戏,甩起来还可以用手势控制电脑,分享一下我们的设计经验
    这个回答真逗
    基于事件通信的轻量级MVP框架实现,附源码
    敏捷开发读书笔记
    开发Access数据库提示的"标准表达式中数据类型不匹配",DateTime类型解决办法
    解决mysql表已满的错误
    我的OO实践由GPS消息处理抽象出一通用命令处理类
    收到开Windows 7 party的资源了【无图无真相】
    一)我要做什么,Petshop 源码分析
    天津大学免费上网,IPV4及IPV6同时共享的解决方案
  • 原文地址:https://www.cnblogs.com/ZGQblogs/p/12877156.html
Copyright © 2020-2023  润新知