Spring 2.5 中的註解式 Web MVC 控制器

工程 | Juergen Hoeller | 2007 年 11 月 14 日 | ...

Spring 2.5 引入了一種編寫註解式 Web MVC 控制器的方法,我們還沒有在這方面寫太多部落格文章... 我將藉此機會向您概述 Spring MVC 目前的真正用途。

Spring MVC 本質上是一個請求分派器框架,具有 Servlet API 變體和 Portlet API 變體。 它在其託管環境(Servlet 或 Portlet)中非常緊密地運行。 將 Spring MVC 視為在 Servlet/Portlet 容器之上提供基礎設施和便利功能:例如,靈活的請求映射、控制器處理和檢視渲染階段之間的分離、資料繫結、補充 JSTL 的基本 JSP 標籤庫等等。 複雜 HTTP 請求處理的建構塊。

Spring MVC 非常靈活:它的核心 DispatcherServlet 不僅可以託管其原生控制器,還可以適應任何種類的動作類型。 它可以託管處理基於 HTTP 的遠端處理協定的純 HttpRequestHandlers:這是 Spring 使用者在定義 HTTP 調用器/Hessian/Burlap 服務匯出器,或 Web 服務的 XFire 匯出器時所利用的。 DispatcherServlet 甚至可以託管任意第三方 Servlet,允許這些 Servlet 由 Spring 環境配置和管理。

Spring 2.5 的註解式控制器

那麼 Spring 2.5 基於註解的控制器方法如何融入這個架構? 很簡單:它本質上是 DispatcherServlet/DispatcherPortlet 支援的另一種控制器類型,不實作特定的介面,而是使用註解來表達特定處理器方法的請求映射。 它主要是一種用於實作多動作控制器的下一代樣式,取代 Spring 優秀的 MultiActionController 類別。

讓我們來看一個範例,取自 Spring 發佈版附帶的「imagedb」範例應用程式。(注意:這是「imagedb」的 Spring 2.5 最終版本,與 RC 版本略有不同。)

@Controller
public class ImageController {

private ImageDatabase imageDatabase;

@Autowired
public ImageController(ImageDatabase imageDatabase) {
this.imageDatabase = imageDatabase;
}

@RequestMapping("/imageList")
public String showImageList(ModelMap model) {
model.addAttribute("images", this.imageDatabase.getImages());
return "imageList";
}

@RequestMapping("/imageContent")
public void streamImageContent(@RequestParam("name") String name, OutputStream outputStream)
throws IOException {

this.imageDatabase.streamImage(name, outputStream);
}

@RequestMapping("/imageUpload")
public String processImageUpload(
@RequestParam("name") String name, @RequestParam("description") String description,
@RequestParam("image") MultipartFile image) throws IOException {

this.imageDatabase.storeImage(name, image.getInputStream(), (int) image.getSize(), description);
return "redirect:imageList";
}

@RequestMapping("/clearDatabase")
public String clearDatabase() {
this.imageDatabase.clearDatabase();
return "redirect:imageList";
}
}

這個控制器類別實際上做什麼?它的設計重點是什麼? 讓我們逐步了解...

@Controller 和 @RequestMapping

首先,該類別使用 @Controller 原型進行註解。 這表示應該掃描其方法以查找請求映射。 它還允許透過 Spring 2.5 的元件掃描 (<context:component-scan>) 進行自動偵測,就像其他原型 @Component、@Repository 和 @Service 一樣。 在「imagedb」範例中,ImageController 仍然透過 <bean> 標籤明確定義 - 僅僅因為自動偵測只有在控制器數量較多時才真正有效。

建構函式標記為 @Autowired 並接受 ImageDatabase 類型的引數。 這是核心 Spring 2.5 功能,即註解驅動的依賴注入:將呼叫此建構函式,傳入 ImageDatabase 類型的 Spring bean,該 bean 是從 Spring ApplicationContext 依類型取得的。 在我們的案例中,這是應用程式服務層中的核心 ImageDatabase 服務。

實際的請求映射透過方法層級的 @RequestMapping 註解來表達。 這些映射中的每一個都繫結到包含 DispatcherServlet 中的特定 HTTP 路徑。 映射路徑也可以從處理器方法名稱推斷出來,在類型層級表達一個常見的映射模式(例如「*.image」) - 重用從優秀的 MultiActionController 已知的 InternalPathMethodNameResolver!

因此,當在類型層級使用 @RequestMapping 時,方法層級的註解將「縮小」特定處理器方法的映射。 @RequestMapping 允許指定 HTTP 請求方法(例如 method = RequestMapping.GET)或特定請求參數(例如 params = "action=save"),所有這些都縮小了特定方法的類型層級映射。 或者,類型層級的 @RequestMapping 也可以與優秀的 Controller 介面實作結合使用 - 例如 SimpleFormController 或 MultiActionController。

彈性的處理器方法簽章

映射是我所謂的「顯而易見」的部分,因為很清楚那裡發生了什麼。 現在,不太明顯的部分:處理器方法簽章。 這是一個非常靈活的事情,不像優秀的 Controller 或 MultiActionController 案例那樣與非常特定的簽章相關聯。 您當然可以使用標準的 HttpServletRequest/HttpServletResponse/ModelAndView 簽章,但真正的力量在於使用更具體的引數。

