在springboot 中配置使用servlet
[toc]
1.前言
还记得,说到web项目,最早接触的就是servlet,实际上SSH项目,也是依赖于servlet,在web.xml文件中进行配置。那么使用了springboot之后,不仅有一个疑问,虽然SpringMVC已经帮我们很容易的实现了spring web项目的使用,只需要@Controller就能搞定。但是我们需要使用servlet该如何做呢?虽然这种需求非常少,但是在springboot的官方文档对servlet的使用有过描述。下面我们来看看在springboot中如何使用和配置servlet。
2.servlet
在com.dhb.servlet包中定义一个myservlet类。通过@webServlet注解定义其name和urlPatterns。
同时继承父类HttpServlet的doGet和doPost方法。这样就是我们之前经常使用的Servlet了。
package com.dhb.servlet;
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(name = "myServlet",urlPatterns = "/srv")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet doGet method called...");
resp.getWriter().println("doGet method called url is ["+req.getRequestURL()+"] time is ["+System.currentTimeMillis()+"]");
resp.getWriter().flush();
resp.getWriter().close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet doPost method called...");
resp.getWriter().println("doPost method called url is ["+req.getRequestURL()+"] time is ["+System.currentTimeMillis()+"]");
resp.getWriter().flush();
resp.getWriter().close();
}
}
这个Servlet配置完成之后,我们要如何让其生效呢,下一步就是配置springboot的启动类。
3.springboot配置
在包com.dhb中定义一个Bean。
需要注意的是,由于使用了@ServletComponentScan,这个注解中没有指定具体的package,那么就需要确保被扫描的类位于该类所在的子目录中。也就是在本文中,其所在的package 在com.dhb中。
package com.dhb;
import com.dhb.servlet.MyServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@ServletComponentScan
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringBootApplication.class, args);
}
//将servlet 添加到springboot容器中
@Bean
public ServletRegistrationBean<MyServlet> webServletRegistrationBean() {
ServletRegistrationBean<MyServlet> bean = new ServletRegistrationBean<>(new MyServlet());
bean.setLoadOnStartup(1);
return bean;
}
}
4.启动项目
在上一篇中介绍了jrebel,现在就通过jribel启动:
![](https://img.haomeiwen.com/i3237432/6786c290ad477285.png)
然后我们通过postman访问:
![](https://img.haomeiwen.com/i3237432/ff9bbef1c81b544e.png)
可以看到收到了预期的响应。
5.UrlMapping设置
在springboot的启动类中定义了的Bean里面,可以设置urlMapping。但是需要注意的是,这样一来,Servlet设置的urlPatterns将不再生效。
我们将代码改为如下:
ServletRegistrationBean<MyServlet> bean = new ServletRegistrationBean<>(new MyServlet(),"/s2");
之后重启,由于使用了Jrebel,则不需要重启,系统会自动加载。
可以看到http://127.0.0.1:8084/s2访问成功:
![](https://img.haomeiwen.com/i3237432/4f8f6254900a066b.png)
但是之前的http://127.0.0.1:8084/srv则返回了404错误。
![](https://img.haomeiwen.com/i3237432/16aa7b6c670ad33e.png)
6.Filter
Filter的使用也很简单,建立一个package com.dhb.servlet.filter 。
package com.dhb.servlet.filter;
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;
import java.io.IOException;
@WebFilter(filterName = "MyFilter",urlPatterns = "/s2")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter init ...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("do Filter ...");
chain.doFilter(request,response);
}
@Override
public void destroy() {
System.out.println("destroy ... ");
}
}
然后我们重启服务,可以看到filter已经被初始化:
2021-02-22 19:41:51.512 INFO 11532 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-02-22 19:41:51.620 INFO 11532 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-02-22 19:41:51.620 INFO 11532 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1289 ms
2021-02-22 19:41:51.682 INFO 11532 --- [ main] o.s.boot.web.servlet.RegistrationBean : Servlet myServlet was not registered (possibly already registered?)
Filter init ...
2021-02-22 19:41:52.018 INFO 11532 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-02-22 19:41:52.365 INFO 11532 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2021-02-22 19:41:52.397 INFO 11532 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8084 (http) with context path ''
2021-02-22 19:41:52.406 INFO 11532 --- [ main] com.dhb.MySpringBootApplication : Started MySpringBootApplication in 2.904 seconds (JVM running for 6.273)
之后请求设置的路径。
http://127.0.0.1:8084/s2
可以看到输出:
2021-02-22 19:41:52.365 INFO 11532 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2021-02-22 19:41:52.397 INFO 11532 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8084 (http) with context path ''
2021-02-22 19:41:52.406 INFO 11532 --- [ main] com.dhb.MySpringBootApplication : Started MySpringBootApplication in 2.904 seconds (JVM running for 6.273)
do Filter ...
servlet doPost method called...
需要注意的是,我们在filter中的chain.doFilter(request,response);这就是责任链模式,request链条上的filter执行完毕之后,再回过来按照逆序执行response上的filter。
7.Listener
Listener是servlet规范中的一中特殊类,用于监听servletContext,HttpSession和ServletRequest等域对象的创建和销毁事件。监听域对象的属性发生修改的事件,用于在事件的发生前,发生后做一些必要的处理,可用于如下场景:
- 1.统计在线人数和在线用户数
- 2.系统启动时加载初始化信心
- 3.统计网站访问信心
- 4.记录用户访问路径
我们来创建一个Listener如下:
package com.dhb.servlet.listener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class MyHttpSessionListener implements HttpSessionListener {
public static int online = 0;
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("创建session id is ["+se.getSession().getId()+"]");
online ++;
System.out.println("当前session总数:["+online+"]");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("销毁session id is ["+se.getSession().getId()+"]");
online --;
System.out.println("当前session总数:["+online+"]");
}
}
同样,需要在MySpringBootApplication中配置一个bean
@Bean
public ServletListenerRegistrationBean listenerRegistrationBean() {
ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();
srb.setListener(new MyHttpSessionListener());
System.out.println("listener 创建成功");
return srb;
}
官方文档建议Listener和Filter都用@Bean 注释方法的方式来创建。这样可以更加灵活控制。
启动Jrebel:
2021-02-23 10:46:38.126 INFO 18368 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-02-23 10:46:38.126 INFO 18368 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1218 ms
listener 创建成功
2021-02-23 10:46:38.201 INFO 18368 --- [ main] o.s.boot.web.servlet.RegistrationBean : Servlet myServlet was not registered (possibly already registered?)
Filter init ...
2021-02-23 10:46:38.442 INFO 18368 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-02-23 10:46:38.774 INFO 18368 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2021-02-23 10:46:38.815 INFO 18368 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8084 (http) with context path ''
2021-02-23 10:46:38.825 INFO 18368 --- [ main] com.dhb.MySpringBootApplication : Started MySpringBootApplication in 2.696 seconds (JVM running for 6.132)
可以看到,Listener实际上在Filter之前被初始化。
但是此时我们需要注意的是,请求任意url,session并没有增加。这是因为我们需要在请求后面增加session的操作。
修改Controller中的代码:
@Controller
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String hello(HttpSession session) {
session.setAttribute("aa","aa");
return "hello";
}
}
现在再来请求http://127.0.0.1:8084/hello,通过不同浏览器可以看到输出:
创建session id is [06327B8BE718B12F0B3CAA829160B162]
当前session总数:[1]
创建session id is [BC09536518EB4ED9F94D2290E6578DD9]
当前session总数:[2]
这样监听器就配置成功了。
8.总结
在SpringBoot中使用servlet是很少见的,毕竟SpringMVC使用起来非常方便,几乎没有直接使用servlet的必要。但是这并不意味着我们可以忽略servlet的存在。毕竟SpringMVC也是通过servlet演化而来。对于servlet规范中的filter和listener,我们可能使用的场景会非常多。特别是Listener,用在初始化数据的load等操作上。