SpringBoot 注册Servlet,Filter,Listener

我们现在使用工程大多是SpringBoot应用, 默认是以jar包的方式运行,使用嵌入式的Tomcat容器,而以前我们使用的Spring Web应用是以war包的方式,放在外部的Tomcat或者WebLogic容器中运行,如果是web应用,工程的src下会有一个webapp/WEB-INF/web.xml文件,我们可以把三大组件都注册在web.xml文件中,而 SpringBoot 工程没有 web.xml 文件怎么注册三大组件呢?SpringBoot为我们提供了注册方式:
• ServletRegistrationBean

@Bean
public ServletRegistrationBean myServlet(){
    // 如果发送/myServlet请求,使用自定义的MyServlet处理
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(), "/myServlet");
    return servletRegistrationBean;
}

• FilterRegistrationBean

@Bean
public FilterRegistrationBean myFilter(){
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(new MyFilter());
    filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet"));
    return filterRegistrationBean;
}

• ServletListenerRegistrationBean

@Bean
public ServletListenerRegistrationBean myListener(){
    ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean(new MyListener());
    return servletListenerRegistrationBean;
}
// 我们的Listener有哪些接口可以继承在的静态代码块中有初始化
static {
    Set<Class<?>> types = new HashSet<>();
    types.add(ServletContextAttributeListener.class);
    types.add(ServletRequestListener.class);
    types.add(ServletRequestAttributeListener.class);
    types.add(HttpSessionAttributeListener.class);
    types.add(HttpSessionListener.class);
    types.add(ServletContextListener.class);
    SUPPORTED_TYPES = Collections.unmodifiableSet(types);
}

SpringBoot 配置 MVC 的时候帮我们自动配置的前端控制器 DispatcherServlet 也是通过这种方式: DispatcherServletAutoConfiguration

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
                                                                       WebMvcProperties webMvcProperties, 
                                                                       ObjectProvider<MultipartConfigElement> multipartConfig) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                                                                                           // 默认拦截 / 路径下的请求,可以通过spring.mvc.servlet.path 修改SpringMVC前端控制器默认请求
                                                                                           webMvcProperties.getServlet().getPath());
    registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
    multipartConfig.ifAvailable(registration::setMultipartConfig);
    return registration;
}

其实在SpringBoot应用中 Servlet 的这三大组件已经过时了,现在基本都不用,原来的 Servlet 现在被 Controller 代替了,Listener的作用也不明显,只有 Filter 还会有使用场景,有时候会使用它做统一的登录验证,比如登录之后把用户信息放在session,在 Filter 里判断 session 中是否存在用户信息,不存在则跳转到登陆页面。

那下面就来说下 Filter:

前端页面在发送请求到 Tomcat 容器之后,web容器会把 http 协议请求包装成一个 HttpRequest 对象,然后用这个 HttpRequest 对象贯穿整个请求的始终,请求会首先到达 Filter,然后调用 Servlet 里的service方法。所以他们共享同一个 HttpRequest 对象,其实 Filter 的作用类似 Spring AOP,如果这里不清楚,可以画个图看下:

springboot filter 记录请求响应_java

每个 Filter 的 doFiltet 方法里都必须有 chain. doFilter(request, response) 方法,代表请求继续往下执行,那我们来看看这个方法干了什么:

// 方法最终会调到 ApplicationFilterChain 的 doFilter 方法
@Override
public void doFilter(ServletRequest request, ServletResponse response)
    throws IOException, ServletException {
    // 判断是否安全可用,这里是false
    if( Globals.IS_SECURITY_ENABLED ) {
        // 省略
    } else {
        // 调用这个方法
        internalDoFilter(request,response);
    }
}
private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {
    // 如果有下一个Filter,调用它的doFilter方法
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        try {
            Filter filter = filterConfig.getFilter();
            if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                filterConfig.getFilterDef().getAsyncSupported())) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
            }
            if( Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();
                Object[] args = new Object[]{req, res, this};
                SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
            } else {
                filter.doFilter(request, response, this);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            // 还是一些异常
        }
        return;
    }
    // 如果没有下一个Filter,调用Servlet的service方法
    try {
        if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
            lastServicedRequest.set(request);
            lastServicedResponse.set(response);
        }
        if (request.isAsyncSupported() && !servletSupportsAsync) {
            request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                                 Boolean.FALSE);
        }
        // Use potentially wrapped request from this point
        if ((request instanceof HttpServletRequest) &&
            (response instanceof HttpServletResponse) &&
            Globals.IS_SECURITY_ENABLED ) {
            final ServletRequest req = request;
            final ServletResponse res = response;
            Principal principal =
                ((HttpServletRequest) req).getUserPrincipal();
            Object[] args = new Object[]{req, res};
            SecurityUtil.doAsPrivilege("service",
                                       servlet,
                                       classTypeUsedInService,
                                       args,
                                       principal);
        } else {
            // 调用servlet.service
            servlet.service(request, response);
        }
    } catch (IOException | ServletException | RuntimeException e) {
        //
    }
}

所以我们看到这个chain. doFilter(request, response) 一共干了两件事:
1、如果有下一个Filter,调用它的doFilter方法
2、如果没有下一个Filter,调用Servlet的service方法
方法结束后会沿着链路回到 Filter 里的 doFilter 方法,最终返回到浏览器。
以上就是Servlet三大组件在SpringBoot中的注入方式