Spring Boot Servlet

上一篇我们对如何创建Controller 来响应JSON 以及如何显示数据到页面中,已经有了初步的了解。 
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 接口直接注册。

通过代码注册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>"); 
    }

}

 

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

SpringBootApplication 上使用@ServletComponentScan注解后
Servlet可以直接通过@WebServlet注解自动注册
Filter可以直接通过@WebFilter注解自动注册
Listener可以直接通过@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
@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。

http://blog.csdn.net/catoop/article/details/50501686

 

Filter


使用@WebFilter 注解

 

@WebFilter 的常用属性

属性名 类型 描述
filterName String 指定过滤器的name属性,等价于,
value String[] 该属性等价于urlPatterns属性。两个属性不能同时使用。
urlPatterns String[] 指定一组过滤器的URL匹配模式,等价于标签。
dispatcherTypes DispatcherType 指定过滤器的转发模式,具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。
initParams WebInitParam[] 指定一组过滤器初始化参数,等价于标签。
asyncSupported boolean 声明过滤器是否支持异步操作模式,等价于 标签。
description String 该过滤器的描述信息,等价于 标签。
displayName String 该过滤器的显示名,通常配合工具使用,等价于 标签。

 

使用@WebFilter 注解的时候发现注解里面没有提供可以控制执行顺序的参数 , 通过实践发现如果想要控制 filter的执行顺序可以通过控制 filter 的文件名来控制 。比如:UserLoginFilter.java 和 ApiLog.java 这两个过滤器文件,因为这两个文件的首字母A排U之前,导致每次执行的时候都是先执行 ApiLog 过滤器再执行 UserLoginFilter 过滤器,所以我们现在修改两个文件的名称分别为 Filter0_UserLogin.java 和 Filter1_ApiLog.java,就会先执行 Filter0_UserLogin 再执行 Filter1_ApiLog。

 

示例:

 

Filter 类:

@WebFilter(filterName = "MyFilterWithAnnotation",urlPatterns = "/api/*",
initParams = {//以下都是druid数据源状态监控的参数
        @WebInitParam(name = "allow", value = ""),// IP白名单 (没有配置或者为空,则允许所有访问)
        @WebInitParam(name = "deny", value = ""),// IP黑名单 (存在共同时,deny优先于allow)
        @WebInitParam(name = "loginUsername", value = "hresh"),// 用户名
        @WebInitParam(name = "loginPassword", value = "123456"),// 密码
        @WebInitParam(name = "resetEnable", value = "false")//   禁用HTML页面上的“Reset All”功能
})
public class MyFilterWithAnnotation implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(MyFilterWithAnnotation.class);
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("初始化MyFilterWithAnnotation过滤器:", filterConfig.getFilterName());
        System.out.println(filterConfig.getInitParameter("loginUsername"));
        System.out.println(filterConfig.getInitParameter("loginPassword"));
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("过滤器开始对请求进行预处理:");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String requestUri = request.getRequestURI();
        System.out.println("请求的接口为:" + requestUri);
        long startTime = System.currentTimeMillis();
        //通过 doFilter 方法实现过滤功能
        filterChain.doFilter(servletRequest,servletResponse);
        // 上面的 doFilter 方法执行结束后用户的请求已经返回
        long endTime = System.currentTimeMillis();
        System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
    }
    @Override
    public void destroy() {
        logger.info("销毁过滤器MyFilterWithAnnotation");
    }
}
复制代码

 

自定义 Controller 验证过滤器

 

@RestController
@RequestMapping("/api")
public class MyController {
    @GetMapping("/hello")
    public String getHello() throws InterruptedException {
        Thread.sleep(1000);
        return "hello";
    }
}
复制代码

 

Spring Boot 启动类:

 

@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
    public static void main(String[] args) {
        SpringApplication.run(SpbGuide3Application.class, args);
    }
}
复制代码

 

运行该项目,打开网址: http://localhost:8080/api/hello

 

