SpringBoot2:web开发常用功能实现及原理解析-监听器(Listener)、过滤器(Filter)、拦截器(Interceptor)、Servlet、AOP

一、学习目标

1、它们的区别是什么?适用的场景是怎样的?它们的执行顺序怎样的?
2、它们在springboot中怎么开发?
3、简单了解一下拦截器底层代码的执行原理。

二、过滤器(Filter)

1、简单说明

Filter过滤器是Servlet容器层面的,在实现上基于函数回调,可以对几乎所有请求进行过滤。过滤器是对数据进行过滤,预处理过程,当我们访问网站时,有时候会发布一些敏感信息,发完以后有的会用*替代,还有就是登陆权限控制等,一个资源,没有经过授权,肯定是不能让用户随便访问的,这个时候,也可以用到过滤器。
过滤器的功能还有很多,例如实现URL级别的权限控制、压缩响应信息、编码格式等等。

SpringBoot实现过滤器,常见有三种方式,越复杂功能越强大。

2、代码案例

3、源码探究

三、监听器(Listener)

1、简单说明

Listener监听器也是Servlet层面的,可以用于监听Web应用中某些对象、信息的创建、销毁和修改等动作发生,然后做出相应的响应处理。根据监听对象,将监听器分为3类:

ServletContext:对应application,实现接口ServletContextListener。在整个Web服务中只有一个,在Web服务关闭时销毁。可用于做数据缓存,例如结合redis,在Web服务创建时从数据库拉取数据到缓存服务器。

HttpSession:对应session会话,实现接口HttpSessionListener。在会话起始时创建,一端关闭会话后销毁。可用作获取在线用户数量

ServletRequest:对应request,实现接口ServletRequestListenerrequest对象是客户发送请求时创建的,用于封装请求数据,请求处理完毕后销毁。可用作封装用户信息。

在写Listener的类时,实现方式和Filter一样,同样有两种实现方式。
一种是只加@Component
另一种是 @WebListener 和 @ServletComponentScan 配合使用。
不过实现接口则根据监听对象区分,如:ServletContextListenerHttpSessionListenerServletRequestListener

2、代码案例

3、源码探究

四、拦截器(Interceptor)

1、简单说明

Interceptor拦截器和Filter和Listener有本质上的不同,前面二者都是依赖于Servlet容器,而Interceptor则是依赖于Spring框架,是aop的一种实现,基于Java动态代理实现的。在SpringBoot中实现拦截器的方式,有点类似于实现过滤器的第三种方式,所以要通过下面两个步骤。

声明拦截器的类:通过实现 HandlerInterceptor接口,实现preHandle、postHandle和afterCompletion方法。
通过配置类配置拦截器:通过实现WebMvcConfigurer接口,实现addInterceptors方法。

一个springboot工程中,可以有多个拦截器。

2、代码案例

拦截器接口规范
在这里插入图片描述
1、声明拦截器

/**
 * 登录检查
 * 1、配置好拦截器要拦截哪些请求
 * 2、把这些配置放在容器中
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("preHandle拦截的请求路径是{}",requestURI);

        //登录检查逻辑
        HttpSession session = request.getSession();

        Object loginUser = session.getAttribute("loginUser");

        if(loginUser != null){
            //放行
            return true;
        }

        //拦截住。未登录。跳转到登录页
        request.setAttribute("msg","请先登录");
//        re.sendRedirect("/");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行{}",modelAndView);
    }

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}",ex);
    }
}

2、配置拦截器

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
    }
}

3、springmvc实现方式
声明拦截器,是一样的。
配置拦截器,是在spring-mvc.xml中配置的。

	<mvc:interceptors>
		<mvc:interceptor>
<!--			匹配的是url路径, 如果不配置或/**,将拦截所有的Controller-->
			<mvc:mapping path="/**" />
<!--			/register 和 /login 不需要拦截-->
			<mvc:exclude-mapping path="/User/login.do" />
			<bean class="com.kfc.interceptor.TokenInterceptor"></bean>
		</mvc:interceptor>
<!--		当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法-->
	</mvc:interceptors>

4、springboot配置多个拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor01()).addPathPatterns("/**").excludePathPatterns("/templates/**").order(2);
        registry.addInterceptor(new MyInterceptor02()).addPathPatterns("/**").excludePathPatterns("/templates/**").order(1);
    }
}

那么,配置多个拦截器的执行顺序是:
order越小,越先执行,否则按照register注册的顺序执行

3、源码探究

springboot的所有请求,从org.springframework.web.servlet.DispatcherServlet#doDispatch方法入手,探究拦截器原理,也不例外。

经过前面的学习,我们知道,请求进入doDispatch后,先根据HandlerMapping找到对应的处理请求的controller方法。
在这里插入图片描述
mappedHandler中,我们可以看到所有的拦截器集合
在这里插入图片描述
接下来,会根据handler查找对应的handlerAdapter
在用handlerAdapter去执行controller方法。
而在执行方法前,springboot会先执行拦截器链的preHandle方法。
在这里插入图片描述
会看到,先调用的applyPreHandle,然后在调用handlerAdapterhandle方法。
所有拦截器的preHandle方法正常执行返回true后,才执行controller方法,否则,直接return,进入finally块结束调用。

进入applyPreHandle方法,看看具体逻辑
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle和triggerAfterCompletion
这里主要是,循环调用所有的拦截器的preHandle方法。如果,有返回false的,则触发afterCompletion方法,afterCompletion方法是倒序调用的。
在这里插入图片描述
在这里插入图片描述
当正常执行preHandle方法后,会执行controller方法,然后,倒序执行拦截器的postHandle
在这里插入图片描述
postHandle执行完后,执行response响应。
在这里插入图片描述
进入processDispatchResult方法内部
在方法的最下面,依旧执行了拦截器的afterCompletion方法。
在这里插入图片描述
到此,一个正常的请求结束。
如果在执行了preHandle一切正常,但是,在controller方法内,发生了异常。
那么会进入doDispatch方法的catch异常捕获环节,在catch中执行拦截器的afterCompletion方法。
在这里插入图片描述

4、源码逻辑梳理

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法
● 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
● 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion

五、AOP

1、简单说明

2、代码案例

3、源码探究

六、Servlet

1、简单说明

2、代码案例

3、源码探究

七、总结

1、范围
在这里插入图片描述
2、执行顺序
它们的执行顺序如下(@ControllerAdvice本文暂不做介绍):
拦截顺序:ServletContextListener> Filter > Interception > AOP > 具体执行的Controller方法 > AOP > @ControllerAdvice > Interception > Filter > ServletContextListener
在这里插入图片描述
3、实现原理
根据实现原理分成下面两大类:
FilterListener:依赖Servlet容器,基于函数回调实现。可以拦截所有请求,覆盖范围更广,但无法获取IOC容器中的Bean
InterceptorAOP :依赖Spring框架,基于Java反射和动态代理实现。只能拦截Controller的请求,可以获取IOC容器中的Bean

Filter -> Interceptor -> aop ,拦截的功能越来越细致、强大,尤其是Interceptor和aop可以更好的结合spring框架的上下文进行开发。但是拦截顺序也是越来越靠后,请求是先进入Servlet容器的,越早的过滤和拦截对系统性能的消耗越少。具体选用哪种方法,就需要开发人员根据实际业务情况综合考虑了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值