servlet、过滤器、监听器使用与概念温习以及在springboot中使用

目前java web开发基本都是基于servlet的,那么自然而然就会用到Servlet、Filter和Listener。
在springboot中,这些功能的配置方式有两种:
1、基于代码注册,通过ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 获得控制。
2、基于注解的配置,在springboot的入口类上加@ServletComponentScan 注解,然后在Servlet、Filter和Listener上加上@WebSerlvet、@WebFilter、@WebListener 注解自动注册。

代码地址:https://github.com/wkcaeser/springboot-studydemo

一、Servlet
代码注册方式,首先实现servlet类:

import com.google.gson.JsonObject;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.LocalDateTime;

//@WebServlet(urlPatterns = "/servlet1")
public class Servlet1 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this is get");
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this is post");
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("status", 1);
        if(req.getParameter("id")!=null){
            jsonObject.addProperty("id", "getId");
        }
        if(req.getAttribute("name")!=null){
            jsonObject.addProperty("name", "getWk");
        }
        resp.getWriter().write(jsonObject.toString());
    }
}
package com.wk.servlet;

import com.google.gson.JsonObject;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//@WebServlet(urlPatterns = "/servlet2")
public class Servlet2 extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this servlet2 get");
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this is servlet2 post");
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("status", 2);
        resp.getWriter().write(jsonObject.toString());
    }
}

这里实现了两个servlet类,然后在springboot入口类声明这两个bean:

 @Bean
    public ServletRegistrationBean servletRegistrationBean1(){
        return new ServletRegistrationBean(new Servlet1(), "/servlet1/*");
    }

    @Bean
    public ServletRegistrationBean servletRegistrationBean2(){
        return new ServletRegistrationBean(new Servlet2(), "/servlet2/*");
    }

注解方式只需要将servlet类上的注解启用然后在springboot入口类上加@ServletComponentScan注解即可。

在springboot中有一个默认的servlet为 DispatcherServlet,其默认的url-pattern为“/”,我们可以通过springboot入口类来修改其默认配置:

@Bean
    public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet);
        registration.getUrlMappings().clear();
        registration.addUrlMappings("*.do");
        registration.addUrlMappings("*.action");
        return registration;
    }

既然servlet的拦截路径可以自己定义,那么很容易出现的问题就是各个servlet拦截路径之间会出现交集,比如servlet1拦截路径为/action/*,servlet2拦截路径为/action/user/,那么这时候请求为/action/user,究竟是哪个servlet处理,还是两个都会处理?这里并不会两个servlet都处理,而是根据匹配度来响应,匹配度最高的会响应,而其他的不会响应。

二、Filter

Filter可以在请求执行前对请求进行过滤或者进行预处理,以及在请求完成后返回客户端之前进行一些额外操作。
当有多个filter时,执行会有顺序,在web.xml是根据注册顺序来执行,在springboot可以通过设置FilterRegistrationBean的order属性来指定执行顺序,order值越小,执行优先级越高。据说可以通过@Order注解来指定执行顺序,但是我试了以后发现没用,目前还没找到问题所在,希望了解的同学可以指导下。
下面是两个Filter以及在springbootapplication中的注册:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.Writer;

//@WebFilter(filterName = "filter1", urlPatterns = "/servlet1")
//@Order(Integer.MAX_VALUE)
public class Filter1 implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter1 start work");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("this is filter1");
        String id = request.getParameter("id");
        System.out.println(id);
        request.setAttribute("name", "wk");
        chain.doFilter(request, response);
        Writer writer = response.getWriter();
        System.out.println(writer);
        System.out.println("this is filter1");
    }

    @Override
    public void destroy() {
        System.out.println("filter1 end work");
    }
}
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

//@Order(1)
//@WebFilter(filterName = "filter2", urlPatterns = "/servlet1")
public class Filter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter2 start work");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("this is filter2");
        chain.doFilter(request,response);
        System.out.println("this is filter2");
    }

    @Override
    public void destroy() {
        System.out.println("Filter2 end work");
    }
}

以下是在springbootapplication中的注册以及执行优先级额指定:

 @Bean
//    @Order(1)
    public FilterRegistrationBean filterRegistrationBean1(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new Filter1());
        filterRegistrationBean.addUrlPatterns("/servlet1");
        filterRegistrationBean.setOrder(Integer.MAX_VALUE);
        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean2(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new Filter2());
        filterRegistrationBean.addUrlPatterns("/servlet1");
        filterRegistrationBean.setOrder(Integer.MAX_VALUE-1);
        return filterRegistrationBean;
    }

将filter上的注解启用就可以实现自动注册,但是通过@Order指定执行顺序并没有作用,所以只好通过配置类属性来指定,希望了解的同学指点下问题所在。

三、Listener

servlet中的listener主要分为三类八种,分别是:
1)监听 Session、request、context 的创建于销毁,分别为
HttpSessionLister、ServletContextListener、ServletRequestListener
2)监听对象属性变化,分别为:
HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener
3)监听Session 内的对象,分别为HttpSessionBindingListener 和 HttpSessionActivationListener。

这里讲第一类的三种,首先是ServletContextListener,这个listener在容器启动过程中就会建立,因为在应用启动过程中会有context的初始化建立过程:

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class ContextListener1 implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext初始化");
        System.out.println(sce.getServletContext().getServerInfo());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext销毁");
        System.out.println(sce.getServletContext().getServerInfo());
    }
}

然后是httpsessionlistener,servlet的session会在两种情况下创建:
1、在jsp页面中没有禁用session时,解析返回jsp页面的时候会创建session。
2、在代码中显示创建session,如resquest.getSession()这种操作。

由于我的代码中没有jsp页面以及显示创建session的操作,所以我先加了一个filter在每次请求之前先检查session,如果session不存在则显示创建session,如下:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.LocalDateTime;

public class SessionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Session filter start");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ((HttpServletRequest)request).getSession();
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        System.out.println("Session filter destroy");
    }
}

然后实现HttpSessionListener接口,创建sessionListener:

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

@WebListener
public class SessionListener1 implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session 被创建");
        System.out.println(LocalDateTime.ofInstant(Instant.ofEpochMilli(se.getSession().getCreationTime()), ZoneId.systemDefault()));
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println(LocalDateTime.ofInstant(Instant.ofEpochMilli(se.getSession().getCreationTime()), ZoneId.systemDefault()));
        System.out.println("session 被销毁");
    }
}

在session创建和销毁时通过java8时间类显示session创建时间。

接着是实现ServletRequestListener 接口来创建requestListener:

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;

@WebListener
public class RequestListener1 implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        System.out.println("destroy request: " + request.getServerName() +":"+request.getServerPort() + request.getServletPath());
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        System.out.println("start request: " + request.getServerName() +":"+request.getServerPort() + request.getServletPath());
    }
}

在请求开始和结束的时候显示请求路径。

你可能感兴趣的:(spring-boot)