当使用spring-Boot时,嵌入式Servlet容器通过扫描注解的方式注册Servlet、Filter和Servlet规范的所有监听器(如HttpSessionListener监听器)。 Spring boot 的主 Servlet 为 DispatcherServlet,其默认的url-pattern为“/”。本篇文章主要写springboot中如何定义Servlet、Filter、Listener、Interceptor等。具体这几个区别暂时不展开讲,我在文中也简单做一下陈述。
一、Servlet
在spring boot中添加自己的Servlet有两种方法,代码注册Servlet和注解自动注册(Filter和Listener也是如此)
- 代码注册通过ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 获得控制。 也可以通过实现 ServletContextInitializer 接口直接注册。【不过多讨论】
- 在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。
@EnableConfigurationProperties({GhProperties.class})
@SpringBootApplication
@ServletComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
下面来自定义我们的Servlet代码:
import javax.servlet.ServletConfig;
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;
import java.io.PrintWriter;
/**
* Created by gonghao on 2017/6/2.
*/
@WebServlet(name = "ghServlet",urlPatterns = "/gh/*",loadOnStartup = 1)
public class GhServlet extends HttpServlet {
@Override
public void init(ServletConfig servletConfig){
//初始化做点事情
System.out.println("初始化servlet:GhServlet,输出日志以示存在!");
}
@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>Hello,My name is : GhServlet</h1>");
out.println("</body>");
out.println("</html>");
}
}
有几点稍微注意下:
name = "ghServlet"
不指定name的情况下,name默认值为类全路径,即com.example.demo.utils.servlet.GhServlet.urlPatterns = "/gh/*"
为需要拦截的内容。loadOnStartup = 1
意思是项目启动时加载,默认的是用到该Servlet才会加载,这样我们就可以在启动的时候看到输出日志了。效果如下:
在线人数那个先忽略,是为了演示Listener的。我们前台发起了请求,符合我们的urlPatterns = /gh/*,所以该请求被拦截到,输出到页面一堆文字(忽略中文乱码)。
有个问题说明一下:DispatcherServlet 默认拦截“/”,GhServlet 拦截“/gh/*,那么在我们访问 http://localhost:8080/gh/11的时候系统是按照:“匹配的优先级是从精确到模糊,符合条件的Servlet并不会都执行”。
二、Filter
由于我们采取注解方式,Servlet、Filter、Listener都可以直接采用。这里直接展示自定义Filter的代码:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Created by gonghao on 2017/6/2.
*/
@WebFilter(filterName = "ghFilter",urlPatterns = "/*",asyncSupported = true)
public class GhFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化自定义Filter:GhFilter");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String uri = httpServletRequest.getRequestURI();
System.out.println("打印请求URI:"+uri);
if(!uri.contains("gh")){
httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/gh");
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("销毁...");
}
}
明确一点:我们的Filter的拦截范围是“/*”
,我们这里在Filter里面做了一些事情:如果请求不包含/gh,我们就给它重定向到 path+/gh,由于前面我们定义了Servlet拦截了/gh/*,所以现在是无论我们在请求里面写了什么东西,都会被重定向到刚刚Servlet那个显示页面上。
例如我们 访问 http://localhost:8080/hello,来看下前后台的显示:
我们可以看到,hello的请求被Filter过滤器拦截到,重定向到/gh,又被我们的Servlet所处理,doPost到输出页面。
三、Listener
Listener的类型有好几个,我们这里简单实现一个HttpSessionListener,也做一个简单的在线人数统计。
我们先对我们的过滤器Filter改造一下,把刚刚那个拦截注释掉,只留下 创建HttpSession那一步:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();//这句话要留着,不然不会创建session,我们的HttpSessionListener也就触发不了了。
// HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
// String uri = httpServletRequest.getRequestURI();
// System.out.println("打印请求URI:"+uri);
// if(!uri.contains("gh")){
// httpServletResponse.sendRedirect(httpServletRequest.getContextPath()+"/gh");
// return;
// }
filterChain.doFilter(servletRequest,servletResponse);
}
自定义Listener的代码:
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by gonghao on 2017/6/2.
*/
@WebListener
public class GhListener implements HttpSessionListener{
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
ServletContext app = httpSessionEvent.getSession().getServletContext();
int count = 0;
String countStr = app.getAttribute("onLineCount")+"";
if(!"null".equals(countStr)){
count = Integer.parseInt(countStr);
}
count++;
app.setAttribute("onLineCount", count);
System.out.println("在线人数:"+count);
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
ServletContext app = httpSessionEvent.getSession().getServletContext();
int count = Integer.parseInt(app.getAttribute("onLineCount").toString());
count--;
System.out.println("在线人数:"+count);
app.setAttribute("onLineCount", count);
}
}
我们分别在IE和chrome访问时,查看后台数据统计:
忽略中间的那段,是做拦截器的输出,我们可以看到人数统计会增加,而且同一个浏览器新开窗口会算成一个session。