目录
Servlet 是基于 Java 技术的 web 组件,容器托管的,用于生成动态内容。
早期的Web应用主要用于浏览新闻等静态页面,Http服务器(比如Apache,Nginx)向浏览器返回静态HTML,浏览器负责解析HTML,将结果呈现给用户。
需求:为了不仅仅浏览静态页面,还希望通过一些交互操作,来获取动态结果。那么可以通过让HTTP服务器调用服务端程序来实现。
那么服务端如何来实现这个需求呢?
Sun公司就推出了Servlet技术。Servlet可以理解为运行在服务端的Java小程序,因为Servlet不能独立运行,所以必须把Servlet部署到容器中,由容器来实例化并调用Servlet。
servlet接口
Servlet接口是Java Servlet API的核心抽象。所有Servlet类必须直接或间接的实现该接口,或者更通常做法是通过继承一个实现了该接口的类从而复用许多共性功能。目前有GenericServlet和HttpServlet这两个类实现了Servlet接口。大多数情况下,开发者只需要继承HttpServlet去实现自己的Servlet即可。
request
请求对象封装了客户端请求的所有信息。在HTTP协议中,这些信息是从客户端发送到服务器请求的HTTP头部和消息体。
servletContext
ServletContext(Servlet上下文)接口定义了servlet运行在的Web应用的视图。容器供应商负责提供Servlet容器的ServletContext接口的实现。Servlet可以使用ServletContext对象记录事件,获取URL引用的资源,存取当前上下文的其他Servlet可以访问的属性。
ServletContext是Web服务器中已知路径的根。例如,Servlet 上下文可以从http://www.mycorp.com/catalog找出,/catalog请求路径称为上下文路径,所有以它开头的请求都会被路由到与ServletContext 相关联的Web应用。
每一个部署到容器的Web应用都有一个Servlet接口的实例与之关联。在容器分布在多台虚拟机的情况下,每个JVM的每个Web应用将有一个ServletContext实例。
如果容器内的Servlet没有部署到Web应用中,则隐含的作为“默认”Web应用的一部分,并有一个默认的ServletContext。在分布式的容器中,默认的ServletContext是非分布式的且仅存在于一个JVM中。
response
响应(response)对象封装了从服务器返回到客户端的所有信息。在HTTP协议中,从服务器传输到客户端的信息通过HTTP头信息或响应的消息体。
filter
过滤器是一种代码重用的技术,它可以改变HTTP请求的内容,响应,及header信息。过滤器通常不产生响应或像servlet那样对请求作出响应,而是修改或调整到资源的请求,修改或调整来自资源的响应。
filter生命周期
在Web应用部署之后,在请求导致容器访问Web资源之前,容器必须找到过滤器列表并按照如上所述的应用到Web资源。容器必须确保它为过滤器列表中的每一个都实例化了一个适当类的过滤器,并调用其init(FilterConfig config)方法。过滤器可能会抛出一个异常,以表明它不能正常运转。如果异常的类型是UnavailableException,容器可以检查异常的isPermanent属性并可以选择稍候重试过滤器。
在部署描述符中声明的每个<filter>在每个JVM的容器中仅实例化一个实例。容器提供了声明在过滤器的部署描述符的过滤器config(译者注:FilterConfig),对Web应用的ServletContext的引用,和一组初始化参数。
当容器接收到传入的请求时,它将获取列表中的第一个过滤器并调用doFilter方法,传入ServletRequest 和 ServletResponse,和一个它将使用的FilterChain对象的引用。
过滤器的doFilter方法通常会被实现为如下或如下形式的子集:
1. 该方法检查请求的头。
2. 该方法可以用自定义的ServletRequest或HttpServletRequest实现包装请求对象为了修改请求的头或数据。
3. 该方法可以用自定义的ServletResponse 或 HttpServletResponse实现包装传入doFilter方法的响应对象用于修改响应的头或数据。
4. 该过滤器可以调用过滤器链中的下一个实体。下一个实体可能是另一个过滤器,或者如果当前调用的过滤器是该过滤器链配置在部署描述符中的最后一个过滤器,下一个实体是目标Web资源。调用FilterChain对象的doFilter方法将影响下一个实体的调用,且传入的它被调用时请求和响应,或传入它可能已经创建的包装版本。
由容器提供的过滤器链的doFilter方法的实现,必须找出过滤器链中的下一个实体并调用它的doFilter方法,传入适当的请求和响应对象。另外,过滤器链可以通过不调用下一个实体来阻止请求,离开过滤器负责填充响应对象。
service方法必须和应用到servlet的所有过滤器运行在同一个线程中。
5. 过滤器链中的下一个过滤器调用之后,过滤器可能检查响应的头。
6. 另外,过滤器可能抛出一个异常以表示处理过程中出错了。如果过滤器在doFilter处理过程中抛出UnavailableException,容器必须停止处理剩下的过滤器链。 如果异常没有标识为永久的,它或许选择稍候重试整个链。
7. 当链中的最后的过滤器被调用,下一个实体访问的是链最后的目标servlet或资源。
47
8. 在容器能把服务中的过滤器实例移除之前,容器必须先调用过滤器的destroy方法以便过滤器释放资源并执行其他的清理工作。
filter环境
可以使用部署描述符中的<init-params>元素把一组初始化参数关联到过滤器。这些参数的名字和值在过滤器运行期间可以使用过滤器的FilterConfig对象的getInitParameter和getInitParameterNames方法得到。另外,FilterConfig提供访问Web应用的ServletContext用于加载资源,记录日志,在ServletContext的属性列表存储状态。链中最后的过滤器和目标servlet或资源必须执行在同一个调用线程。
在Web应用中配置filter
过滤器可以通过规范的第8-67页的8.1.2节“@WebFilter”的@WebFilter注解定义或者在部署描述符中使用<filter>元素定义。在这个元素中,程序员可以声明如下内容:
■ filter-name: 用于映射过滤器到servlet或URL
■ filter-class: 由容器用于表示过滤器类型
■ init-params: 过滤器的初始化参数
程序员可以选择性的指定icon图标,文字说明,和工具操作显示的名字。容器必须为部署描述符中定义的每个过滤器声明实例化一个Java类实例。因此,如果开发人员对同一个过滤器类声明了两次,则容器将实例化两个相同的过滤器类的实例。
下面是一个过滤器声明的例子:
<filter>
<filter-name>Image Filter</filter-name>
<filter-class>com.acme.ImageFilter</filter-class>
</filter>
一旦在部署描述符中声明了过滤器,装配人员使用<filter-mapping>定义Web应用中的servlet和静态资源到过滤器的应用。过滤器可以使用<servlet-name>元素关联到一个Servlet。例如,以下示例代码映射Image Filter过滤器到ImageServlet servlet:
<filter-mapping>
<filter-name>Image Filter</filter-name>
<servlet-name>ImageServlet</servlet-name>
</filter-mapping>
过滤器可以使用<url-pattern>风格的过滤器映射关联到一组servlet和静态内容:
在这里, Logging过滤器应用到Web应用中的所有servlet和静态资源,因为每一个请求的URI匹配‘/*’URL模式。
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
当使用<url-pattern>风格配置<filter-mapping>元素,容器必须使用定义在12章“映射请求到Servlet”中的路径映射规则(path mapping rule)决定<url-pattern>是否匹配请求URI。
容器使用的用于构建应用到一个特定请求URI的过滤器链的顺序如下所示:
1. 首先, <url-pattern>按照在部署描述符中的出现顺序匹配过滤器映射。
2. 接下来,<servlet-name>按照在部署描述符中的出现顺序匹配过滤器映射。
如果过滤器映射同时包含了<servlet-name> 和 <url-pattern>,容器必须展开过滤器映射为多个过滤器映射(每一个<servlet-name> 和 <url-pattern>一个),保持<servlet-name>和<url-pattern>元素顺序。例如,以下过滤器映射:
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<url-pattern>/foo/*</url-pattern>
<servlet-name>Servlet1</servlet-name>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/bar/*</url-pattern>
</filter-mapping>
等价于:
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<url-pattern>/foo/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<servlet-name>Servlet1</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<servlet-name>Servlet2</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<url-pattern>/bar/*</url-pattern>
</filter-mapping>
关于过滤器链顺序的要求意味着容器,当接收到传入的请求,按照如下方式处理请求:
■ 按照120页的“映射规则”识别目标Web资源。
■ 如果有过滤器使用servlet name匹配到具有<servlet-name>的Web资源,容器以声明在部署描述符中的顺序构建过滤器链。该链中的最后一个过滤器,即最后一个<servlet-name> 匹配的过滤器将调用目标Web资源。
■ 如果有过滤器使用<url-pattern>匹配且该<url-pattern>按照第12.2节“映射规则”中的规则匹配请求的URI,容器容器以声明在部署描述符中的顺序构建<url-pattern>匹配到的过滤器链。该链中的最后一个过滤器是在部署描述符中对当前请求URI最后一个<url-pattern>匹配的过滤器。链中的最后一个过滤器将调用<servlet-name>匹配的链中的第一个过滤器,或如果没有,则调用目标Web资源。
想要高性能Web容器将缓存过滤器链从而不需要根据每个请求重新计算它们。
filter和RequestDispatcher
Java Servlet规范自从2.4新版本以来,能够在请求分派器forward()和include()调用情况下配置可被调用的过滤器。
通过在部署描述符中使用新的<dispatcher>元素,开发人员可以为filter-mapping指定是否想要过滤器应用到请求,当:
1. 请求直接来自客户端。
可以由一个带有REQUEST 值的<dispatcher>元素,或者没有任何<dispatcher>元素来表示。
2.使用表示匹配<url-pattern> 或 <servlet-name>的Web组件的请求分派器的forward()调用情况下处理请求。
可以由一个带有FORWARD值的<dispatcher>元素表示。
3.使用表示匹配<url-pattern> 或 <servlet-name>的Web组件的请求分派器的include()调用情况下处理请求。
50
可以由一个带有INCLUDE值的<dispatcher>元素表示。
4. 使用第106页“错误处理”指定的错误页面机制处理匹配<url-pattern>的错误资源的请求。
可以由一个带有ERROR值的<dispatcher>元素表示。
5. 使用第10页指定的“异步处理”中的异步上下文分派机制对web组件使用dispatch调用处理请求。
可以由一个带有ASYNC值的<dispatcher>元素表示。
6. 或之上1,2,3,4或5的任何组合。
例如:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
</filter-mapping>
客户端以/products/...开始的请求将导致Logging Filter被调用,但不是在以路径/products/...开始的请求分派器调用情况下。LoggingFilter 将在初始请求分派和恢复请求时被调用。如下代码:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<servlet-name>ProductServlet</servlet-name>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
客户端到ProductServlet的请求将不会导致Logging Filter被调用,且也不会在请求分派器forward()调用到ProductServlet情况时,仅在以ProductServlet名字开头的请求分派器 include()调用时被调用。如下代码:
<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
客户端以/products/...开始的请求,或在以路径/products/...开始的请求分派器forward()调用情况时,将导致Logging Filter被调用。
最后,如下代码使用特殊的servlet名字 “*”:
<filter-mapping>
<filter-name>All Dispatch Filter</filter-name>
<servlet-name>*</servlet-name>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
在按名字或按路径获取的所有请求分派器forward()调用时该代码将导致All Dispatch Filter被调用。
会话
Cookies
通过HTTP cookie的会话跟踪是最常用的会话跟踪机制,且所有servlet容器都应该支持。
容器向客户端发送一个cookie,客户端后续到服务器的请求都将返回该cookie,明确地将请求与会话关联。会话跟踪cookie的标准名字必须是JSESSIONID,所有3.0兼容的容器必须支持。容器也允许通过容器指定的配置自定义会话跟踪cookie的名字。
所有servlet容器必须提供能够配置容器是否标记会话跟踪cookie为HttpOnly。已建立的配置必须应用到所有上下文中还没有建立特定的配置(见SessionCookieConfig javadoc获取更多细节)。
如果web应用为其会话跟踪cookie配置了一个自定义的名字,则如果会话id编码到URL中那么相同的自定义名字也将用于URI参数的名字(假如URL重写已开启)。
SSL会话
安全套接字层,在HTTPS使用的加密技术,有一种内置机制允许多个来自客户端的请求被明确识别为同一会话。Servlet容器可以很容易地使用该数据来定义会话。
URL 重写
URL重写是会话跟踪的最低标准。当客户端不接受cookie时,服务器可使用URL重写作为会话跟踪的基础。URL重写涉及添加数据、会话ID、容器解析URL路径从而请求与会话相关联。
会话ID必须被编码为URL字符串中的一个路径参数。参数的名字必须是jsessionid。下面是一个URL包含编码的路径信息的例子:
http://www.myserver.com/catalog/index.html;jsessionid=1234
URL重写在日志、书签、referer header、缓存的HTML、URL工具条中暴露会话标识。在支持cookie或SSL 会话的情况下,不应该使用URL重写作为会话跟踪机制。
会话完整性
当服务的来自客户端的请求不支持使用cookie时,Web容器必须能够支持HTTP 会话。 为了满足这个要求, Web容器通常支持URL重写机制。
注解
在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属性。所有其他属性是可选的默认设置(请参考javadoc获取更多细节)。当注解上唯一属性是url模式时推荐使用value且当也有使用其他属性时使用urlPatterns属性。在同一注解上同时使用value 和 urlPatterns属性是非法的。如果没有指定Servlet名字则默认是全限定类名。被注解的sevlet必须指定至少一个url模式进行部署。如果同一个Servlet类以不同的名字声明在部署描述符中,必须实例化一个新的Servlet实例。如果使用不同名字添加的同一个Servlet类使用定义在4-35页的4.4.1节 “编程式添加和配置Servlet” 的编程式API添加到ServletContext,使用@WebServlet注解声明的属性值必须被忽略,必须创建一个指定名字的Servlet的新的实例。
@WebServlet注解的类必须继承javax.servlet.http.HttpServlet类。
下面是如何使用该注解的一个示例。
代码示例@WebServlet 注解示例
@WebServlet(”/foo”)
public class CalculatorServlet extends HttpServlet{
//...
}
下面是如何使用该注解指定更多的属性的一个示例。
代码示例@WebServlet 注解示例使用其它指定的注解属性
@WebServlet(name=”MyServlet”, urlPatterns={"/foo", "/bar"})
public class SampleUsingAnnotationAttributes extends HttpServlet{
public void doGet(HttpServletRequest req, HttpServletResponse res) {
}
}
@WebFilter
该注解用于在Web应用中定义Filter。该注解在一个类上指定且包含声明过滤器的元数据。如果没有指定Filter名字则默认是全限定类名。注解的urlPatterns属性, servletNames 属性 或 value 属性必须被指定。所有其他属性是可选的默认设置(请参考javadoc获取更多细节)。当注解上唯一属性是url模式时推荐使用value且当也有使用其他属性时使用urlPatterns属性。在同一注解上同时使用value 和 urlPatterns属性是非法的。
@ WebFilter注解的类必须实现javax.servlet.Filter。
下面是如何使用该注解的一个示例。
代码示例@WebFilter 注解示例
@WebFilter(“/foo”)
public class MyFilter implements Filter {
public void doFilter(HttpServletRequest req, HttpServletResponse res) {
...
}
}
@WebInitParam
该注解用于指定必须传递到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
示例:
@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属性和<multipart-config>的<location>元素被解析为一个绝对路径且默认为javax.servlet.context.tempdir。如果指定了相对地址,它将是相对于tempdir位置。绝对路径与相对地址的测试必须使用java.io.File.isAbsolute。
其他注解/惯例
除了这些注解,定义在第15-183页15.5节的“注解和资源注入”将继续工作在这些新注解上下文中。
默认情况下,所有应用将有index.htm(l)和index.jsp在welcome-file-list列表中。该描述符可以用来覆盖这些默认设置。
当使用注解时,从WEB-INF/classes或WEB-INF/lib中的不同框架jar包/类加载监听器、Servlet的顺序是没有指定的。如果顺序是很重要的,那么请看web.xml模块部分和后面的web.xml和web-fragment.xml顺序部分。顺序仅能在部署描述符中指定。
可插拔性
web fragment
在这个版本(Servlet 3.0)的规范中,引入了web模块部署描述符片段(web fragment)的概念。web fragment是web.xml的部分或全部,可以在一个类库或框架 jar包的META-INF目录指定和包括。在WEB-INF/lib目录中的普通的老的jar文件即使没有web-fragment.xml也可能被认为是一个fragment,任何在它中指定的注解都将按照定义在8.2.3节的规则处理,容器将会取出并按照如下定义的规则进行配置。
web fragment是web应用的一个逻辑分区,以这样一种方式,在应用中使用的框架可以定义所有制品(artifact)而无需要求开发人员在web.xml中编辑或添加信息。它几乎包含web.xml描述符中使用的所有相同元素。不过描述符的顶级元素必须是web-fragment且对应的描述符文件必须被称为web-fragment.xml,相关元素的顺序在web-fragment.xml 和 web.xml也是不同的,请参考定义在第14章的部署描述符一章中对应的web-fragment schema。
如果框架打包成jar文件,且有部署描述符的形式的元数据信息,那么web-fragment.xml描述符必须在该jar包的META-INF/目录中。
如果框架想使用META-INF/web-fragment.xml,以这样一种方式,它扩充了web应用的web.xml,框架必须被绑定到Web应用的WEB-INF/lib目录中。为了使框架中的任何其他类型的资源(例如,类文件)对web应用可用,把框架放置在web应用的classloader委托链的任意位置即可。换句话说,只有绑定到web应用的WEB-INF/lib目录中的JAR文件,但不是那些在类装载委托链中更高的,需要扫描其web-fragment.xml。
在部署期间,容器负责扫描上面指定的位置和发现web-fragment.xml并处理它们。存在于当前的单个web.xml的名字唯一性的要求,也同样适用于一组web.xml和所有能适用的web-fragment.xml文件。
如下是库或框架可以包括什么的例子。
<web-fragment>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>
WelcomeServlet
</servlet-class>
</servlet>
<listener>
<listener-class>
RequestListener
</listener-class>
</listener>
</web-fragment>
以上的web-fragment.xml将被包括在框架的jar文件的META-INF /目录。web-fragment.xml配置和应该应用的注解的顺序是未定义的。如果顺序对于某一应用是很重要的方面,请参考下面如何实现所需的顺序定义的规则。
web.xml和web-fragment.xml顺序
可插拔性
web应用
Web应用程序部署
当一个Web应用程序部署到容器中,在Web应用程序开始处理客户端请求之前,必须按照下述步骤顺序执行。
■ 实例化部署描述文件中<listener>元素标识的每个事件监听器的一个实例。
■ 对于已实例化的实现了ServletContextListener接口的监听器实例,调用contextInitialized()方法。
■ 实例化部署描述文件中<filter>元素标识的每个过滤器的一个实例,并调用每个过滤器实例的init()方法。
■ 包含<load-on-startup>元素的<servlet>元素,根据load-on-startup元素值定义的顺序为每个servlet实例化一个实例,并调用每个servlet实例的init()方法。
包含web.xml部署描述文件
如果Web应用不包含任何servlet、过滤器、或监听器组件或使用注解声明相同的,那么可以不需要web.xml文件。换句话说,只包含静态文件或JSP页面的应用程序并不需要一个web.xml的存在。
servlet3.1规范中文版。
https://waylau.gitbooks.io/servlet-3-1-specification/
参考文章:
https://www.cnblogs.com/inspred/p/10902441.html
https://blog.csdn.net/luwfls/article/details/78110381
来源:CSDN
作者:chuixue24
链接:https://blog.csdn.net/chuixue24/article/details/103454973