
对于Servlet 讲的文章比较好的是 www.ibm.com/developerwo… , 大家可以看看这篇文章 .
对于Servlet深入理解可以看看 : blog.csdn.net/qq_25933249…
很明确 , Servlet他其实由两部分 , 第一部分就Servlet容器, 第二部分就是我们编写的Servlet接口.
本文以SpringBoot进行阐述 .
Servlet
注册一个 Servlet ,这个注册机制其实设计到tomcat的内部实现org.springframework.boot.web.embedded.tomcat.TomcatStarter#onStartup 这里进行注册.
@Bean
public ServletRegistrationBean<Servlet> servletRegistrationBean() {
ServletRegistrationBean<Servlet> registrationBean = new ServletRegistrationBean<>();
registrationBean.addUrlMappings("/get");
registrationBean.setName("servlet 1");
registrationBean.addInitParameter("port", "8080");
registrationBean.setOrder(Integer.MAX_VALUE);
registrationBean.setServlet(new MyServlet());
return registrationBean;
}
public static class MyServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
String conf = config.getInitParameter("port");
System.out.printf("init config k : %s, v: %s.\n", "port", conf);
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
res.getWriter().write("HELLO WORLD");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy ");
}
}
Servlet有 5个方法, init , getServletConfig() , service , destroy getServletInfo 之类的.
init 和 destroy 只会在服务初始化和销毁执行一次.
service 方法则是在每次服务都会被调用的 .
SpringBoot中Servlet注册是在 org.springframework.boot.web.servlet.RegistrationBean#onStartup , 其实对于org.springframework.web.servlet.DispatcherServlet 也是在这里注册的. 这个是Spring基于Tomcat的回调机制实现的一种 注入方式.
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description)
+ " was not registered (disabled)");
return;
}
// 注册 , 其实
register(description, servletContext);
}
Session 与 Cookie
Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态。
我们以简单的例子 , 看看Session和Cookie是如何运作的. (测试前清空cookie)
第一我们不关闭浏览的cookie .
@RestController
public class Test {
@GetMapping("/test")
public Object get(HttpServletRequest request, HttpServletResponse response) {
System.out.println("cookie" + " : " + request.getHeader("cookie"));
Cookie cookie = new Cookie("time", "" + System.currentTimeMillis());
response.addCookie(cookie);
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user == null) {
session.setAttribute("user", UUID.randomUUID().toString());
}
return user;
}
}
第一次请求响应是 :
HTTP/1.1 200
Set-Cookie: time=1580539709072
Set-Cookie: JSESSIONID=AE10A3E001E312061E071234ACDBA36A; Path=/; HttpOnly
Content-Length: 0
Date: Sat, 01 Feb 2020 06:48:29 GMT
我们发现除了我们自己定义的还有一个 JSESSIONID=AE10A3E001E312061E071234ACDBA36A 这个是什么 ?
我们发现我们本地多了俩

这个就是我们存入的cookie . 所以 session其实就是cookie的表示 . 作为用户的key , 他的生命周期只有一次会话. 也就是我们关闭浏览器, 这个cookie就会被删除. 这个是Session的默认实现 . 他不允许在浏览器保存 .
Session可以设置 存活时间.
HttpSession session = request.getSession();
// 设置为10S, 当用户10S未请求客户端的时候, 会自动认为这个session无效
session.setMaxInactiveInterval(10);
而且如果我们不关闭浏览器, 那么每次访问响应给我们的永远是不变的 JSESSIONID
所以我们看一下流程其实就是 .
当用户发送一个请求给服务器时, 用户会携带cookie去访问服务器. 这个是浏览器默认实现的. 当服务器拿到cookie发现Cookie中没有JSESSIONID , 会给用户创建一个 JSESSIONID 的cookie . 这个cookie在浏览器保存最长时间只能是一次会话(就是浏览器打开关闭代表一次会话). 所以用户可以在浏览器未关闭之前一直拿着这个 JSESSIONID作为身份认证. 当浏览器关闭后这个cookie也会被删除. 此时下次打开浏览器再请求又循环到开始了.
其实这里有一个BUG , 就是我可以伪装用户的JSESSIONID 访问浏览器 . 这样子很能出现危险 . 所以不要在 session中存入用户认证信息. 这样子会很危险.

根据这个图. 我们可以发现我们很轻松的伪装信息. 所以不要在 Session中存入敏感信息或者认证信息.
但是当我们把Cookie禁用了 会怎么办呢 ?
那么 浏览器的Cookie失效了, 那么代表Session也失效了(因为Session也是基于Cookie). .最简单的测试就是
curl http://localhost:8080/test 会发现永远返回空 .
但是我们要记住 , 还有一种方式可以传递 cookie. 那就是 url传递.
如何传递呢 . http://localhost:8080/test;use=493B013E8DC852A7BFDE40DCDFAD06B5
比如说我们访问 http://localhost:8080/test 将 cookie写入到服务器, 需要 http://localhost:8080/test;auth=D92A968FA16D00F511F5B8EECB8733A6 这么写 , 但是问题你咋获取这个SessionID , 那么就需要参数返回了.
springboot开启只需要
# 设置session模式 , 运行时是不能修改的.
server.servlet.session.tracking-modes=url
# 设置session 的name
server.servlet.session.cookie.name=auth
关于Session的具体实现问题 , 可以看看这篇文章 www.cnblogs.com/chenpi/p/54…
Listener 监听器

其实 ServletContextListener 这个也挺有用的 .
我们来使用 HttpSessionListener 测试一下
// 注册Listener
@Bean
public ServletListenerRegistrationBean<HttpSessionListener> sessionListenerServletListenerRegistrationBean() {
ServletListenerRegistrationBean<HttpSessionListener> registrationBean = new ServletListenerRegistrationBean<>();
registrationBean.setListener(new MyHttpSessionListener());
return registrationBean;
}
// 这里是触发事件
public static class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("创建 session 成功");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("销毁 session 成功");
}
}
可以监听session的创建和销毁.
Session好像默认存活时间是20mine .
Filter 过滤器
类似于Spring的 org.springframework.web.servlet.HandlerInterceptor , 只不过 Filer是事情通知. 不过他要比 HandlerInterceptor 早.
其中 chain.doFilter(request, response); 主要是用来放行的 .
@Bean
public FilterRegistrationBean<Filter> filterRegistrationBean() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter");
chain.doFilter(request, response);
}
@Override
public void destroy() {
//
}
});
registrationBean.setName("filter 1");
// 拦截所有请求
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
ServletContext
SpringBoot注入可以使用 , 可以获取ServletContext , 但是我感觉没啥用, 毕竟现在是前后端分离
@Configuration
public class Config implements ServletContextAware{
public ServletContext servletContext;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}