1、监听器

1.1 什么是监听器

监听器是 Servlet 规范中定义的一种特殊类

  • 用来监听 ServletContext、HttpSession 和 ServletRequest 等域对象的创建和销毁事件
  • 用来监听域对象的属性发生修改的事件
  • 可以在事件发生前、发生后做一些必要的处理

image-20220731010309478

既可以使用 web.xml 进行注册,也可以使用 @WebListener 注解来进行注册

1.2 监听器的用途

  1. 统计在线人数和用户
  2. 系统启动的时候加载初始化信息
  3. 统计网站的访问量
  4. 和 Spring 相结合

1.3 监听器的分类

Servlet 规范中定义了 9 个监听器接口,可以用来监听 ServletContext、HttpSession 和 ServletRequest 对象的生命周期和属性变化事件

监听器按照监听的事件可以分为 3 大类:

  1. 监听对象创建和销毁的监听器
  2. 监听对象中属性变更的监听器
  3. 监听 HttpSession 中的对象状态改变的监听器

1.3.1 监听对象创建和销毁的监听器

image-20220731010914856

1.3.2 监听对象中属性变更的监听器

image-20220731010931362

1.3.3 监听 HttpSession 中的对象状态改变的监听器

image-20220731012039956

注意:如果监听器是 HttpSessionBindingLinstener,只有实现了这个监听器接口的对象存储到 Session 域的时候,才会触发绑定事件,但是一个类即使没有实现 HttpSessionAttributeListener,那么当这个类的对象存储到 Session 域的时候,也是会触发事件

1.4 监听器的启动顺序

image-20220731012248521

1.5 四种状态

ServletContext,HttpSession,ServletRequest 三个域对象的属性变化(getAttribute,setAttribute,removeAttribute)对象感知监听器,对象需要实现接口,不需要配置 xml

一个对象必须要实现了 Serializable 接口,才能被存储到磁盘上

  • 绑定状态:就一个对象被放到 session 域中
  • 解绑状态:就是这个对象从 session 域中移除了
  • 钝化状态:是将 session 内存中的对象持久化(序列化)到磁盘
  • 活化状态:就是将磁盘上的对象再次恢复到 session 内存中

2、过滤器

2.1 什么是过滤器

对事物进行过滤的,对请求进行过滤。使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。如登录控制,权限管理,过滤敏感词汇等

2.2 使用方式

2.2.1 使用注解 @WebFilter

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
    String description() default "";

    String displayName() default ""; // filter显示名称

    WebInitParam[] initParams() default {};

    String filterName() default ""; // 该filter的名字

    String smallIcon() default "";

    String largeIcon() default "";

    String[] servletNames() default {}; // 指定对哪些servlet进行过滤

    String[] value() default {}; // 指定拦截路径

    String[] urlPatterns() default {}; // 指定拦截路径

    DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};

    boolean asyncSupported() default false; // 是否支持异步模式
}

urlPatterns 和 value 是一样的。urlPatterns 和 value 只能配置一个,不能两个都配置,两个都配置就会报错

2.2.2 使用 web.xml 方式

<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>com.pendulumye.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2.3 过滤器生命周期

Filter 有 3 个阶段,分别是初始化,拦截和过滤,销毁。

  • 初始化阶段:当服务器启动时,我们的服务器(Tomcat)就会读取配置文件,扫描注解,然后来创建我们的 Filter。
  • 拦截和过滤阶段:只要请求资源的路径和拦截的路径相同,那么过滤器就会对请求进行过滤,这个阶段在服务器运行过程中会一直循环。
  • 销毁阶段:当服务器(Tomcat)关闭时,服务器创建的 Filter 也会随之销毁

Filter 的三个阶段就对应着 Filter 的 3 个方法:

  • init 方法会在 Filter 创建时调用
  • doFilter 方法会在请求和拦截匹配时调用
  • destroy 方法会在 Filter 销毁时调用

2.4 过滤器执行顺序

在我们的请求到达 Servle 之间是可以经过多个 Filter 的,一般来说,建议 Filter 之间不要有关联,各自处理各自的逻辑即可。这样,我们也无需关心执行顺序问题。如果一定要确保执行顺序,就要对配置进行修改了,执行顺序如下:

  1. 在 web.xml 中,filter 执行顺序跟声明的顺序有关,先声明的先执行
  2. 使用注解配置的话,filter 的执行顺序跟名称的字母顺序有关,例如:AFilter 会比 BFilter 先执行
  3. 如果既有在 web.xml 中声明的 Filter,也有通过注解配置的 Filter,那么会优先执行 web.xml 中配置的 Filter

image-20220720122649689

多个Filter过滤器同时执行的时候,他们都要使用同一个Request对象

2.5 使用案例

一个评论页面,可以进行评论,如果评论中含有我们定义的敏感词汇,那么我们就进行过滤,使用**来进行代替

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>评论</title>
</head>
<body>
<h1>输入评论内容</h1>
<form action="${pageContext.request.contextPath}/comment" method="post">
    <textarea name="message" cols="30" rows="10"></textarea>
    <input type="submit" value="提交">
</form>
<p >${requestScope.get("name")}<span style="color: red">${requestScope.get("comment")}</span></p>
</body>
</html>

@WebFilter(servletNames = {"comment"},initParams = {@WebInitParam(name = "sensitiveWord", value = "zz")})
public class CommentFilter implements Filter {