Filter 的创建和销毁由 Web 服务器负责,Web 应用程序启动时,Web 服务器将创建 Filter 的实例对象,并调用其 init 方法,完成对象的初始化功能,从而为后续的用户请求做好拦截的准备工作,filter 对象只会创建一次,即 init 方法只会执行一次。通过 init 方法的参数,可获得代表当前 filter 配置信息的 FilterConfig 对象。同样 Filter 对象的销毁由 destroy 方法执行,进而释放过滤器使用的资源。

 

继承 HttpFilter


结合上述注册 Servlet 的案例,我们不由得想到是否可以通过自定义类继承 HttpFilter,重写 doFilter方法,具体代码如下:

Filter 类:

@Component
public class MyFilter3 extends HttpFilter {
    private static final Logger logger = LoggerFactory.getLogger(MyFilterWithAnnotation.class);
    @Override
    public void init() throws ServletException {
        FilterConfig filterConfig = this.getFilterConfig();
        logger.info("初始化MyFilter3过滤器:", filterConfig.getFilterName());
    }
    @Override
    public void destroy() {
        logger.info("销毁过滤器MyFilterWithAnnotation");
    }
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String requestUri = request.getRequestURI();
        System.out.println("进入MyFilter3 过滤器");
        System.out.println("请求的接口为:" + requestUri);
        long startTime = System.currentTimeMillis();
        //通过 doFilter 方法实现过滤功能
        super.doFilter(request, response, chain);
        // 上面的 doFilter 方法执行结束后用户的请求已经返回
        long endTime = System.currentTimeMillis();
        System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
    }
}

 

Spring Boot 启动类:

 

@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
    public static void main(String[] args) {
        SpringApplication.run(SpbGuide3Application.class, args);
    }
}

与 servlet 不同,不需要在 Spring Boot 启动类中添加 FilterRegistrationBean

运行该项目,打开网址: http://localhost:8080/api/hello

 

实现 Filter 接口


定义两个 Filter 类,用来实现 filter 排序功能。

@Component
public class MyFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(MyFilter.class);
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("初始化MyFilter过滤器:", filterConfig.getFilterName());
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("过滤器开始对请求进行预处理:");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String requestUri = request.getRequestURI();
        System.out.println("请求的接口为:" + requestUri);
        long startTime = System.currentTimeMillis();
        //通过 doFilter 方法实现过滤功能
        filterChain.doFilter(servletRequest,servletResponse);
        // 上面的 doFilter 方法执行结束后用户的请求已经返回
        long endTime = System.currentTimeMillis();
        System.out.println("该用户的请求已经处理完毕,请求花费的时间为:" + (endTime - startTime));
    }
    @Override
    public void destroy() {
        logger.info("销毁过滤器MyFilter");
    }
}
复制代码

 

MyFilter2 文件和上述代码一致,只是命名不同。

 

在配置中注册自定义的过滤器

@Configuration
public class MyFilterConfig {
    @Autowired
    MyFilter myFilter;
    @Autowired
    MyFilter2 myFilter2;
    @Bean
    public FilterRegistrationBean<MyFilter> setUpMyFilter(){
        FilterRegistrationBean<MyFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        //setOrder 方法可以决定 Filter 的执行顺序
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.setFilter(myFilter);
        filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/*")));
        return filterRegistrationBean;
    }
    @Bean
    public FilterRegistrationBean<MyFilter2> setUpMyFilter2(){
        FilterRegistrationBean<MyFilter2> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.setFilter(myFilter2);
        filterRegistrationBean.setUrlPatterns(new ArrayList<>(Arrays.asList("/api/* ")));
        return filterRegistrationBean;
    }
}
复制代码

 

在配置中注册自定义的过滤器,通过 FilterRegistrationBean 的 setOrder 方法可以决定 Filter 的执行顺序。

 

Spring Boot 启动类:

@SpringBootApplication
@ServletComponentScan
public class SpbGuide3Application {
    public static void main(String[] args) {
        SpringApplication.run(SpbGuide3Application.class, args);
    }
}

运行该项目,打开网址: http://localhost:8080/api/hello

 

https://developer.aliyun.com/article/1026974

 https://developer.aliyun.com/article/1026974

 

posted @ 2016-11-15 13:46  沧海一滴  阅读(3124)  评论(0编辑  收藏  举报