Servlet3.0规范的新特性主要是为了3个目的:
1.简化开发
2.便于布署
3.支持Web2.0原则
为了简化开发流程,Servlet3.0引入了注解(annotation),这使得web布署描述符web.xml
不在是必须的选择。
Pluggability可插入性
当使用任何第三方的框架,如Struts,JSF或Spring,我们都需要在web.xml
中添加对应的Servlet的入口。这使得web描述符笨重而难以维护。Servlet3.0的新的可插入特性使得web应用程序模块化而易于维护。通过web fragment实现的可插入性减轻了开发人员的负担,不需要再在web.xml
中配置很多的Servlet入口。
Asynchronous Processing 异步处理
另外一个显著的改变就是Servlet3.0支持异步处理,这对AJAX应用程序非常有用。当一个Servlet处理某些请求的时候,如查询数据库或消息连接,这个线程要等待直到获得所需要的资源才能够执行其他的操作。异步处理通过运行线程执行其他的操作来避免了这种阻塞。
Annotations in Servlet
使用注解来定义Servlet和filter使得我们不用在web.xml
中定义相应的入口。
@WebServlet
@WebServlet
用来定义web应用程序中的Servlet。这个注解有多个属性,例如name
,urlPattern
, initParams
。
@WebServlet(name = "GetQuoteServlet", urlPatterns = {"/getquote"} )
public class GetQuoteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
try {
String symbol = request.getParameter("symbol");
out.println("<h1>Stock Price is</h1>" + StockQuoteBean.getPrice(symbol);
} finally {
out.close();
}
}
}
public class StockQuoteBean {
private static StockQuoteServiceEntity serviceEntity = new StockQuoteServiceEntity();
public static double getPrice(String symbol) {
if(symbol !=null ) {
return serviceEntity.getPrice(symbol);
. } else {
return 0.0;
}
}
}
在上面的例子中,一个Servlet只对应了一个urlPattern
。实际上一个Servlet可以对应多个urlPattern
,我们可以这样定义:
@WebServlet(name = "GetQuoteServlet", urlPatterns = {"/getquote", "/stockquote"} )
public class GetQuoteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
try {
String symbol = request.getParameter("symbol");
out.println("<h1>Stock Price is</h1>" + StockQuoteBean.getPrice(symbol);
} finally {
out.close();
}
}
}
@WebFilter
我们可以使用@WebFilter
注解来定义filter。这个注解可以被应用在实现了javax.servlet.Filter
接口的类上。
@WebFilter(filterName = "AuthenticateFilter", urlPatterns = {"/stock.jsp", "/getquote"})
public class AuthenticateFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String username = ((HttpServletRequest) request).getParameter("uname");
String password = ((HttpServletRequest) request).getParameter("password");
if (username == null || password == null) {
((HttpServletResponse) response).sendRedirect("index.jsp"); }
if (username.equals("admin") && password.equals("admin")) {
chain.doFilter(request, response); }
else {
((HttpServletResponse) response).sendRedirect("index.jsp"); }
}
public void destroy() {
. }
public void init(FilterConfig filterConfig) {
}
}
@WebInitParam
可以使用@WebInitParam
注解来制定Servlet或filter的初始参数。当然我们也可以使用@WebServlet
或@WebFileter
的initParam
属性来指定初始参数。下面是使用@WebInitParam
的例子:
@WebServlet(name = "GetQuoteServlet", urlPatterns = {"/getquote"})
@WebInitParam(name = "default_market", value = "NASDAQ")
public class GetQuoteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
String market = getInitParameter("default_market");
String symbol = request.getParameter("symbol");
out.println("<h1>Stock Price in " + market + " is</h1>" + StockQuoteBean.getPrice(symbol, market));
} finally {
out.close();
}
}
}
下面是使用initParam
属性的例子:
@WebServlet(name = "GetQuoteServlet",
urlPatterns = {"/getquote"},
initParams={@WebInitParam(name="default_market", value="NASDAQ")}
)
public class GetQuoteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
String market = getInitParameter("default_market");
String symbol = request.getParameter("symbol");
out.println("<h1>Stock Price in " + market + " is</h1>" + StockQuoteBean.getPrice(symbol, market));
} finally {
out.close();
}
}
}
@WebListener
@WebListener
注解被应用在作为listener监听web应用程序事件的类上,所以@WebListener
能够被应用在实现了ServletContextListener
,ServletContextAttributeListener
,ServletRequestListener
,ServletRequestAttributeListener
,HttpSessionListener
和HttpSessionAttributeListener
接口的类上。在下面的例子中,该类实现了ServletContextListener
接口。
@WebListener
public class QuoteServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.setInitParameter(“default_market”, “NASDAQ”);
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
@MultipartConfig
使用@MultipartConfig
注解来指定Servlet要求的multipart MIME类型。这种类型的MIME附件将从request对象中读取。
The Metadata and Common Annotations元数据与通用的注解
除了以上的Servlet特定的注解之外,Servlet3.0还支持JSR175(Java元数据规范)和JSR250(Java平台通用注解)所规定的注解,包括:
* 安全相关的注解,如 @DeclareRoles
和 @RolesAllowed
* 使用EJB的注解,如 @EJB
和 @EJBs
* 资源注入相关的注解,如 @Resource 和 @Resources
* 使用JPA的注解,如 @PersistenceContext
, @PersistenceContexts
, @PersistenceUnit
, 和 @PersistenceUnits
* 生命周期的注解,如 @PostConstruct
和 @PreDestroy
* 提供WebService引用的注解,如 @WebServiceRef
and @WebServiceRefs
注解和web.xml哪个会生效
注解的引入使得web.xml
变成可选的了。但是,我们还是可以使用web.xml
。容器会根据web.xml
中的metadata-complete
元素的值来决定使用web.xml
还是使用注解。如果该元素的值是true
,那么容器不处理注解,web.xml
是所有信息的来源。如果该元素不存在或者其值不为true
,容器才会处理注解。
Web框架的可插入性
我们前面说过了Servlet3.0的改进之一就是使得我们能够将框架和库插入到web应用程序中。这种可插入性减少了配置,并且提高了web应用程序的模块化。Servlet3.0是通过web模块布署描述片段(简称web片段)来实现插入性的。
一个web片段就是web.xml
文件的一部分,被包含在框架特定的Jar包的META-INF
目录中。Web片段使得该框架组件逻辑上就是web应用程序的一部分,不需要编辑web布署描述文件。
Web片段中使用的元素和布署文件中使用的元素基本相同,除了根元素不一样。Web片段的根元素是<web-fragment>
,而且文件名必须叫做web-fragment.xml
。容器只会在放在WEB-INF\lib
目录下的Jar包中查找web-fragment.xml文
件。如果这些Jar包含有web-fragment.xml
文件,容器就会装载需要的类来处理他们。
在web.xml
中,我们要求Servlet的name必须唯一。同样的,在web.xml
和所有的web片段中,Servlet的name也必须唯一。
下面就是一个web-fragment
的例子:
web-fragment.xml
<web-fragment>
<servlet>
<servlet-name>ControllerServlet</servlet-name>
<servlet-class>com.app.control.ControllerServlet</servlet-class>
</servlet>
<listener>
<listener-class>com.listener.AppServletContextListener</listener-class>
</listener>
</web-fragment>
框架的Jar包是放在WEB-INF\lib
目录下的,但是Servlet3.0提供两种方法指定多个web片段之间的顺序:
1. 绝对顺序
2. 相对顺序
我们通过web.xml
文件中的<absolute-ordering>
元素来指定绝对顺序。这个元素有之元素name,name的值是各个web片段的name元素的值。这样就指定了web片段的顺序。如果多个web片段有相同的名字,容器会忽略后出现的web片段。下面是一个指定绝对顺序的例子:
web.xml
<web-app>
<name>DemoApp</name>
<absolute-ordering>
<name>WebFragment1</name>
<name>WebFragment2</name>
</absolute-ordering>
...
</web-app>
相对顺序通过web-fragment.xml
中的<ordering>
元素来确定。Web片段的顺序由<ordering>
的子元素<before>
,<after>
和<others>
来决定。当前的web片段会放在所有的<before>
元素中的片段之前。同样的,会放在所有的<after>
元素中的片段之后。<others>
用来代替所有的其他片段。注意只有当web.xml
中没有<absolute-ordering>
时,容器才会使用web片段中定义的相对顺序。
下面是一个帮助理解相对顺序的例子:
web-fragment.xml
<web-fragment>
<name>WebFragment1</name>
<ordering><after>WebFragment2</after></ordering>
...
`</web-fragment>
``
###web-fragment.xml
WebFragment2
..
###web-fragment.xml
WebFragment3
这些文件将会按照下面的顺序被处理:
1. WebFragment3
2. WebFragment2
3. WebFragment1
包含WebFragment3的Jar文件被最先处理,包含WebFragment2的文件被第二个处理,包含WebFragment1的文件被最后处理。
如果既没有定义绝对顺序,也没有定义相对顺序,那么容器就认为所有的web片段间没有顺序上的依赖关系。
##Servlet中的异步处理
很多时候Servlet要和其他的资源进行互动,例如访问数据库,调用web service。在和这些资源互动的时候,Servlet不得不等待数据返回,然后才能够继续执行。这使得Servlet调用这些资源的时候阻塞。Servlet3.0通过引入异步处理解决了这个问题。异步处理允许线程调用资源的时候不被阻塞,而是直接返回。`AsyncContext`负责管理从资源来的回应。`AsyncContext`决定该回应是应该被原来的线程处理还是应该分发给容器中其他的资源。`AsyncContext`有一些方法如`start`,`dispatch`和`complete`来执行异步处理。
要想使用Servlet3.0的异步处理,我们需要设置`@Webservlet`和`@WebFilter`注解的`asyncSupport`属性。这个属性是布尔值,缺省值是`false`。
Servlet3.0的异步处理可以很好的和AJAX配合。在Servlet的`init`方法中,我们能够访问数据库或从JMS读取消息。在`doGet`或`doPost`方法中,我们能够启动异步处理,`AsyncContext`会通过`AsyncEvent`和`AsyncListener`来管理线程和数据库操作/JMS操作自己的关系。
##已有API的改进
除了这些新特性之外,Servlet3.0还对以往已经存在的API做了一些改进。
###HttpServletRequest
To support the multipart/form-data MIME type, the following methods have been added to the `HttpServletRequest` interface:
为了支持`multipart/form-data` MIME类型,在`HttpServletRequest`接口中添加了项目的方法:
Iterable<Part> getParts()
Part getPart(String name)
##Cookies
为了避免一些跨站点攻击,Servlet3.0支持HttpOnly的cookie。HttpOnly cookie不想客户端暴露script代码。Servlet3.0在Cookie类中添加了如下的方法来支持HttpOnly cookie:
void setHttpOnly(boolean isHttpOnly)
boolean isHttpOnly()
###ServletContext
通过在`ServletContext`中添加下面的方法,Servlet3.0允许Servlet或filter被编程的加入到context中:
addServlet(String servletName, String className)
addServlet(String servletName, Servlet servlet)
addServlet(String servletName, Class<? extends Servlet> servletClass)
addFilter(String filterName, String className)
addFilter(String filterName, Filter filter)
addFilter(String filterName, Class<? extends Filter>filterClass)
setInitParameter (String name, String Value)
“`
Servlet3.0新功能: 异步处理
J2EE 6和Glassfish 3V正式发布了,J2EE 6正式发布了Servlet3.0, 为了能更好的对WEB2.0提供支持, 3.0添加了异步处理的机制.
##HTTP1.1相对于HTTP1.0的影响.
HTTP1.1最大的一个改变就是提供了长连接,这样HTTP不再是一次请求,一次连接的协议了,只要HTTP的connection不关闭,一次HTTP连接可以支持任意多次request/reponse处理. 当WEB Client与WEB Server建立连接时,客户端可以采用长连接,也就是说Client会一直保持对WEB Server的连接(例如:Browser对一个网站保持当连接,直到Browser关闭或最终退出该网站). 旧的WEB Server会为每一个Http连接分配一个active的Thread,这样当Client的数量增大时,Server端Thread Pool的最大容量也需要相应增大,但Thread是相当耗内存的,一个不小心就会导致Server端NotEnoughMemory…
基于HTTP1.1,大部分支持Servlet2.X的WEB容器都采用的NIO去接收和处理请求. 当Client和Server端建立连接时,Server端并不分配一个Thread给HTTP连接.直到Server端收到Client端发送的Request时, Server才开始为该Request分配Thread(注意:这里不是为HTTP连接分配Thread).
这样当大量的Client建立长连接与Server进行交互时,Server无需维持一个Thread给inactive的HTTP长连接, 每个Servlet在doReceived()时其实对应的是一个active Request,而不是HTTPConnection本身. 这样Server端所需的最大Thread数大大地减少了.
AJAX的影响
- Request的数量爆炸性增加增加
过去WEB Browser打开一个Web page,只需要和Web Server端建立一个HTTP连接.但AJAX技术出现以后,一个Web page上可能有多个与Web Server的连接,而且Ajax request通常是十分频繁的,Server接收到的Request数量大大增长了, 这样原先NIO的技术已经不能很好的支持基于Ajax的服务了.
Servlet 3.0的异步处理就能够解决上面的问题.
Servlet3.0的solution:
当request发送到Server端时,servlet的doReceived()将request放进一个queue里,然后doReceived结束.这个时候server并没有关闭response,Client端一直在等server端response的内容. Server端维护自己的ThreadPool,当ThreadPool里有idle的Thread,就从queue里取出一个request,分配idle的Thread给request,并进行处理.
“`
@WebServlet(“/test” asyncSupported=true)
public class MyServlet extends HttpServlet {
ScheduledThreadPoolExecutor executor = null;
public void init(ServletConfig arg0) throws ServletException {
executor = new ThreadPoolExecutor(10);//独立的线程池处理请求
}
public void doGet(HttpServletRequest req, HttpServletResponse res) {
...
AsyncContext aCtx = request.startAsync(req, res);
executor.execute(new AsyncWebService(aCtx));//异步处理
}
}
public class AsyncWebService implements Runnable {
AsyncContext ctx;
public AsyncWebService(AsyncContext ctx) {
this.ctx = ctx;
}
public void run() {//处理请求
//Do something here ...
// Dispatch the request to render the result to a JSP.
ctx.dispatch("/render.jsp");
}
}