Servlet2.5是JavaEE5.0規範,最低運作環境為JDK5.0以及Tomcat5.0。而Servlet3.0是JavaEE6.0規範,最低環境為JDK6.0以及Tomcat7.0。Servlet3.0的新特性主要分為以下幾個點:
- 基于注解配置三大元件
- 檔案上傳API的優化
- 異步處理
- 元件可插性
- 動态注冊三大元件(ServletContext)
- SPI - ServletContainerInitializer 機制
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
基于注解的配置
基于web.xml的配置與基于注解的配置,在Servlet3.0中,可以混合使用
@WebServlet
用來注冊servlet元件,在servlet2.x中,需要在web.xml中配置servlet标簽:
<servlet>
<!--servlet描述-->
<description>Servlet描述</description>
<!--servlet展示名稱-->
<display-name>TestServlet</display-name>
<!--servlet名稱-->
<servlet-name>TestServlet</servlet-name>
<!--servlet class-->
<servlet-class>com.yangsx95.demo.TestServlet</servlet-class>
<!--servlet 初始化參數-->
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>value2</param-value>
</init-param>
<!--servlet加載順序-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--servlet url 映射-->
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
在3.0 可以使用如下方式注冊一個Servlet:
//@WebServlet("test") // value 就是 urlPatterns,是預設屬性,二者不能同時使用
@WebServlet(
description = "Servlet描述",
displayName = "TestServlet",
name = "TestServlet",
initParams = {@WebInitParam(name="param1", value = "value1"), @WebInitParam(name="param2", value = "value2")},
loadOnStartup = 1,
urlPatterns = {"test", "aaa"}
)
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 擷取servlet名稱
this.getServletName();
// 擷取servlet初始化參數
Enumeration<String> initParameterNames = this.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
System.out.println(initParameterNames.nextElement());
}
resp.getWriter().append("servlet test");
}
}
@WebFilter
xml配置:
<filter>
<description>TestFilter描述</description>
<display-name>TestFilter</display-name>
<!--Filter名稱-->
<filter-name>TestFilter</filter-name>
<!--Filter class-->
<filter-class>com.yangsx95.demo.TestFilter</filter-class>
<!--初始化參數-->
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>value2</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<!--filter 過濾規則-->
<!--過濾路徑-->
<!--<url-pattern>/*</url-pattern>-->
<!--過濾指定的servlet-->
<servlet-name>TestServlet</servlet-name>
</filter-mapping>
對應的Servlet3.0 注解配置:
@WebFilter(
displayName = "TestFilter",
filterName = "TestFilter",
initParams = {@WebInitParam(name="param1", value = "value1"), @WebInitParam(name="param2", value = "value2")},
// urlPatterns = {"/*"}, // 過濾路徑
servletNames = {"TestServlet"} // 過濾指定的servlet
)
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 擷取初始化參數
// filterConfig.getInitParameter();
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("-------------start-----------");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("--------------end------------");
}
@Override
public void destroy() {
}
}
@WebListener
<listener>
<listener-class>com.yangsx95.demo.TestListener</listener-class>
</listener>
對應的Servlet3.0注解方式也很簡單:
@WebListener
public class TestListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("應用程式啟動");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("應用程式關閉");
}
}
檔案上傳API
@WebServlet("/upload")
@MultipartConfig // 代表該servlet可以處理multipart請求
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 擷取伺服器本地路徑
String txtPath = this.getServletContext().getRealPath("/txts");
Part file = req.getPart("file");// 檔案表單對應的input name
// 擷取指定的頭部屬性
// file.getHeader("Content");
file.write(txtPath + File.separator + "111.txt"); // 将檔案寫入到磁盤
}
}
異步處理
部分servlet需要做一些很長的耗時操作,這時,就需要servlet提前響應前端使用者,自己背景繼續執行邏輯,比如郵件發送、短信通知、複雜耗時任務提前傳回進行中。這就是servlet的異步處理。
下面是代碼展示:
@WebServlet(value = "/register", asyncSupported = true) // 開啟異步支援
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 擷取異步上下文對象,包含request資訊,response資訊
AsyncContext ac = req.getAsyncContext();
// 建立異步Runnable對象
ComputeRunnable computeRunnable = new ComputeRunnable(ac);
// 異步處理逾時時間
ac.setTimeout(30 * 1000);
// 設定異步監聽器
ac.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("異步任務完成");
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("異步任務逾時");
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("異步任務出錯");
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("異步任務開始執行");
}
});
// 異步執行runnable對象
ac.start(computeRunnable);
// 立即響應
resp.getWriter().print("成功,稍後會給您發送郵件");
}
}
public class ComputeRunnable implements Runnable {
private AsyncContext asyncContext;
public ComputeRunnable(AsyncContext context) {
this.asyncContext = context;
}
@Override
public void run() {
System.out.println(asyncContext.getRequest());
System.out.println(asyncContext.getResponse());
}
}
元件可插性
JavaEE6.0項目支援将打為Jar包的Servlet、Filter、Listener直接插入到運作的web項目中,這些jar中包含相應的配置檔案,這些jar也稱為web片斷項目。
定義一個web-fragment項目很容易,隻需要在web-inf下建立檔案web-fragment即可:
<?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">
<name>WebFragment1</name>
<servlet>
<servlet-name>hi</servlet-name>
<servlet-class>com.yangsx95.HiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hi</servlet-name>
<url-pattern>/hi.view</url-pattern>
</servlet-mapping>
</web-fragment>
将此項目打包為jar,然後放入web項目的lib目錄下,就可以通路web片段項目中的servlet了。
動态注冊元件
也就是在代碼中,通過一些api,比如addServlet動态添加元件,處于安全考慮,元件的動态注冊,隻能在應用啟動時執行。
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// 擷取ServletContext
ServletContext servletContext = servletContextEvent.getServletContext();
ServletRegistration.Dynamic servletName = servletContext.addServlet("servletName", TestServlet.class);
servletName.addMapping("/test");
FilterRegistration.Dynamic filterName = servletContext.addFilter("filterName", TestFilter.class);
filterName.setInitParameter("param2", "value2");
filterName.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
servletContext.addListener( TestListener.class);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
ServletContainerInitializer
Serlvet容器在啟動容器的時候,會掃描目前應用中的每個jar中的
META-INF/services/javax.servlet.ServletContainerInitializer
中指定的實作類,啟動并運作這個實作類的方法。這是一種SPI機制。
示例代碼:
@HandlesTypes({Person.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
// 參數1是 HandlesTypes 指定的類型集合。 将系統中所有HandlesType定義的類型的類,也就是子類/實作類(這裡是Person類的子類/實作類),都傳遞給這個接口;
// 參數2是ServletContext對象,用于在啟動時向應用程式動态注冊元件
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
servletContext.addListener(MyServletContextListener.class);
}
}
配置
META-INF/services/javax.servlet.ServletContainerInitializer
:
com.yangsx95.demo.MyServletContainerInitializer