Servlet 3.1实践
Servlet 3.1 新特性详解
参考:
关键特性
- Asynchronization(异步支持): 通过AsyncContext另起异步线程处理业务逻辑, 不再阻塞Filter或Servlet线程. 可以实现Reactor模式.
- Pluggability(插拨支持): 通过Annotation(@WebServlet, @WebFilter, @WebListener, @WebInitParam, @MultipartConfig, @ServletSecurity, @HttpConstraint, @HttpMethodConstraint)或/META-INF/web-fragment.xml充分解耦组件,实现插拨机制.
注意: 在web.xml设置metadata-complete="true"可以关闭Pluggability特性. 但不影响@HandlesTypes+ServletContainerInitializer的动态注册机制. - Fileupload(文件上传): 通过@MultipartConfig+Part实现文件上传. 不再需要apache-fileupload.
- Dynamic Registration(动态注册):
- 通过ServletContext.addXXX()+Registration实现.
- 相关API只能在ServletContextListener.contextInitialized()或者ServletContainerInitializer.onStartup()中调用. 后者是基于java服务API机制实现, 需要在(/META-INF/services/javax.servlet.ServletContainerInitializer)配置实现类.
- 动态注册不受web.xml中metadata-complete="true"影响,但优先级低于web.xml, 相同设置会被覆盖.
以上4点是Servlet 3.0/3.1的关键特性, 其中
- Pluggabilityr的目标是"充分解耦,实现插拨"
- Dynamic Registration的目标是"动态注册,编程"
- Asynchronization的目标是提高并发性能
- FileUpload, Annotation是增加编程方便
Servlet 3.1 新特性练习
Jetty9对于Servlet 3.1的支持很不友善, 配置起来令人不舒服! 在Tomcat 7/8/9都支持Embedded. 后面使用Tomcat8(要求JDK7, 支持Servlet3.1/JSP2.3)练习.
准备工具: 配置嵌入式Tomcat
Maven依赖:
<properties>
<spring.version>4.2.7.RELEASE</spring.version>
<tomcat.version>8.5.5</tomcat.version>
</properties>
<dependencies>
<!-- ================================ -->
<!-- spring test frameworks -->
<!-- ================================ -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- ================================ -->
<!-- junit frameworks -->
<!-- ================================ -->
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.stefanbirkner/system-rules -->
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>1.16.1</version>
</dependency>
<!-- ================================ -->
<!-- tomcat frameworks -->
<!-- ================================ -->
<!-- https://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat#add-a-launcher-class -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Java实现:
public final class EmbedTomcat extends RiseJUnitTester {
public static final String WEBAPP_DIRECTORY = "src/main/webapp/";
public static final String ROOT_CONTEXT = "";
public static final int HTTP_PORT = 80;
public static final int HTTPS_PORT = 443;
public static final int OFF = -1;
public static void start() {
start(ROOT_CONTEXT, HTTP_PORT, OFF, null, null, null);
}
public static void start(int httpPort) {
start(ROOT_CONTEXT, httpPort, OFF, null, null, null);
}
public static void start(String keyAlias, String password, String keystorePath) {
start(ROOT_CONTEXT, OFF, HTTPS_PORT, keyAlias, password, keystorePath);
}
public static void start(int httpsPort, String keyAlias, String password, String keystorePath) {
start(ROOT_CONTEXT, OFF, httpsPort, keyAlias, password, keystorePath);
}
public static void start(int httpPort, int httpsPort, String keyAlias, String password, String keystorePath) {
start(ROOT_CONTEXT, httpPort, httpsPort, keyAlias, password, keystorePath);
}
public static void start(String contextPath, int httpPort, int httpsPort, String keyAlias, String password, String keystorePath) {
// FIX: A context path must either be an empty string or start with a '/' and do not end with a '/'.
if (contextPath == null || contextPath.equals("/")) {
contextPath = ROOT_CONTEXT;
}
try {
// initial
processSystemEnvironment();
Tomcat tomcat = new Tomcat();
if (httpsPort > 0) {
tomcat.setPort(httpsPort);
Connector httpsConnector = tomcat.getConnector();
httpsConnector.setSecure(true);
httpsConnector.setScheme("https");
httpsConnector.setAttribute("keyAlias", keyAlias);
httpsConnector.setAttribute("keystorePass", password);
httpsConnector.setAttribute("keystoreFile", keystorePath);
httpsConnector.setAttribute("clientAuth", "false");
httpsConnector.setAttribute("sslProtocol", "TLS");
httpsConnector.setAttribute("SSLEnabled", true);
if (httpPort > 0) {
Connector httpConnector = new Connector();
httpConnector.setPort(httpPort);
httpConnector.setRedirectPort(httpsPort);
tomcat.getService().addConnector(httpConnector);
}
} else if (httpPort > 0) {
tomcat.setPort(httpPort);
}
StandardContext ctx = (StandardContext) tomcat.addWebapp(contextPath, new File(WEBAPP_DIRECTORY).getAbsolutePath());
// Declare an alternative location for your "WEB-INF/classes" dir Servlet 3.0 annotation will work
WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", new File("target/classes").getAbsolutePath(), "/"));
ctx.setResources(resources);
ctx.setJarScanner(new WebappStandardJarScanner());
ctx.setDefaultWebXml(new File("src/main/webapp/WEB-INF/web.xml").getAbsolutePath());
// FixBug: no global web.xml found
for (LifecycleListener ll : ctx.findLifecycleListeners()) {
if (ll instanceof ContextConfig) {
((ContextConfig) ll).setDefaultWebXml(ctx.getDefaultWebXml());
}
}
tomcat.start();
tomcat.getServer().await();
} catch (Exception e) {
throw new RuntimeException("tomcat launch failed", e);
}
}
}
代码功能:
- 支持http启动
- 支持https启动, 需用keytool创建keystore.
- 支持http + https启动, 其中http转发https.
以上用于测试
另外Tomcat自带的StandardJarScanner的processManifest()在jar的META-INF/manif.mf含有"Class-Path"项时处理不大正确. 复制其源码保存为WebappStandardJarScanner.java(package相同),并注释下述语句:
private static final Set<ClassLoader> CLASSLOADER_HIERARCHY;
static {
Set<ClassLoader> cls = new HashSet<>();
// FixBug: fail to scan
// ClassLoader cl = WebappStandardJarScanner.class.getClassLoader();
// while (cl != null) {
// cls.add(cl);
// cl = cl.getParent();
// }
CLASSLOADER_HIERARCHY = Collections.unmodifiableSet(cls);
}
准备工作: 创建war项目
Maven配置
<dependencies>
<!-- https://mvnrepository.com/artifact/org.eclipse.jetty.aggregate/jetty-all -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- 准备工作: 嵌入式Tomcat -->
<dependency>
<groupId>com.yy.risedev</groupId>
<artifactId>risedev-test</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- compiler plugin -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- source plugin -->
<plugin>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 无web.xml时maven检测错误 -->
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
注意failOnMissingWebXml需要配置为false, 否则maven会显示表示错误的红叉叉...
练习1: web.xml 与 ServletContainerIntializer同时存在
- 在web.xml配置/test的Servlet,其类型为TestServlet2
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="true">
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>servlet.TestServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
ServletContainerIntializer实现
- 在/META-INF/services/javax.servlet.ServletContainerInitializer设置实现类. 嗯! 就是一句话
servlet.MyServletContainerInitializer
- MyServletContainerInitializer代码:
package servlet; import java.util.Set; import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes; @HandlesTypes(WebAppIntializer.class)
public class MyServletContainerInitializer implements ServletContainerInitializer { @Override
public void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException {
for (Class<?> c : arg0) {
if (WebAppIntializer.class.isAssignableFrom(c)) {
try {
((TestWebAppIntializer) (c.newInstance())).onStartup(arg1);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
} }- WebAppIntializer代码:
package servlet; import javax.servlet.ServletContext;
import javax.servlet.ServletException; public interface WebAppIntializer {
void onStartup(ServletContext ctx) throws ServletException;
}- TestWebAppIntializer代码:
package servlet; import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration; public class TestWebAppIntializer implements WebAppIntializer { public void onStartup(ServletContext ctx) throws ServletException {
ServletRegistration.Dynamic dyna = ctx.addServlet("testServlet", "servlet.TestServlet");
dyna.addMapping("/test");
} }通过java服务API,会扫描@HandlesTypes的类型,然后传递给MyServletContainerInitializer.onStartup()执行. 假意相识的感觉? 这是Spring IOC的DI特性. 看来Spring的影响不小哦!
执行结果: 在访问http://localhost/test时,执行的是TestServlet的逻辑, 而非TestServlet2. 即web.xml配置覆盖了动态注册. 若改成/test2, 则能访问到动态注册的TestServlet2了. 同时, 也可看到metadata-complete="true"不影响动态注册.
练习2: web.xml与web-fragment.xml同时存在
- /META-INF/web-fragment.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
version="3.0" metadata-complete="true">
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>servlet.TestServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test2</url-pattern>
</servlet-mapping>
</web-fragment>
- /WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="false">
</web-app>
注意: metadata-complete="false", 否则pluggability特性会被关闭.
- 执行结果: 访问http://localhost/test,调用TestServlet2的业务逻辑,证明web-fragment.xml也会覆盖动态注册的
后续练习...
随时有想法都可以快速尝试
Servlet 3.1实践的更多相关文章
- 异步Servlet的理解与实践
AsyncContext理解 Servlet 3.0(JSR315)定义了Servlet/Filter的异步特性规范. 怎么理解"异步Servlet/Filter"及其使用情景? ...
- Java Web基础 --- Servlet 综述(理论篇)
摘要: Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础.本文首先从请求/响应架构应用的大背景谈起 Servlet 的由来,明确 Ser ...
- JSP的简单介绍
什么是JSP? JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术. JSP这门技术的最大的特点在于,写jsp就像在写htm ...
- paip.spring3 mvc servlet的配置以及使用最佳实践
paip.spring3 mvc servlet的配置以及使用最佳实践 1. Web.xml 1 2. springMVC.xml 2 1. mvcAction .mvcAction 2 2. Res ...
- 云原生实践之 RSocket 从入门到落地:Servlet vs RSocket
技术实践的作用在于:除了用于构建业务,也是为了验证某项技术或框架是否值得大规模推广. 本期开始,我们推出<RSocket 从入门到落地>系列文章,通过实例和对比来介绍RSocket.主要围 ...
- Java Servlet开发的轻量级MVC框架最佳实践
在Servlet开发的工程实践中,为了减少过多的业务Servlet编写,会采用构建公共Servlet的方式,通过反射来搭建轻量级的MVC框架,从而加快应用开发. 关于Servlet开发的基础知识,请看 ...
- 最佳实践: 勿在 Servlet 中实现 SingleThreadModel
摘要 请不要实现 SingleThreadModel 接口.这种实践将导致 Web 容器创建多个 servlet 实例:即为每个用户创建一个实例.对于任何大小的应用程序,这种实践都将导致严重的性能问题 ...
- Java Servlet DAO实践(二)
Java Servlet DAO实践(二) DAO连接类 package com.seller.servlets.dao; import java.sql.*; public class DataBa ...
- Servlet和JSP学习指导与实践(三):JSP助阵
前言: JSP(Java Server Page)虽然作为一门服务端的语言,但它并没有创新新的语言标准.有些人一接触jsp之后发现易学易懂.实际上,jsp的内部原理仍然是基于Servlet,它是Ser ...
随机推荐
- Visual Studio 项目目录下的bin目录和 obj目录
一.Bin目录 Visual Studio 编译时,在bin 目录下有debug 和 release 目录. 1.Debug: 通常称为调试版本,它包含调试信息,所以要比Release 版本大很多(可 ...
- iOS中OC给Category加入属性
引: 非常多人知道能够用Category给已有的类加入一些新方法,可是不同于swift中的extension,Objective-C中的Category(类别)是不支持直接加入属性的.那假设就是须要加 ...
- php实现 密码验证合格程序(复杂问题分类,超简单的)(分类+规范编码)
php实现 密码验证合格程序(复杂问题分类,超简单的)(分类+规范编码) 一.总结 一句话总结:复杂问题分类,超简单的.分类+规范编码. 1.写的时候判断 不能有相同长度超2的子串重复 的时候,子 ...
- [Git] Use git add --patch for better commit history and mitigating bugs
Let's split our changes into separate commits. We'll be able to check over our changes before stagin ...
- [Angular] ChangeDetection -- onPush
To understand how change detection can help us improve the proference, we need to understand when it ...
- wait()、notify()、notifyAll()与线程通信方式总结
1.通过wait().notify().notifyAll()进行线程通信 线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号.例如,线程B可以等待线程A的一个信 ...
- Opencv 使用Stitcher类图像拼接生成全景图像
Opencv中自带的Stitcher类可以实现全景图像,效果不错.下边的例子是Opencv Samples中的stitching.cpp的简化,源文件可以在这个路径里找到: \opencv\sourc ...
- 【前端统计图】echarts多条折线图和横柱状图实现
参考链接:echarts官网:http://echarts.baidu.com/ 原型图(效果图): 图片.png 代码: <!DOCTYPE html> <html> < ...
- 【codeforces 791D】 Bear and Tree Jumps
[题目链接]:http://codeforces.com/contest/791/problem/D [题意] 你可以从树上的节点一次最多走k条边. (称为跳一次); 树为无权树; 然后问你任意两点之 ...
- redux相关学习资源
很多学习资料,直接在SF.掘金搜索关键词redux源码等可以获得. redux参考版本3.6或3.7.2 redux-thunk看1.0.1 1.redux源码分析之四:compose函数 ...