Servlet 是一种基于 Java 技术的 Web 组件,用于生成动态内容,由容器管理。类似于其他 Java 技术组件,Servlet 是平台无关的 Java 类组成,并且由 Java Web 服务器加载执行。通常情况,由 Servlet 容器提供运行时环境。Servlet 容器,有时候也称作为Servlet 引擎,作为Web服务器或应用服务器的一部分。通过请求和响应对话,提供Web客户端与 Servlets 交互的能力。容器管理Servlets实例以及它们的生命周期。
相关技术:
CGI - 基于进程,php, apache httpd CGI
Servlet-基于线程
规范版本 | 发布时间 | Java平台 | 主要更新 |
---|---|---|---|
Servlet4.0 | 2017 | JavaEE8 | 支持http/2 |
Servlet3.1 | 2013 | JavaEE7 | 非阻塞I/O,http协议更新机制(websoket) |
Servlet3.0 | 2009 | JavaEE6 | 可插拔、简化部署、异步servlet,安全,文件上传 |
Servlet2.5 | 2005 | JavaEE5 | Annotation支持 |
Servlet2.4 | 2003 | J2EE1.4 | web.xml支持xml scheme |
Servlet2.3 | 2001 | J2EE1.3 | 新增filter,事件/监听器,Wrapper |
Servlet2.2 | 1999 | J2EE1.2 | 作为J2ee的一部分,以.war文件作为独立web应用 |
补充知识:
zip,jar,war,ear 其本质都是zip的拓展,都是压缩文件。war和jar拓展名不一样内容是一样的。
核心组件API | 说明 | 起始版本 | SpringFrameWork代表实现 |
---|---|---|---|
javax.servlet .Servlet | 动态内容组件 | 1.0 | DispatcherServlet |
javax.servlet.Filter | Servlet过滤器 | 2.3 | CharacterEncodingFilter |
javax.servlet .ServletContext | Servlet 应用上下文 | ||
javax.servlet .AsyncContext | 异步上下文 | 3.0 | 无 |
javax.servlet.ServletContextListener | ServletContext 生命周期监听器 | 2.3 | ContextLoaderListener |
javax.servlet.ServletRequestListener | ServletRequest 生命周期监听器 | 2.3 | RequestContextListener |
javaxservlet.http.HttpSessionListener | HttpSession 生命周期监听器 | 2.3 | HttpSessionMutexListener |
javax.servlet.AsyncListener | 异步上下文监听器 | 3.0 | StandardServletAsyncWebRequest |
javax.servlet.ServletContainerInitializer | Servlet 容器初始化器 | 3.0 | Springservletcoptelyong |
注:
Servlet规范重点章节 2,3,4,5,9,11,12 规范链接
CHAPTER 2 The Servlet Interface
CHAPTER 3 The Request
CHAPTER 4 Servlet Context
CHAPTER 5 The Response
CHAPTER 9 Dispatching Requests(非常重要)
CHAPTER 11Application Lifecycle Events
CHAPTER 12 Mapping Requests to Servlets(非常重要)
还需要看TomcatNIO
并非请求头(header)和请求体(body)都是异步读取的,要看Servlet容器的实现模型。
Tomcat官方文档
目前我们都不使用jsp了所以我们可以将jsp的servlet关掉。在tomcat中将此Servlet注释掉。
<servlet>
<servlet-name>jspservlet-name>
<servlet-class>org.apache.jasper.servlet.JspServletservlet-class>
<init-param>
<param-name>forkparam-name>
<param-value>falseparam-value>
init-param>
<init-param>
<param-name>xpoweredByparam-name>
<param-value>falseparam-value>
init-param>
<load-on-startup>3load-on-startup>
servlet>
因为jspServerlet 只是匹配jsp文件,要匹配静态文件我们需要用DefaultServlet来匹配。各个服务平台往往都是用default来命名,在springboot里静态文件的处理是forward(这个就是转发请求到下一个servlet) 到对应平台的Servlet来处理,从而适配了。所以在springboot 里面将css 和js文件放到static文件下面就可以读取到。
源码:
public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
// 这里定义了不同平台处理静态文件的servlet 名称
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
private static final String GAE_DEFAULT_SERVLET_NAME = "_ah_default";
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
@Nullable
private String defaultServletName;
@Nullable
private ServletContext servletContext;
public DefaultServletHttpRequestHandler() {
}
public void setDefaultServletName(String defaultServletName) {
this.defaultServletName = defaultServletName;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
if (!StringUtils.hasText(this.defaultServletName)) {
if (this.servletContext.getNamedDispatcher("default") != null) {
this.defaultServletName = "default";
} else if (this.servletContext.getNamedDispatcher("_ah_default") != null) {
this.defaultServletName = "_ah_default";
} else if (this.servletContext.getNamedDispatcher("resin-file") != null) {
this.defaultServletName = "resin-file";
} else if (this.servletContext.getNamedDispatcher("FileServlet") != null) {
this.defaultServletName = "FileServlet";
} else {
if (this.servletContext.getNamedDispatcher("SimpleFileServlet") == null) {
throw new IllegalStateException("Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.");
}
this.defaultServletName = "SimpleFileServlet";
}
}
}
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Assert.state(this.servletContext != null, "No ServletContext set");
RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
if (rd == null) {
throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" + this.defaultServletName + "'");
} else {
// 分发到对应的servlet 进行处理 因为他们都遵循了同一个规范
rd.forward(request, response);
}
}
}
关于default servlet 处理静态文件的一个配置示例
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>*.cssurl-pattern>
servlet-mapping>
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>*.jsurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>defaultservlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
<init-param>
<param-name>debugparam-name>
<param-value>0param-value>
init-param>
<init-param>
<param-name>listingsparam-name>
<param-value>falseparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
注解:
关于forward 的时候会不会经过过滤链,这需要在过滤链进行配置。关于执行顺序,先定义mapping的filter优先。
<filter-mapping>
<filter-name>CharsetEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
<dispatcher>REQUESTdispatcher>
<dispatcher>FORWARDdispatcher>
<dispatcher>INCLUDEdispatcher>
<dispatcher>ERRORdispatcher>
filter-mapping>
课堂上的补充知识点:
SPI:spi加载机制