目前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());
}
}
在请求开始和结束的时候显示请求路径。