「imagedb」範例顯示了幾個基本變體

@RequestMapping("/imageList")
public String showImageList(ModelMap model) {
model.addAttribute("images", this.imageDatabase.getImages());
return "imageList";
}

對於此處理器方法,要解析的唯一引數是 ModelMap。 ModelMap 是 Spring 2.0 重新設計的 ModelAndView 物件的一部分,封裝了一系列將暴露給檢視的名稱-值屬性對。 上面的程式碼只是呼叫 ImageDatabase 服務來載入 ImageDescriptor 物件的 List,並在屬性名稱「images」下公開它們。 或者,您可以呼叫沒有屬性名稱的 addAttribute 變體,在這種情況下,名稱將從給定的值類型推斷出來(在我們的案例中: 「imageDescriptorList」)。

傳回值是一個字串,只是指示要渲染的檢視的名稱。 本質上,您可以編寫相同的方法,沒有引數且傳回值為 ModelAndView - 但以上通常更容易閱讀,並且避免了對 ModelAndView 物件的依賴。(請注意,ModelMap 是「ui」封裝中的泛型類別,而 ModelAndView 是「web.servlet」封裝中相當特定的類別。)

@RequestMapping("/imageContent")
public void streamImageContent(@RequestParam("name") String name, OutputStream outputStream)
throws IOException {

this.imageDatabase.streamImage(name, outputStream);
}

此處理器方法顯示了一個完全不同的用例。 它的目的是將影像內容從資料庫串流到 HTTP 回應。 它直接寫入回應,而不轉發到檢視; 因此,它的傳回類型為 void。 它使用 Spring 2.5 的新 @RequestParam 註解來接收 HTTP 請求參數,形式為方法引數,以及 OutputStream 類型的引數,用於處理回應串流。 影像內容的實際載入再次委派給 ImageDatabase 服務。

或者,您可以使用更傳統的 HttpServletRequest/HttpServletResponse 簽章來實作相同的處理器方法,從而更好地控制精確的 HTTP 處理。 但是,這會引入與 Servlet API 更強的耦合,並且需要付出更多努力才能進行單元測試。

@RequestMapping("/imageContent")
public void streamImageContent(HttpServletRequest request, HttpServletResponse response)
throws IOException {

this.imageDatabase.streamImage(request.getParameter("name"), response.getOutputStream());
}

此類處理器方法的目的應該已經變得明顯:它們是 HTTP 請求世界和服務層世界之間相當簡單的「橋樑」,用於適應請求參數和回應內容。 讓我們看一下影像上傳處理器以獲取進階範例。

@RequestMapping("/imageUpload")
public String processImageUpload(
@RequestParam("name") String name, @RequestParam("description") String description,
@RequestParam("image") MultipartFile image) throws IOException {

this.imageDatabase.storeImage(name, image.getInputStream(), (int) image.getSize(), description);
return "redirect:imageList";
}

基本目的再次是接受幾個特定的 HTTP 請求參數,執行一些處理,然後傳回檢視的名稱 - 在這種情況下,表示重新導向到「imageList」路徑。 但是,此特定方法會處理多部分檔案上傳,這就是為什麼「image」引數宣告為 MultipartFile 類型。 Spring 的 @RequestParam 處理會自動將其解析為多部分元素,以便處理器方法能夠取得檔案大小並以 InputStream 的形式存取上傳的檔案內容。

如需註解式處理器方法支援的引數類型的完整清單,請參閱 @RequestMapping javadoc

超越無狀態多動作控制器

以上是用於實作多動作控制器的 Spring 2.5 Web 註解。 相同的控制器樣式也可以容納基本表單處理,取代優秀的 SimpleFormController。 這可以在 Spring 2.5 版本的 PetClinic 中看到,該版本的所有表單控制器現在都以註解樣式實作,顯示了表單物件和基於 JavaBean 的資料繫結的使用。 對於這些表單處理功能的討論將成為後續文章的主題。

讓我總結一下 Spring MVC 的目的:僅限於無狀態控制器、基本表單處理和彈性的檢視渲染。 MVC 本質上是 Spring 核心 Web 支援的一個以分派為中心的模組,可以作為許多不同種類用途的執行時間 - 以及在之上建構的更高級別的功能。 在這方面,它類似於 Java EE 5 的 JSF 執行時間,後者也主要作為一個基本的 Web 平台,用於在其之上建構更高級別的功能。

這就是 Spring Web Flow 進入畫面的地方:SWF 是我們更高級別的、面向對話的控制器引擎,對 *MVC 檢視* 和 *JSF 檢視* 都有強大的支援。 我強烈建議查看 SWF 以建構 Web 使用者介面,尤其是在面臨重要的導航和狀態管理需求時。 在 Spring Web Flow 2.0 里程碑中,發生著令人興奮的事情,這與 Spring 2.5 MVC 基礎的發展方向一致 - 同時也透過我們新的 Spring Faces 模組特別關注 JSF。 敬請關注!

取得 Spring 電子報

透過 Spring 電子報保持聯繫

訂閱

領先一步

VMware 提供培訓和認證來加速您的進度。

瞭解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔。

瞭解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

檢視所有活動