既然談到了 JAX-RS,也使用了其參考實作 Jersey,在課程的最後,就來將 Java Tutorial 第五堂(1) 練習 14 的成果中的 Spring MVC 換成 Jersey,作為本課程的最後一個練習。
練習 20:整合 Jersey/Spring/Hibernate
在 Lab 檔案的 exercises/exercise20 中有個 DVDLibrary 目錄,已經事先將練習 14 中一些可重用的程式碼(像是 Dvd.java、DvdDao.java、DvdLibraryService.java 等)與設定檔(像是 build.gradle 等)準備好。因為現在要使用 Jersey 取代 Spring MVC,所以請將 web.xml 中的<servlet>
與 <servlet-mapping>
等設定刪除,並撰寫以下的內容:
...
<filter>
<filter-name>jersey</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>tw.codedata</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.server.mvc.jsp.JspMvcFeature</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.mvc.templateBasePath.jsp</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>jersey.config.servlet.filter.staticContentRegex</param-name>
<param-value>/.*jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jersey</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
相關設定選項之意義,可以參考 Java Tutorial 第六堂(2) 中的說明。因為不使用 Spring MVC 了,所以可以將 dispatcher-servlet.xml 中的
<mvc:annotation-driven />
刪除,而且因為不再設定 DispatcherServlet
作為 Servlet,因此必須有另一個方式來讀取 Spring IoC 的 XML 設定檔,我們稍後會使用 Spring 的 XmlWebApplicationContext
來執行這項任務,它預設會讀取 WEB-INF 中的 applicationContext.xml,因此請將 dispatcher-servlet.xml 更名為 applicationContext.xml。
我們希望在 Web 應用程式啟始時,就讀取 applicationContext.xml,這可以在 Web 應用程式中定義
ServletContextListener
實例來達成,請在 src/main/java/tw/codedata 中新增一個 DVDLibraryContextListener.java 如下:
package tw.codedata;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.web.context.support.XmlWebApplicationContext;
@WebListener
public class DVDLibraryContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
XmlWebApplicationContext xmlWebAppCtx = new XmlWebApplicationContext();
xmlWebAppCtx.setServletContext(sce.getServletContext());
xmlWebAppCtx.refresh();
sce.getServletContext().setAttribute("dvdLibraryService", xmlWebAppCtx.getBean("dvdLibraryService"));
}
@Override
public void contextDestroyed(ServletContextEvent sce) {}
}
由於標註了
@WebListener
且實作了 ServletContextListener
介面,應用程式初始後會呼叫 DVDLibraryContextListener
的 contextInitialized
方法,這個方法中簡單來說,執行了 applicationContext.xml 內容的讀取並進行各種物件 IoC 處理,接著取得 DvdLibraryService
實例,並設定給 ServletContext
作為屬性。
每個 Web 應用程式,都會有唯一的
ServletContext
實例,因此,你只要想辦法取得 ServletContext
實例,就可以取得已設定為 "dvdLibraryService"
屬性的 DvdLibraryService
實例,JAX-RS 支援依賴注入,你可以透過 @Context
標註想要注入的 ServletContext
參考名稱,這樣 JAX-RS 就會自動為你注入,因此,DvdController
可以改寫如下:
package tw.codedata;
import java.util.*;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.Context;
import org.glassfish.jersey.server.mvc.Viewable;
import javax.servlet.ServletContext;
@Path("/dvds")
public class DvdController {
@Context ServletContext context;
public DvdLibraryService getDvdLibraryService() {
return (DvdLibraryService) context.getAttribute("dvdLibraryService");
}
@GET
public Viewable list() {
Map model = new HashMap();
model.put("dvds", getDvdLibraryService().allDvds());
return new Viewable("/list", model);
}
@POST
public Viewable add(
@FormParam("title") String title,
@FormParam("year") Integer year,
@FormParam("duration") Integer duration,
@FormParam("director") String director) {
Dvd dvd = getDvdLibraryService().addDvd(title, year, duration, director);
Map model = new HashMap();
model.put("dvd", dvd);
return new Viewable("/success", model);
}
}
根據
@PATH
設定,index.jsp 中 List Dvds 的路徑應該修改為 dvds
:
<a href="dvds">List Dvds</a>
而 add.jsp 中的
action
也要修改為 dvds
:
<form action="dvds" method="post">
由於 Jersey 的 Model 中設定的名稱,在 JSP 頁面中必須以
it
名稱為前綴,因此 list 中的 <c:forEach>
必須修改如下:
<c:forEach var="dvd" items="${it.dvds}">
而 success.jsp 中每個 EL 變數前都要加上
it
:
<li>Title: ${it.dvd.title}</li>
<li>Year: ${it.dvd.year}</li>
<li>Duration: ${it.dvd.duration}</li>
<li>Director: ${it.dvd.director.name}</li>
完成以上所有修改之後,試著執行
gradle tomcatRunWar
,看看修改是否成功。
終於,本課程到了尾聲了 … 撰寫本文的這個時候,正值 JDK8 發表不久,算算 Java 也歷經了快二十年的變遷,這麼多年來 Java 累積的東西非常龐大,JDK8 又試著導入新的典範與不少的觀念 ... 這個課程只是個簡短的生態之旅,如果你正要進入 Java 的世界,別忘了,你的旅程才剛要開始!...