Servlet4.0

Servlet4.0

 

参考文章:

https://developer.ibm.com/zh/tutorials/j-javaee8-servlet4/

 

 

概述

使用Servlet4.0

服务器推送

HttpServletMapping

Servlet4.0的细微变化

题外话

ServletRequest和HttpServletRequest区别

ServletRequest

HttpServletRequest

 

 

 

概述

Servlet API是Java开发人员最熟悉的API之一,Servlet在1999年所发布的J2SE1.2版本中首次面世,Servlet在JavaWEB的开发中发挥着重要的作用。JavaEE8对Servlet4.0进行了重要的更新。其中服务器推送是最主要的更新,如果要使用服务器推送的功能,则我们必须使用HTTP/2.0版本的协议。JavaEE8提供了对Servlet映射的运行时发现,在运行时我们可以获取Servlet的名称,Servlet的映射路径。JavaEE8简化了对Filter的开发。

IDEA 2017.3版本才开始提供对JAVAEE8的支持,本文使用的是IDEA 2018.3版本,默认只支持Servlet4.0。

开发环境:jdk8,tomcat9,tomcat-native,openssl

 

 

使用Servlet4.0

Servlet4.0是使用HTTP/2协议,而Tomcat9下载后,默认使用的是HTTP/1.1,所以我们要先修改server.xml配置文件:

1、注释原有的默认使用HTTP/1.1方式

<!-- 
      <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
               -->

  

2、开启HTTP2的注释,并删除certificateChainFile这一行HTTP2使用的端口不在是8080,而是8443,使用HTTP2需要配置一个私钥文件和一个证书文件。

 <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
               maxThreads="150" SSLEnabled="true" >
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                         certificateFile="conf/localhost-rsa-cert.pem"
                         certificateChainFile="conf/localhost-rsa-chain.pem"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>

  

关于上面Certificate标签有关说明:当你申请证书时或获取一个证书crt和一个私钥key文件,certificateKeyFile放的就是你的证书key文件,certificateFile则是私钥cert文件,默认放在Tomcat9的conf目录下。

certificateChainFile:一般操作系统/浏览器会内置一些CA(证书颁发机构)的证书,如果你的证书是直接由这些内置CA颁发的,那么就不需要Chain文件,浏览器可以直接识别你的证书。如果你的证书是由二级CA颁发的,即内置CA颁发给另一个二级CA,然后二级CA在颁发给你,那么就需要Chain文件,否则浏览器就不知道你的证书和内置CA的关系。如果你将自记得CA设置成了内置CA,那么直接由你的CA颁发的证书自然不需要Chain文件。

 

 

3、现在我们需要生成server.xml配置中的证书和私钥文件,在这里我是使用openssl生成的,openssl windows版本网盘下载地址,提取码:0sra ,网盘分享的openssl是win32版本的,由于windows64是兼容windows32的,所以不必担心不能使用的问题。解压开后,进入bin目录,启动openssl.exe

生成私钥:

OpenSSL> genrsa -out localhost-rsa-key.pem 1024

  

使用配置文件生成证书(一路回撤即可):

OpenSSL> req -new -x509 -key localhost-rsa-key.pem -out localhost-rsa-cert.pem -days 3650 -config D:softopensslopenssl-0.9.8k_WIN32inopenssl.cnf

  

4、将第三步生成了两个文件,放入tomcat9的conf目录下。

 

5、下载tomcat-nativate文件并解压,如果你是win32系统直接将bin目录下tcnative-1.dll和tcnative-1-src.pdb文件复制到jdk的bin目录下,如果你是win64则将x64文件下的这两个文件复制到jdk目录下。

至此我们已经完成了Tomcat9的HTTP协议的升级,现在你可以启动Tomcat9,然后访问https://localhost:8443/即可看到tomcat页面。

服务器推送

将用户所需的WEB资源提前推送到用户的浏览器缓存中,当用户使用浏览器访问所需WEB资源时,用户不需要再次下载所需的资源,因为用户所需的WEB资源已经存在与用户的浏览器缓存中。

如下内容摘自这里

Servlet4.0通过PushBuilder接口公开服务器推送。为了能够进行访问,你需要通过调用newPushBuilder()方法,从HttpServletRequest获取PushBuilder实例。

@Override
protected void doGet(HttpServletRequest request,
                     HttpServletResponse response)
                     throws ServletException, IOException {

   PushBuilder pushBuilder = request.newPushBuilder();

}

  

每次调用newPushBuilder()方法时,都将返回PushBuilder的新实例。如果服务器推送不可用,newPushBuilder()将返回null。在某些情况下,客户端可能会为请求事务拒绝服务器推送。如果客户端没有使用安全连接,服务器推送也不会起作用。因此,务必要在对PushBuilder实例调用方法前,针对null返回值进行测试。

顾名思义,PushBuilder实现Builder模式。在这一实现过程中,通过链接赋值方法构建推送请求。这些赋值方法通过设置请求HTTP标头、方法类型(GET是唯一的可接受值)、查询字符串、会话ID和资源路径(即,将要推出的资源的路径),来配置PushBuilder实例。

大多数来自原始HtpServletRequest实例的请求标头,只添加到PushBuilder实例中。由于正确运行服务器推送并不需要某些标头,因此不包括以下标头:

 条件标头

 Ranfe标头

 Authorization标头

 Referrer标头

