美文网首页Spring
Spring Boot Servlet、Filter、Liste

Spring Boot Servlet、Filter、Liste

作者: OzanShareing | 来源:发表于2018-01-22 11:12 被阅读20次

    Web开发使用 Controller 基本上可以完成大部分需求,但是我们还可能会用到 Servlet、Filter、Listener、Interceptor 等等。

    当使用Spring-Boot时,嵌入式Servlet容器通过扫描注解的方式注册Servlet、Filter和Servlet规范的所有监听器(如HttpSessionListener监听器)。

    Spring boot 的主 Servlet 为 DispatcherServlet,其默认的url-pattern为“/”。也许我们在应用中还需要定义更多的Servlet,该如何使用SpringBoot来完成呢?

    在spring boot中添加自己的Servlet有两种方法,代码注册Servlet和注解自动注册(Filter和Listener也是如此)。

    一、代码注册通过ServletRegistrationBean、 FilterRegistrationBean 和ServletListenerRegistrationBean 获得控制。也可以通过实现 ServletContextInitializer 接口直接注册。

    二、在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。

    通过代码注册Servlet示例代码:
    SpringBootSampleApplication.java

    package org.springboot.sample;
    
    import org.springboot.sample.servlet.MyServlet;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.embedded.ServletRegistrationBean;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.servlet.DispatcherServlet;
    
    @SpringBootApplication
    public class SpringBootSampleApplication {
    
        /**
         * 使用代码注册Servlet(不需要@ServletComponentScan注解)
         *
         * @return
         * @author SHANHY
         * @create  2016年1月6日
         */
        @Bean
        public ServletRegistrationBean servletRegistrationBean() {
            return new ServletRegistrationBean(new MyServlet(), "/xs/*");// ServletName默认值为首字母小写,即myServlet
        }
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootSampleApplication.class, args);
        }
    }
    

    MyServlet.java

    package org.springboot.sample.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * Servlet
     *
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月6日
     */
    //@WebServlet(urlPatterns="/xs/*", description="Servlet的说明")
    public class MyServlet extends HttpServlet{
    
        private static final long serialVersionUID = -8685285401859800066L;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println(">>>>>>>>>>doGet()<<<<<<<<<<<");
            doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println(">>>>>>>>>>doPost()<<<<<<<<<<<");
            resp.setContentType("text/html");  
            PrintWriter out = resp.getWriter();  
            out.println("<html>");  
            out.println("<head>");  
            out.println("<title>Hello World</title>");  
            out.println("</head>");  
            out.println("<body>");  
            out.println("<h1>大家好,我的名字叫Servlet</h1>");  
            out.println("</body>");  
            out.println("</html>"); 
        }
    
    }
    
    

    使用注解注册Servlet示例代码
    SpringBootSampleApplication.java

    package org.springboot.sample;
    
    import org.springboot.sample.servlet.MyServlet;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.embedded.ServletRegistrationBean;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.servlet.DispatcherServlet;
    
    @SpringBootApplication
    @ServletComponentScan
    public class SpringBootSampleApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootSampleApplication.class, args);
        }
    }
    

    MyServlet2.java

    package org.springboot.sample.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * Servlet
     *
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月6日
     */
    @WebServlet(urlPatterns="/xs/myservlet", description="Servlet的说明") // 不指定name的情况下,name默认值为类全路径,即org.springboot.sample.servlet.MyServlet2
    public class MyServlet2 extends HttpServlet{
    
        private static final long serialVersionUID = -8685285401859800066L;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println(">>>>>>>>>>doGet2()<<<<<<<<<<<");
            doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println(">>>>>>>>>>doPost2()<<<<<<<<<<<");
            resp.setContentType("text/html");  
            PrintWriter out = resp.getWriter();  
            out.println("<html>");  
            out.println("<head>");  
            out.println("<title>Hello World</title>");  
            out.println("</head>");  
            out.println("<body>");  
            out.println("<h1>大家好,我的名字叫Servlet2</h1>");  
            out.println("</body>");  
            out.println("</html>"); 
        }
    
    }
    

    使用 @WebServlet 注解,其中可以设置一些属性。

    有个问题:DispatcherServlet 默认拦截“/”,MyServlet 拦截“/xs/*”,MyServlet2 拦截“/xs/myservlet”,那么在我们访问http://localhost:8080/xs/myservlet 的时候系统会怎么处理呢?
    如果访问 http://localhost:8080/xs/abc 的时候又是什么结果呢?这里就不给大家卖关子了,其结果是匹配的优先级是从精确到模糊,符合条件的Servlet并不会都执行

    既然系统DispatcherServlet 默认拦截“/”,那么我们是否能做修改呢,答案是肯定的,我们在SpringBootSampleApplication中添加代码:

     /**
         * 修改DispatcherServlet默认配置
         *
         * @param dispatcherServlet
         * @return
         * @author SHANHY
         * @create  2016年1月6日
         */
        @Bean
        public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
            ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet);
            registration.getUrlMappings().clear();
            registration.addUrlMappings("*.do");
            registration.addUrlMappings("*.json");
            return registration;
        }
    

    当然,这里可以对DispatcherServlet做很多修改,并非只是UrlMappings。

    直接使用@WebFilter和@WebListener的方式,完成一个Filter 和一个 Listener。

    过滤器(Filter)文件

    MyFilter.java

    package org.springboot.sample.filter;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    
    /**
     * 使用注解标注过滤器
     * @WebFilter将一个实现了javax.servlet.Filter接口的类定义为过滤器
     * 属性filterName声明过滤器的名称,可选
     * 属性urlPatterns指定要过滤 的URL模式,也可使用属性value来声明.(指定要过滤的URL模式是必选属性)
     * 
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月6日
     */
    @WebFilter(filterName="myFilter",urlPatterns="/*")
    public class MyFilter implements Filter {
    
        @Override
        public void destroy() {
            System.out.println("过滤器销毁");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            System.out.println("执行过滤操作");
            chain.doFilter(request, response);
        }
    
        @Override
        public void init(FilterConfig config) throws ServletException {
            System.out.println("过滤器初始化");
        }
    
    }
    

    ServletContext监听器(Listener)文件

    MyServletContextListener.java

    package org.springboot.sample.listener;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.annotation.WebListener;
    
    /**
     * 使用@WebListener注解,实现ServletContextListener接口
     *
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月6日
     */
    @WebListener
    public class MyServletContextListener implements ServletContextListener {
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("ServletContex初始化");
            System.out.println(sce.getServletContext().getServerInfo());
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("ServletContex销毁");
        }
    
    }
    

    ServletContext监听器(Listener)文件

    MyHttpSessionListener.java

    package org.springboot.sample.listener;
    
    import javax.servlet.annotation.WebListener;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    /**
     * 监听Session的创建与销毁
     *
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月6日
     */
    @WebListener
    public class MyHttpSessionListener implements HttpSessionListener {
    
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            System.out.println("Session 被创建");
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            System.out.println("ServletContex初始化");
        }
    
    }
    

    注意不要忘记在 SpringBootSampleApplication.java 上添加 @ServletComponentScan 注解。

    在启动的过程中我们会看到输出:

    ServletContex初始化
    Apache Tomcat/8.0.30
    过滤器初始化
    

    服务启动后,随便访问一个页面,会看到输出:

    执行过滤操作
    Session 被创建
    

    至于如何使用代码的方式注册Filter和Listener,请参考Servlet的介绍。不同的是需要使用 FilterRegistrationBean 和 ServletListenerRegistrationBean 这两个类。

    最后上一张工程结构图:

    过滤器属于Servlet范畴的API,与Spring 没什么关系。

    Web开发中,我们除了使用 Filter 来过滤web请求外,还可以使用Spring提供的HandlerInterceptor(拦截器)。

    HandlerInterceptor 的功能跟过滤器类似,但是提供更精细的的控制能力:在request被响应之前、request被响应之后、视图渲染之前以及request全部结束之后。我们不能通过拦截器修改request内容,但是可以通过抛出异常(或者返回false)来暂停request的执行。

    实现 UserRoleAuthorizationInterceptor 的拦截器有:

    • ConversionServiceExposingInterceptor
    • CorsInterceptor
    • LocaleChangeInterceptor
    • PathExposingHandlerInterceptor
    • ResourceUrlProviderExposingInterceptor
    • ThemeChangeInterceptor
    • UriTemplateVariablesHandlerInterceptor
    • UserRoleAuthorizationInterceptor

    其中 LocaleChangeInterceptor 和 ThemeChangeInterceptor 比较常用。

    配置拦截器也很简单,Spring提供了基础类WebMvcConfigurerAdapter ,我们只需要重写 addInterceptors 方法添加注册拦截器。

    实现自定义拦截器只需要3步:

    • 创建我们自己的拦截器类并实现 HandlerInterceptor 接口。
    • 创建一个Java类继承WebMvcConfigurerAdapter,并重写 addInterceptors 方法。
    • 实例化我们自定义的拦截器,然后将对象手动添加到拦截器链中(在addInterceptors方法中添加)。

    PS:本文重点在如何在Spring-Boot中使用拦截器,关于拦截器的原理请大家查阅资料了解。

    代码示例:

    MyInterceptor1.java

    package org.springboot.sample.interceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * 自定义拦截器1
     *
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月7日
     */
    public class MyInterceptor1 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println(">>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");
    
            return true;// 只有返回true才会继续向下执行,返回false取消当前请求
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
        }
    
    }
    

    MyInterceptor2.java

    package org.springboot.sample.interceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * 自定义拦截器2
     *
     * @author   单红宇(365384722)
     * @myblog  http://blog.csdn.net/catoop/
     * @create    2016年1月7日
     */
    public class MyInterceptor2 implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println(">>>MyInterceptor2>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");
    
            return true;// 只有返回true才会继续向下执行,返回false取消当前请求
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println(">>>MyInterceptor2>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println(">>>MyInterceptor2>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
        }
    
    }
    

    MyWebAppConfigurer.java

    package org.springboot.sample.config;
    
    import org.springboot.sample.interceptor.MyInterceptor1;
    import org.springboot.sample.interceptor.MyInterceptor2;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    @Configuration
    public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 多个拦截器组成一个拦截器链
            // addPathPatterns 用于添加拦截规则
            // excludePathPatterns 用户排除拦截
            registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
            registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
            super.addInterceptors(registry);
        }
    
    }
    

    然后在浏览器输入地址: http://localhost:8080/index 后,控制台的输出为:

    >>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)
    >>>MyInterceptor2>>>>>>>在请求处理之前进行调用(Controller方法调用之前)
    >>>MyInterceptor2>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    >>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    >>>MyInterceptor2>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    >>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    

    根据输出可以了解拦截器链的执行顺序(具体原理介绍,大家找度娘一问便知)

    最后强调一点:只有经过DispatcherServlet 的请求,才会走拦截器链,我们自定义的Servlet 请求是不会被拦截的,比如我们自定义的Servlet地址 http://localhost:8080/xs/myservlet 是不会被拦截器拦截的。并且不管是属于哪个Servlet 只要符合过滤器的过滤规则,过滤器都会拦截。

    最后说明下,我们上面用到的 WebMvcConfigurerAdapter 并非只是注册添加拦截器使用,其顾名思义是做Web配置用的,它还可以有很多其他作用,通过下面截图便可以大概了解,具体每个方法都是干什么用的,留给大家自己研究(其实都大同小异也很简单)。

    相关文章

      网友评论

        本文标题:Spring Boot Servlet、Filter、Liste

        本文链接:https://www.haomeiwen.com/subject/owhlaxtx.html