『Enterprise Java - Servlet』 在 Java8中使Servlet 3.1

前言:

本文讲解了在 JAVA8的环境中使用 Servlet3.1的标准容器中使用 Http 上下文的方式来深入了解 Servlet,通过一个简单的Maven Web项目,可以通过您喜爱的浏览器或Postman来访问。

环境准备:

  • Java Version : 1.8.0_121
  • Maven Version:Apache Maven 3.3.9
  • IDE:IntelliJ IDEA (2017.2.3)
  • System:macOS Sierra

Servlet - 简介:

Servlet是基于Java技术的web组件,容器托管的,用于生成动态内容。它是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。像其他基于Java的组件技术一样,Servlet也是基于平台无关的Java类格式,被编译为平台无关的字节码,可以被基于Java技术的web server动态加载并运行。容器,有时候也叫做servlet引擎,是web server为支持servlet功能扩展的部分。客户端通过Servlet容器实现的请求/应答模型与Servlet交互。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:

  • 性能明显更好。
  • Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
  • Servlet 是独立于平台的,因为它们是用 Java 编写的。
  • 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
  • Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。

Servlet - 容器:

Servlet容器是web server或application server的一部分,提供基于请求/响应发送模型的网络服务,解码基于MIME的请求,并且格式化基于MIME的响应。Servlet 容器也包含了管理Servlet生命周期。

Servlet容器可以嵌入到宿主的web server中,或者通过Web Server的本地扩展API单独作为附加组件安装。Servelt容器也可能内嵌或安装到包含web功能的application server中。

所有Servlet容器必须支持基于HTTP协议的请求/响应模型,比如像基于HTTPS(HTTP over SSL)协议的请求/应答模型可以选择性的支持。容器必须实现的HTTP协议版本包含HTTP/1.0 和 HTTP/1.1。因为容器或许支持RFC2616 (HTTP/1.1)描述的缓存机制,缓存机制可能在将客户端请求交给Servlet处理之前修改它们,也可能在将Servlet生成的响应发送给客户端之前修改它们,或者可能根据RFC2616规范直接对请求作出响应而不交给Servlet进行处理。

Servlet容器应该使Servlet执行在一个安全限制的环境中。在Java平台标准版(J2SE, v.1.3 或更高) 或者 Java平台企业版(Java EE, v.1.3 或更高) 的环境下,这些限制应该被放置在Java平台定义的安全许可架构中。比如,高端的application server为了保证容器的其他组件不受到负面影响可能会限制Thread对象的创建。

Java SE 6是构建Servlet容器最低的Java平台版本。

事件序列:

以下是一个典型的事件序列:

1、客户端(如 web浏览器)发送一个HTTP请求到web服务器;

2、Web服务器接收到请求并且交给servlet容器处理,servlet容器可以运行在与宿主web服务器同一个进程中,也可以是同一主机的不同进程,或者位于不同的主机的web服务器中,对请求进行处理。

3、servlet容器根据servlet配置选择相应的servlet,并使用代表请求和响应对象的参数进行调用。

4、servlet通过请求对象得到远程用户,HTTP POST参数和其他有关数据可能作为请求的一部分随请求一起发送过来。Servlet执行我们编写的任意的逻辑,然后动态产生响应内容发送回客户端。发送数据到客户端是通过响应对象完成的。

5、一旦servlet完成请求的处理,servlet容器必须确保响应正确的刷出,并且将控制权还给宿主Web服务器。

Servlet - 规范:

许多供应商已经实施了servlet规范(例如:Tomcat,Jetty),尽管规范的确发展,供应商最终还是为我们部署我们的Web应用程序提供了实现。

通过JSR 340的提案,servlet 3.1规范在大版本3.0(JSR 315)上进行了迭代,允许我们的Web应用程序利用非阻塞IO和HTTP协议升级机制等功能。

在Servlet 3.0规范发布后的一大特点是不再需要一个web.xml 作为描述为我们所有的Servlet、过滤器、Listeners、init-PARAMS在 xml 文件中进行配置,大多数元数据、配置现在可以通过注释来完成。

此次使用web.xml 仅仅是为了在尝试访问安全路由时配置容器的登录过程。

web.xml:

1
2
3
4
5
6
7
8
9
10
11
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

<!-- 将以任何安全路由的基本身份验证来挑战用户 -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>servletbasics</realm-name>
</login-config>
</web-app>

Servlet - 生命周期:

Servlet是按照一个严格定义的生命周期被管理,该生命周期规定了Servlet如何被加载、实例化、初始化、处理客户端请求,以及何时结束服务。该声明周期可以通过javax.servlet.Servlet接口中的init、service和destroy这些API来表示,所有Servlet必须直接或间接的实现GenericServlet或HttpServlet抽象类。

以下是典型servlet的生命周期。

  1. init 实例化:

    用户访问这个Servlet,由容器实例化,它的init()方法被调用一次。通常,Servlet被实例化一次,并且在使用中产生大量并发,尽管容器可以汇集实现SingleThreadModel的多个servlet来处理重负载。

  2. 调用Service服务:
    Servi为每个请求调用Servlets的service()方法,如果您的servlet实现了HttpServlet接口,那么该请求将被委派给与您给定的请求动词匹配的任何便利方法。

  3. 销毁 Servlet:
    destroy()方法被调用,允许我们钩入生命周期,并终止servlet使用的任何资源。

  4. 垃圾收集:
    垃圾收集器收集servlet。

