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几乎一样,就不贴出来了

posted @ 2020-05-12 16:46  断腿三郎  阅读(341)  评论(0编辑  收藏  举报