设置推送资源路径需要调用path()方法。该方法只能被调用一次,因为它会改变PushBuilder对象的路径值。该路径可能会以正斜杠(”/“)开头,指示资源路径是绝对路径;否则,该资源会被认为是相对于关联请求的上下文路径。该路径可以包含一个查询字符串,该查询字符串将与queryString()方法设置的任何字符串合并。

 

测试:

准备:使用一张图片,复制一份并改名。如下代码hello.jpg和hey.jpg使用的是同一张图片,保证大小一致。清空浏览器缓存

不使用服务器推送:

@WebServlet("/NoPushServlet")
public class NoPushServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("nopush.jsp").forward(req, resp);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
Hello
<p><img src="./hello.jpg" alt=""></p>
</body>
</html>

 

不使用服务器推送,浏览器渲染hello.jsp页面时会再次请求后台下载hello.jpg图片,这会再次创建一个HTTP链接,发送请求耗时0.16ms,等待响应耗时6.49ms,最后下载图片Content Download耗时1.13ms。

 

使用服务器推送:

/**
 * 将用户所需的WEB资源提前推送到用户的浏览器缓存中,当用户使用浏览器访问所需WEB资源时,
 * 用户不需要再次下载所需的资源,因为用户所需的WEB资源已经存在与用户的浏览器缓存中。
 */
@WebServlet("/PushServlet")
public class PushServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PushBuilder pushBuilder = req.newPushBuilder();
        if (pushBuilder != null) {
            PushBuilder path = pushBuilder.path("./hey.jpg");
            path.push();

            // 只推送最后一个path里的资源
//        PushBuilder path = pushBuilder.path("./hey.jpg").path("./hello.jpg");
//        path.push();

            // 推送多个资源的写法
//        pushBuilder.path("./hey.jpg").push();
//        pushBuilder.path("./hello.jpg").push();
        }


        req.getRequestDispatcher("push.jsp").forward(req, resp);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
Hello
<p><img src="./hey.jpg" alt=""></p>
</body>
</html>

 

使用服务器推送,效果很明显,不需要向服务器二次请求建立连接,因为图片在建立Servlet链接请求的时候,服务器端就已经推送过来了,所以在PushServlet时可以看到,这张图比NoPushServlet中Content Download多耗了一点时间,因为要多下载一张图。总体上相当于省下了hey.jpg建立链接的时间。

如果一个页面上有很多张图时候,使用服务器推送技术可以省下的时间也会是一个可观的数字。

 

 

HttpServletMapping

用于在运行时获取Servlet的映射信息,它是Servlet4.0新增的接口,含有四个方法:

 getMappingMatch():返回匹配的类型(如果没有则返回null)

 getMatchValue():返回匹配的值(如果没有返回空字符串)

 getPattern():与此请求匹配的url模式,如果未知则为空String。

 getServletName():映射到请求的servlet的名称(在web.xml,WebServlet.name(),ServletContext.addServlet(String,Class)或其他addServlet()指定的方法之一)

 

@WebServlet(value = "/myServletMapping", name = "my-servlet-mapping")
public class MyServletMapping extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // HttpServletMapping用于在运行时获取Servlet的映射信息
        HttpServletMapping httpServletMapping = req.getHttpServletMapping();

        // getMappingMatch返回匹配的值(如果没有返回空字符串)
        MappingMatch mappingMatch = httpServletMapping.getMappingMatch(); // EXACT

        // 返回匹配的值(如果没有返回空字符串)
        String matchValue = httpServletMapping.getMatchValue(); // myServletMapping

        // 与此请求匹配的url,如果未知则为空String。
        String pattern = httpServletMapping.getPattern(); // /myServletMapping

        // 映射到请求的servlet的名称,如果没写默认是全路径名称
        String servletName = httpServletMapping.getServletName(); // my-servlet-mapping

    }
}

 

Servlet4.0的细微变化

 Servlet4.0添加了GenericFilter和HttpFilter抽象类,这些抽象类通过提供最低限度地实现生命周期方法init()和destory(),简化了编写过滤器。

 ServletContext 接口采用了一些新方法:

 addJspFile() 可将带有给定 JSP 文件的 servlet 添加到 servlet 上下文中。

 getSessionTimeout() 和 setSessionTimeout() 可提供对会话超时的访问权限。

 getRequestCharacterEncoding() 和 setRequestCharacterEncoding() 可为当前的 servlet 上下文提供访问权限,并改变默认的请求字符编码。

 HttpServletRequest 接口上的 isRequestedSessionIdFromUrl() 方法已被弃用。

 由于升级到 Java SE 8,默认方法已被添加到侦听器接口中。

 

 

题外话

ServletRequest和HttpServletRequest区别

 

ServletRequest

定义一个对象以向servlet提供客户端请求信息。servlet容器创建一个ServletRequest对象,并将其作为参数传递给servlet的服务方法。

ServletRequest对象提供数据,包括参数名和值、属性和输入流。扩展ServletRequest的接口可以提供额外的协议特定的数据(例如,HTTP数据是由HttpServletRequest提供的。

 

HttpServletRequest

扩展ServletRequest接口,为HTTP servlet提供请求信息。

servlet容器创建一个HttpServletRequest对象,并将其作为参数传递给servlet的服务方法(doGet,doPost等)。

 

ServletRequest只是提供了基础通信的行为,如:

设置字符集,获取参数,获取端口、地址等。

HttpServletRequest处理提供了上面的这些行为,还支持

获取cookie、session信息,关于servlet的请求映射、方法,文件上传Part等。

 

 

前进时,请别遗忘了身后的脚印。
原文地址:https://www.cnblogs.com/liudaihuablogs/p/14169475.html