Servlet - 过滤器:

Servlet的过滤器旨在拦截对servlet,jsp或甚至静态HTML文件的请求。他们还将响应拦截回客户端,因此可用于修改请求/响应,有时甚至根据特定条件阻止或重定向它们。

其中包括:

  • 身份验证:拦截请求以防止未经身份验证的用户
  • 压缩:将响应压缩回客户端
  • 更改请求/响应体的交换格式
  • 跟踪请求/响应

Servlet - 监听器:

servlet规范允许我们定义WebListener,它可以对Web应用程序中发生的某些事件做出反应。事件可以在会话,请求,应用程序级别,不同类型的WebListener被设计为对不同的事件做出反应。

对于不同的场景,存在以下WebListeners:

Scope WebListener 接口 Event
Web context ServletContextListener
ServletContextAttributeListener
ServletContextEvent
ServletContextAttributeEvent
Session HttpSessionListener
HttpSessionActivationListener
HttpSessionAttributeListener
HttpSessionEvent
HttpSessionEvent
HttpSessionBindingEvent
Request ServletRequestListener
ServletRequestAttributeListener
ServletRequestEvent
ServletRequestAttributeEvent

Servlet - Context:

Servlet - 注解:

在web应用中,使用注解的类仅当它们位于WEB-INF/classes目录中,或它们被打包到位于应用的WEB-INF/lib中的jar文件中时它们的注解才将被处理。

Web应用部署描述符的web-app元素包含一个新的“metadata-complete”属性。“metadata-complete”属性定义了web描述符是否是完整的,或是否应该在部署时检查jar包中的类文件和web fragments。如果“metadata-complete”设置为“true”,部署工具必须必须忽略存在于应用的类文件中的所有指定部署信息的servlet注解和web fragments。如果metadata-complete属性没有指定或设置为“false”,部署工具必须检查应用的类文件的注解,并扫描web fragments。

以下注解必须被Servlet 3.0兼容的容器支持。

@WebServlet:

该注解用于在Web应用中定义Servlet组件。该注解在一个类上指定并包含声明Servlet的元数据。必须指定注解的urlPatterns或value属性。所有其他属性是可选的默认设置。@WebServlet注解的类必须继承javax.servlet.http.HttpServlet类。

使用示例:

1
2
3
4
@WebServlet(”/foo”)
public class CalculatorServlet extends HttpServlet{
//...
}

@WebFilter:

该注解用于在Web应用中定义Filter。该注解在一个类上指定且包含声明过滤器的元数据。如果没有指定Filter名字则默认是全限定类名。注解的urlPatterns属性, servletNames 属性 或 value 属性必须被指定。所有其他属性是可选的默认设置。@WebFilter注解的类必须实现javax.servlet.Filter。

使用示例:

1
2
3
4
5
6
@WebFilter(“/foo”)
public class MyFilter implements Filter {
public void doFilter(HttpServletRequest req, HttpServletResponse res) {
...
}
}

@WebInitPara:

该注解用于指定必须传递到Servlet或Filter的任何初始化参数。它是WebServlet和WebFilter注解的一个属性。

@WebListener:

WebListener注解用于注解用来获得特定web应用上下文中的各种操作事件的监听器。@WebListener注解的类必须实现以下接口:

  • javax.servlet.ServletContextListener
  • javax.servlet.ServletContextAttributeListener
  • javax.servlet.ServletRequestListener
  • javax.servlet.ServletRequestAttributeListener
  • javax.servlet.http.HttpSessionListener
  • javax.servlet.http.HttpSessionAttributeListener
  • javax.servlet.http.HttpSessionIdListener

使用示例:

1
2
3
4
5
6
7
8
@WebListener
public class MyListener implements ServletContextListener{
public void contextInitialized(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
sc.addServlet("myServlet", "Sample servlet", "foo.bar.MyServlet", null, -1);
sc.addServletMapping("myServlet", new String[] { "/urlpattern/*" });
}
}

@MultipartConfig:

该注解,当指定在Servlet上时,表示请求期望是mime/multipart类型。相应servlet的HttpServletRequest对象必须使用getParts和getPart方法遍历各个mime附件以获取mime附件。javax.servlet.annotation.MultipartConfig的location属性和元素被解析为一个绝对路径且默认为javax.servlet.context.tempdir。如果指定了相对地址,它将是相对于tempdir位置。绝对路径与相对地址的测试必须使用java.io.File.isAbsolute。

Servlet - 异步操作:

异步处理在重负载或在客户端和服务器之间以不同速度读取和写入大量数据的情况下特别有用,意味着两个实体之一可能会等待来自另一个的输入。

在servlet 3.0规范中,引入了servlet内部的异步处理,允许长时间运行的任务在一个单独的线程中完成,以允许请求线程返回到从它处理其他请求的池中。

通过servlet 3.1规范,我们得到了能够以异步方式在客户端和服务器之间读写数据的功能,从而允许以非阻塞方式异步处理客户端和服务器之间的长时间读写操作,特别适用于大型读取和写入时可能阻止的数据流以不同的速度完成。这些功能通过ReadListenerWriteListener接口来实现。

作为servlet 3.1规范的一部分,我们为servlet和过滤器提供异步处理支持。

参考资料:

JCP(Java Servlet 3.1规范):https://jcp.org/en/jsr/detail?id=340