    private List<String> sensitiveWords = new ArrayList<>();
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //得到敏感词汇
        String word = filterConfig.getInitParameter("sensitiveWord");
        //加入集合
        sensitiveWords.add(word);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //设置编码
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html;charset=utf-8");
        //得到评论
        String message = servletRequest.getParameter("message");
        for (String sensitiveWord : sensitiveWords) {
            //对所有敏感词汇进行过滤
            if (message.contains(sensitiveWord)){
                //替换敏感词汇
                message = message.replace(sensitiveWord, "**");
            }
        }
        //存入request域
        servletRequest.setAttribute("comment",message);
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
    }
}

@WebServlet(name = "comment",value = "/comment")
public class CommentServlet extends HttpServlet {

    //记录评论敏感词汇的ip
    private HashSet<String> hashSet = new HashSet<>();

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String message = request.getParameter("message");
        String comment = (String) request.getAttribute("comment");
        if (message.equals(comment)){
            System.out.println("没有敏感词汇.....");
            //设置名字
            request.setAttribute("name","good boy:");
        }else {
            //有敏感词汇,记录IP
            String localAddr = request.getLocalAddr();
            System.out.println(localAddr);
            hashSet.add(localAddr);
            //设置名字
            request.setAttribute("name","bad boy:");
        }
        //转发到comment.jsp页面
        request.getRequestDispatcher("/comment.jsp").forward(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }
}

3、拦截器

3.1 什么是拦截器

拦截器(Interceptor)是一种动态拦截方法调用的机制,在 SpringMVC 中动态拦截控制器方法的执行

3.2 使用方式

3.2.1 使用 AOP 切面功能

image-20220731131406661

3.2.2 使用 Spring 的拦截器相关接口

实现拦截器一般需要以下几个步骤:

  1. 实现 WebMvcConfigurer 接口,重写 addCorsMappings() 方法和 addInterceptors() 方法【配置拦截器】
  2. 实现 HandlerInterceptor 接口或者继承 HandlerInterceptorAdapter,重写 preHandle() 方法【自定义拦截器】

简单介绍一下WebMvcConfigurer中比较重要的几个方法:

  • addInterceptors:添加拦截器
  • addCorsMappings:跨域
  • addViewControllers:页面跳转(不用像现在我们要写一个 Controller 进行映射就可实现跳转)
  • addResourceHandlers:静态资源(自定义静态资源映射目录)
  • configureDefaultServletHandling:默认静态资源处理器
  • configureViewResolvers:视图解析器(配置请求视图映射,配置了以后我们返回一个页面路径的字符串时,解析器会帮我们拼装前缀和后缀等信息)
  • configureMessageConverters:信息转换器(比如我们入参的信息直接转换成 json 或者转换成对应的 bean 类就具体在这里配置)

3.3 拦截器执行流程

image-20220731143149570

image-20220731140825375

3.4 使用案例

一个简单的登录拦截器实现案例

@Configuration // 标识这是一个配置类
public class InterceptorConfiguration implements WebMvcConfigurer {

    /**
     * 配置:允许 http 请求进行跨域访问
     * @param corsRegistry
     */
    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**") // 哪些接口 URL 需要增加跨域设置
                .allowedOrigins("*") // 制定的是前端哪些域名被允许跨域访问
                .allowCredentials(true) // 需要带 cookie 等凭证时,设置为 true,会把相关信息带上
                .allowedMethods("GET", "HEAD", "POST", "DELETE", "OPTIONS") // 指定允许的请求方法
                .maxAge(3600); // cookie 失效的时间,单位秒;若设置为 -1,则关闭浏览器失效
    }

    /**
     * 配置:要拦截的路径以及不拦截的路径
     * @param interceptorRegistry
     */
    @Override
    public void addInterceptors(InterceptorRegistry interceptorRegistry) {
        // 注册 Interceptor 拦截器(Interceptor 是我们自己写的拦截器类)
        InterceptorRegistration registration = interceptorRegistry.addInterceptor(new LoginInterceptor());
        // 添加需要拦截的路径
        registration.addPathPatterns("/**");
        // 添加不拦截的路径
        registration.excludePathPatterns(
                "**/*.html",
                "**/*.jsp",
                "**/*.css"
        );
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer pathMatchConfigurer) {

    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer contentNegotiationConfigurer) {

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {

    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {

    }

    @Override
    public void addFormatters(FormatterRegistry formatterRegistry) {

    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {

    }

    @Override
    public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {

    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {

    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> list) {

    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> list) {

    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {

    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {

    }

    @Override
    public Validator getValidator() {
        return null;
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}

public class LoginInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        try {
            Admin admin = (Admin) request.getSession();.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_ADMIN);

            if (admin != null){
                return true;
            }

            // 如果拦截以后重定向的路径,一般设置为登录页面
            response.sendRedirect(request.getContextPath() + "login.jsp");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }
}

4、三者区别

4.1 系统级别

image-20220731142507410

4.2 生命周期

image-20220731142527545

4.3 访问权限

image-20220731142542514

4.4 作用

image-20220731142613672


END

本文作者:
文章标题:JavaWeb 三大器
本文地址:https://www.pendulumye.com/javaweb/466.html
版权说明:若无注明,本文皆PendulumYe原创,转载请保留文章出处。
最后修改:2022 年 07 月 31 日
千山万水总是情,给个一毛行不行💋