Servlet简介
Servlet 是一种基于 Java 技术的 Web 组件,用于生成动态内容,由容器管理。类似于其他 Java 技术组件,Servlet 是平台无 关的 Java 类组成,并且由 Java Web 服务器加载执行。通常情况,由 Servlet 容器提供运行时环境。Servlet 容器,有时候也 称作为 Servlet 引擎,作为Web服务器或应用服务器的一部分。通过请求和响应对话,提供Web 客户端与 Servlets 交互的能 力。容器管理Servlets实例以及它们的生命周期。
从功能上,Servlet 介于 CGI(Common Gateway Interface)与服务扩展(如:Netscape Server API 或 Apache 模块)之 间。
在体系上,Servlet 技术(或者规范)属于 Java EE 技术(规范)的一部分。不过 Servlet 并非一开始就隶属于 J2EE 或者 Java EE。接下来的小节将会介绍 Servlet 各个版本。
Servlet 版本
规范版本 | 发布时间 | Java平台 | 主要更新 |
---|---|---|---|
Servlet 4.0 | 2017年9月 | Java EE 8 | 支持Http/2 |
Servlet 3.1 | 2013年5月 | Java EE 7 | 非阻塞I/O、Http协议更新机制(WebSocket) |
Servlet 3.0 | 2009年12月 | Jave EE 6 | 可插拔、简化部署、异步Servlet、安全、文件上传 |
Servlet 2.5 | 2005年9月 | Java EE 5 | Annotation支持 |
Servlet 2.4 | 2003年11月 | J2EE 1.4 | web.xml 支持 XML Scheme |
Servlet 2.3 | 2001年8月 | J2EE 1.3 | 新增Filter、事件/监听器、Wrapper |
Servlet 2.2 | 1999年8月 | J2EE 1.2 | 作为J2EE的一部分,以.war 文件作为独立web应用 |
Servlet 核心API
核心组件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 |
javax.servlet.http.HttpSessionListener | HttpSession生命周期监听器 | 2.3 | HttpSessionMutexListener |
javax.servlet.AsyncListener | 异步上下文监听器 | 3.0 | StandardServletAsyncWebRequest |
javax.servlet.ServletContainerInitializer | Servlet容器初始化器 | 3.0 | SpringServletContainerInitializer |
Servlet 组件注册
Servlet注册
注册方式 | 传统方式 | 注解方式 | 编程方式 |
---|---|---|---|
Servlet注册 | web.xml 部署<servlet> + <servlet-mapping> | @WebServlet | ServletContext#addServlet |
Fileter注册 | web.xml部署<filter> + <filter-mapping> | @WebFilter | ServletContext#addFilter |
*Listener注册 | web.xml 部署<listener> | @WebListener | ServletContext#addListener |
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-
app_2_5.xsd"
metadata-complete="true" version="2.5">
<context-param>
<description>
Spring 配置文件路径参数,
该参数值将被 org.springframework.web.context.ContextLoaderListener 使用 </description>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/META-INF/spring/spring-context.xml
</param-value>
</context-param>
<listener>
<description>
org.springframework.web.context.ContextLoaderListener 为可选申明Listener </description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
Spring Servlet Web
理解Servlet
生命周期
- 初始化:init(ServletConfig)
- 服务:service(ServletRequest,ServletResponse)
- 销毁:destroy()
DispatcherServlet
初始化过程
Servlet初始化过程
理解Filter
生命周期
- 初始化
- 服务
- 销毁
理解ServletContext
生命周期
- 初始化:
contextInitialized(ServletContextEvent)
- 销毁:
contextDestroyed(ServletContextEvent)
Servlet 异步支持
@WebServlet(asyncSupported = true,//激活异步特性
name = "asyncServlet",// servlet 名字
urlPatterns = "/async-servlet")
public class AsyncServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
// 判断是否支持异步
if (request.isAsyncSupported()) {
// 创建 AsyncContext
AsyncContext asyncContext = request.startAsync();
// 设置超时时间
asyncContext.setTimeout(50L);
asyncContext.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent event) throws IOException {
println("执行完成");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
HttpServletResponse servletResponse = (HttpServletResponse) event.getSuppliedResponse();
servletResponse.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
println("执行超时");
}
@Override
public void onError(AsyncEvent event) throws IOException {
println("执行错误");
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
println("开始执行");
}
});
}
println("Hello,World");
// ServletResponse servletResponse = asyncContext.getResponse();
// // 设置响应媒体类型
// servletResponse.setContentType("text/plain;charset=UTF-8");
// // 获取字符输出流
// PrintWriter writer = servletResponse.getWriter();
// writer.println("Hello,World");
// writer.flush();
}
private static void println(Object object) {
String threadName = Thread.currentThread().getName();
System.out.println("AsyncServlet[" + threadName + "]: " + object);
}
}
DeferredResult 支持
@GetMapping("/hello-world")
public DeferredResult<String> helloWorld() {
DeferredResult<String> result = new DeferredResult<>(50L);
// result.setResult("Hello,World");
// 入队操作
// queue.offer(result);
println("Hello,World");
result.onCompletion(() -> {
println("执行结束");
});
result.onTimeout(() -> {
println("执行超时");
});
return result;
}
Callable支持
@GetMapping("/callable-hello-world")
public Callable<String> callableHelloWorld() {
final long startTime = System.currentTimeMillis();
println("Hello,World");
return () -> {
long costTime = System.currentTimeMillis() - startTime;
println("执行计算结果,消耗:" + costTime + " ms.");
return "Hello,World";
};
}
CompletionStage支持
@GetMapping("/completion-stage")
public CompletionStage<String> completionStage(){
final long startTime = System.currentTimeMillis();
println("Hello,World");
return CompletableFuture.supplyAsync(()->{
long costTime = System.currentTimeMillis() - startTime;
println("执行计算结果,消耗:" + costTime + " ms.");
return "Hello,World"; // 异步执行结果
});
}
Spring Web MVC异步Servlet实现原理
Java Specification Requests (JSR) : https://github.com/mercyblitz/jsr
Spring Boot Servlet Web
Spring Boot嵌入式Servlet容器限制
Servlet特性 | 兼容性 | 解决方案 |
---|---|---|
web.xml | 不支持 | RegistrationBean或 @Bean 注册 |
ServletContainerInitializer | 不支持 | ServletContextInitializer |
@WebServlet等 | 有限支持 | 依赖@ServletComponentScan |
参考资料一
87.2 Convert an Existing Application to Spring Boot
you may need to add some configuration to your Application context, by replacing those elements from the web.xml , as follows:
A @Bean of type Servlet or ServletRegistrationBean installs that bean in the container as if it were a <servlet/> and <servlet-mapping/> in web.xml .
A @Bean of type Filter or FilterRegistrationBean behaves similarly (as a <filter/> and <filter- mapping/> ).
An ApplicationContext in an XML file can be added through an @ImportResource in your Application . Alternatively, simple cases where annotation configuration is heavily used already can be recreated in a few lines as @Bean definitions.
参考资料二
27.4.2 Servlet Context Initialization
Embedded servlet containers do not directly execute the Servlet 3.0+
javax.servlet.ServletContainerInitializer interface or
Spring’s org.springframework.web.WebApplicationInitializer interface. This is an intentional design decision intended to reduce the risk that third party libraries designed to run inside a war may break Spring Boot applications.
参考资料三
Scanning for Servlets, Filters, and listeners
When using an embedded container, automatic registration of classes annotated
with @WebServlet , @WebFilter , and @WebListener can be enabled by using @ServletComponentScan .
Spring Boot Servlet注册
通过RegistrationBean
注册
-
ServletContextInitializer
-
RegistrationBean
-
ServletListenerRegistrationBean
@WebListener
-
FilterRegistrationBean
@WebFilter
-
ServletRegistrationBean
@WebServlet
-
@ServletComponentSacn
扫描package->@Web* ->RegistrationBean
Bean定义 ->RegistrationBean
Bean -
通过@Bean
注册
通过@ServletComponentScan
注册
Spring Boot应用传统Servlet容器部署
基本原理
扩展SpringBootServletInitializer
public class DefaultSpringBootServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
builder.sources(SpringBootServletBootstrap.class);
return builder;
}
}
@EnableAutoConfiguration
@ServletComponentScan(basePackages = "com.imooc.spring.web.servlet")
class SpringBootServletBootstrap {
public static void main(String[] args) {
SpringApplication.run(SpringBootServletBootstrap.class, args);
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ServletRegistrationBean asyncServletServletRegistrationBean(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new AsyncServlet(),"/");
registrationBean.setName("MyAsyncServlet");
return registrationBean;
}
@Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
FilterRegistration.Dynamic registration = servletContext.addFilter("filter", filter);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/");
};
}
}
使用Tomcat 7插件(Servlet3.0)
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>tomcat-run</id>
<goals>
<goal>exec-war-only</goal>
</goals>
<phase>package</phase>
<configuration>
<!-- ServletContext path -->
<path>/</path>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
使用 Tomcat 8 插件(Servlet 3.1)
<build>
<plugins>
<!-- Tomcat 8 Maven 插件用于构建可执行 war -->
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.maven/tomcat8-maven-plugin --> <plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat8-maven-plugin</artifactId>
<version>3.0-r1655215</version>
<executions>
<execution>
<id>tomcat-run</id>
<goals>
<!-- 最终打包成可执行的jar包 -->
<goal>exec-war-only</goal>
</goals>
<phase>package</phase>
<configuration>
<!-- ServletContext 路径 -->
<path>/</path>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<pluginRepositories>
<pluginRepository>
<!-- tomcat8-maven-plugin 所在仓库 -->
<id>Alfresco</id>
<name>Alfresco Repository</name> <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url> <snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
网友评论