青豆:Spring MVC 入門

工程 | Colin Sampaleanu | 2011 年 1 月 4 日 | ...

Spring MVC 作為核心 Spring Framework 的一部分,是一個成熟且功能強大的動作-響應式 Web 框架,具有廣泛的能力和選項,旨在處理各種面向 UI 和非面向 UI 的 Web 層用例。所有這些對於 Spring MVC 新手來說可能令人望而生畏。我認為向這類讀者展示啟動和執行一個最簡單的 Spring MVC 應用程式所需的工作量非常少是有益的(也就是說,把我的例子視為類似世界上最簡單的 Spring MVC 應用程式),這就是本文其餘部分將要演示的內容。

我假設你熟悉 Java、Spring(基本的依賴注入概念)以及基本的 Servlet 程式設計模型,但不瞭解 Spring MVC。閱讀這篇部落格文章後,讀者可以繼續學習 Spring MVC,方法是檢視 Keith Donald 的Spring MVC 3 Showcase,或參考涵蓋 Spring 和 Spring MVC 的各種其他線上和印刷資源。

關於依賴和構建系統:本文不假設您正在使用特定的構建系統,例如 Maven、Gradle 或 Ant。文章末尾包含了一個相當簡潔的 Maven POM 示例檔案,可作為參考。

Spring MVC 包含了大多數其他所謂的 Web MVC 框架的基本概念。傳入的請求透過一個前端控制器 (Front Controller) 進入框架。在 Spring MVC 中,這是一個實際的 Java Servlet,名為 DispatcherServlet。可以將 DispatcherServlet 視為守門員。它不執行任何實際的 Web 或業務邏輯,而是將工作委託給稱為控制器 (Controllers) 的 POJO,實際工作在那裡完成(無論是全部完成還是通過後端完成)。工作完成後,由檢視 (Views) 負責以適當的格式生成輸出(無論是 JSP 頁面、Velocity 模板還是 JSON 響應)。使用策略 (Strategies) 來決定哪個控制器(以及該控制器內的哪個方法)處理請求,以及哪個檢視渲染響應。Spring 容器用於將所有這些部分連線在一起。它看起來像這樣

啟動 DispatcherServlet 和 Spring 容器

如前所述,所有傳入的請求都流經 DispatcherServlet。就像 Java EE 應用程式中的任何其他 Servlet 一樣,我們透過 Web 應用的 WEB-INF/web.xml 中的一個配置告訴 Java EE 容器在 Web 應用啟動時載入此 Servlet。DispatcherServlet 還負責載入一個 Spring ApplicationContext,用於執行託管元件的裝配和依賴注入。在此基礎上,我們為 Servlet 指定一些初始化引數來配置 Application Context。讓我們看看 web.xml 中的配置

WEB-INF/web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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-app_2_5.xsd">

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

這裡完成了一些事情

  • 我們將 DispatcherServlet 註冊為一個名為 appServlet 的 Servlet
  • 我們將此 Servlet 對映為處理以 "/" 開頭(相對於應用路徑)的傳入請求
  • 我們使用 ContextConfigLocation 初始化引數來自定義 DispatcherServlet 載入的 Spring Application Context 的基本配置 XML 檔案的位置,而不是依賴於預設位置 <servletname>-context.xml)。
等等,如果有人不想透過 XML 配置 Spring 怎麼辦?

DispatcherServlet 載入的 Application Context 的預設型別期望至少載入一個包含 Spring Bean 定義的 XML 檔案。正如您將看到的,我們還將使 Spring 能夠載入基於 Java 的配置,與 XML 並存。

每個人在此領域都有自己的(有時是非常強烈的)看法,但雖然我通常偏愛基於 Java 的配置,但我確實相信在某些領域使用少量 XML 配置有時仍然更有意義,原因有很多(例如,無需重新編譯即可更改配置、XML 名稱空間的簡潔性、工具支援等)。基於此,此應用將採用混合方法,同時支援 Java 和 XML。

請放心,如果您偏愛純 Java 方法,完全不使用 Spring XML,這很容易實現,只需在 web.xml 中設定一個初始化引數來覆蓋預設的 Application Context 型別,並改用一個名為 AnnotationConfigWebApplicationContext 的變體即可。

控制器

現在我們建立一個最簡單的控制器

package xyz.sample.baremvc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {

	@RequestMapping(value = "/")
	public String home() {
		System.out.println("HomeController: Passing through...");
		return "WEB-INF/views/home.jsp";
	}
}

讓我們來看看這個類的關鍵方面

  • 該類已使用 @Controller 註解進行標註,表明這是一個能夠處理 Web 請求的 Spring MVC 控制器。由於 @Controller 是 Spring @Component Stereotype 註解的特化,該類將作為容器元件掃描過程的一部分被 Spring 容器自動檢測到,從而建立一個 Bean 定義,並允許像任何其他 Spring 管理的元件一樣進行依賴注入。
  • home 方法已使用 @RequestMapping 註解進行標註,指定該方法應處理到路徑 "/" 的 Web 請求,即應用程式的主頁 (home) 路徑。
  • home 方法只是簡單地向系統輸出列印一條訊息,然後返回 WEB-INF/views/home.jsp,指示應處理響應的檢視,在本例中是一個 JSP 頁面。(如果將包含 WEB-INF 字首的完整檢視路徑以及它是 JSP 的事實硬編碼到程式碼中,您覺得這不對,那您是對的。我們稍後會解決這個問題)
現在,我們需要建立檢視。這個 JSP 頁面將簡單地列印一條問候語。

WEB-INF/views/home.jsp


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
	<head>
		<title>Home</title>
	</head>
	<body>
		<h1>Hello world!</h1>
	</body>
</html>

最後,如前所述,我們需要建立一個最簡單的 Spring Application Context 定義檔案。

WEB-INF/spring/appServlet/servlet-context.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

	<!-- Scans within the base package of the application for @Components to configure as beans -->
	<!-- @Controller, @Service, @Configuration, etc. -->
	<context:component-scan base-package="xyz.sample.baremvc" />

	<!-- Enables the Spring MVC @Controller programming model -->
	<mvc:annotation-driven />

</beans>

讓我們檢查一下這個檔案的內容

  • 您會注意到這裡使用了幾種不同的 Spring XML 名稱空間:contextmvc 和預設的 beans
  • <context:component-scan> 聲明確保 Spring 容器進行元件掃描,以便自動發現任何使用 @Component 子型別(例如 @Controller)註解的程式碼。您會注意到,為了提高效率,我們限制了 Spring 在類路徑中掃描的包空間範圍(在本例中限制為 xyz.sample.baremvc
  • <mvc:annotation-driven> 宣告設定了 Spring MVC 對請求路由到 @Controllers 的支援,以及如何處理轉換、格式化和驗證等方面的內容(基於類路徑中存在的(庫)提供了一些合理的預設設定,並可以在需要時進行覆蓋)
Web 應用現在已準備好執行。假設 Servlet 容器(我的例子中使用 tc Server)設定為在 localhost:8080 監聽,啟動應用程式並透過瀏覽器訪問 URL https://:8080/baremvc,結果將顯示預期的問候語:

儘管它很簡單,但執行此應用程式涉及到工作中的 Spring MVC 應用程式的所有主要部分。讓我們逐步瞭解主要的序列和元件互動

  • Web 應用啟動時,由於 web.xml 中的配置,DispatcherServlet 被載入並初始化。
  • DispatcherServlet 載入基於註解的 Application Context,該上下文已配置為透過指定基本包的正則表示式掃描帶註解的元件。
  • 帶註解的元件(如 HomeController)被容器檢測到。
  • https://:8080/baremvc 的 HTTP 請求到達 Servlet 引擎並被路由到我們的 (baremvc) Web 應用。
  • URL 末尾隱含的 "/" 路徑匹配為 DispatcherServlet 註冊的正則表示式,請求被路由到它
  • DispatcherServlet 需要決定如何處理請求。它使用一個稱為 HandlerAdapter策略 (strategy) 來決定將請求路由到何處。可以定製要使用的特定 HandlerAdapter 型別(或多種型別,因為它們可以鏈式使用),但預設情況下使用基於註解的策略,它根據這些類中 @RequestMapping 註解中的匹配條件,將請求適當地路由到標註為 @Controller 的類中的特定方法。在本例中,匹配到 home 方法上的正則表示式,並呼叫它來處理請求。
  • home 方法完成其工作,在本例中只是向系統輸出列印一些內容。然後它返回一個字串,這是一個提示(在本例中是一個非常明確的提示,即 WEB-INF/views/home.jsp),以幫助選擇要渲染響應的檢視。
  • DispatcherServlet 再次依賴於一個稱為 ViewResolver 的策略來決定哪個檢視負責渲染響應。可以根據應用程式需要配置此策略(以簡單或鏈式方式),但預設情況下使用 InternalResourceViewResolver。這是一個非常簡單的檢視解析器,它生成一個 JstlView,該檢視簡單地委託給 Servlet 引擎內部的 RequestDispatcher 進行渲染,因此適用於 JSP 頁面或 HTML 頁面。
  • Servlet 引擎透過指定的 JSP 渲染響應

更進一步

到目前為止,我們已經構建了一個確實符合世界上最簡單的 Spring MVC 應用程式的應用,但坦率地說,它並沒有真正達到該描述的精神。讓我們將事情發展到另一個層次。

如前所述,將檢視模板的路徑硬編碼到控制器中是不合適的,就像我們當前的控制器那樣。控制器和檢視之間更鬆散、更具邏輯性的耦合,控制器專注於執行某些 Web 或業務邏輯,並且通常不關心檢視路徑、JSP 與其他模板技術等具體細節,這是關注點分離 (separation of concerns) 的一個例子。這使得控制器和檢視都能得到更大的重用,並且更容易獨立地進行演變,可能由不同的人負責不同型別的程式碼。

本質上,理想的控制器程式碼應該類似於這個變體,返回一個純邏輯的檢視名稱(無論是簡單的還是組合的)


//...
@Controller
public class HomeController {

	@RequestMapping(value = "/")
	public String home() {
		System.out.println("HomeController: Passing through...");
		return "home";
	}
}

Spring MVC 的 ViewResolver 策略 (Strategy) 實際上是用於實現控制器和檢視之間這種更鬆散耦合的機制。如前所述,在應用程式未配置特定的 ViewResolver 的情況下,Spring MVC 會設定一個預設的、配置最簡單的 InternalResourceViewResolver,這是一個非常簡單的檢視解析器,它生成一個 JstlView。我們可以使用其他潛在的檢視解析器,但為了獲得更好的解耦級別,我們實際上只需要配置自己的 InternalResourceViewResolver 例項,並進行一些微調。InternalResourceViewResolver 採用一種非常簡單的策略;它僅僅接收控制器返回的檢視名稱,並在其前面新增一個可選的字首(預設為空),在其後面新增一個可選的字尾(預設為空),然後將生成的結果路徑提供給它建立的 JstlViewJstlView 隨後委託給 Servlet 引擎的 RequestDispatcher 來完成實際工作,即渲染模板。因此,為了允許控制器返回像 home 這樣的邏輯檢視名稱,而不是像 WEB-INF/views/home.jsp 這樣的特定檢視模板路徑,我們只需將此檢視解析器配置字首為 WEB-INF/views,字尾為 .jsp,這樣它就會分別將這些新增到控制器返回的邏輯名稱前面和後面。

配置檢視解析器例項的一種簡單方法是引入使用 Spring 基於 Java 的容器配置,將解析器作為 Bean 定義


package xyz.sample.baremvc;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
public class AppConfig {

	// Resolve logical view names to .jsp resources in the /WEB-INF/views directory
	@Bean
	ViewResolver viewResolver() {
		InternalResourceViewResolver resolver = new InternalResourceViewResolver();
		resolver.setPrefix("WEB-INF/views/");
		resolver.setSuffix(".jsp");
		return resolver;
	}
}

我們已經進行了元件掃描,因此由於 @Cofiguration 本身就是一個 @Component,這個包含(解析器)bean 的新配置定義會被 Spring 容器自動檢測到。然後 Spring MVC 會掃描所有 Bean 並找到解析器。

這是一種不錯的方法,但有些人可能更喜歡將解析器配置為 XML 定義檔案中的 Bean,例如


	<!-- Resolve logical view names to .jsp resources in the /WEB-INF/views directory -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/" />
		<property name="suffix" value=".jsp" />
	</bean>

很難說對於這個物件,哪種特定方法比另一種好得多,因此在這種情況下這確實是個人偏好的問題(我們實際上可以看到 Spring 的一個優勢,即其靈活的特性)。

處理使用者輸入

幾乎任何 Web 應用都需要接收客戶端的輸入,對其進行處理,然後返回或渲染結果。將輸入匯入 Spring MVC 應用程式的方法有很多種,渲染結果的方法也有很多種,但至少讓我們展示其中一種變體。在這個簡單的例子中,我們將修改 HomeController,新增一個新的處理方法,該方法接收兩個字串輸入,比較它們,並返回結果。

package xyz.sample.baremvc;

import java.util.Comparator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {

	@Autowired
	Comparator<String> comparator;

	@RequestMapping(value = "/")
	public String home() {
		System.out.println("HomeController: Passing through...");
		return "home";
	}

	@RequestMapping(value = "/compare", method = RequestMethod.GET)
	public String compare(@RequestParam("input1") String input1,
			@RequestParam("input2") String input2, Model model) {

		int result = comparator.compare(input1, input2);
		String inEnglish = (result < 0) ? "less than" : (result > 0 ? "greater than" : "equal to");

		String output = "According to our Comparator, '" + input1 + "' is " + inEnglish + "'" + input2 + "'";

		model.addAttribute("output", output);
		return "compareResult";
	}
}

新程式碼的關鍵元素

  • 我們使用另一個 @RequestMapping 註解將以路徑 /compare 結尾的請求對映到新的 compare 方法
  • 我們期望呼叫者將兩個字串輸入引數作為 GET 請求的一部分傳遞給我們,因此我們透過 @RequestParam 註解獲取它們。請注意,我們依賴此註解的預設處理方式,即這些引數是必需的。如果它們缺失,客戶端將收到 HTTP 400 錯誤。另請注意,這只是將引數傳遞到 Spring MVC 應用程式的一種方式。例如,對於更 RESTful 的方法,很容易獲取嵌入在請求 URL 路徑本身中的引數
  • 我們使用 Comparator 例項來比較這兩個字串
  • 我們將比較結果放入以 result 為鍵的 Model 物件中,以便檢視可以訪問它。簡單地說,可以將 Model 視為一個功能更強的雜湊對映 (hashmap)。
雖然我們可以修改現有的檢視來顯示比較結果,但我們選擇了使用一個新的檢視模板

WEB-INF/views/compareResult.jsp


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
	<head>
		<title>Result</title>
	</head>
	<body>
		<h1><c:out value="${output}"></c:out></h1>
	</body>
</html>

最後,我們需要為控制器提供一個要使用的 Comparator 例項。我們已使用 Spring 的 @Autowired 註解(在控制器被檢測到後將自動檢測到)標註控制器中的 comparator 欄位,並指示 Spring 容器將一個 Comparator 注入到該欄位中。因此,我們需要確保容器中有一個可用的例項。為此,我們建立了一個最簡單的 Comparator 實現,它只執行不區分大小寫的比較。為了簡單起見,這個類本身已使用 Spring 的 @Service Stereotype 註解(屬於 @Component 的一種)進行標註,因此它將作為容器元件掃描過程的一部分被 Spring 容器自動檢測到,並注入到控制器中。


package xyz.sample.baremvc;

import java.util.Comparator;
import org.springframework.stereotype.Service;

@Service
public class CaseInsensitiveComparator implements Comparator<String> {
	public int compare(String s1, String s2) {
		assert s1 != null && s2 != null;
		return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
	}
}

請注意,我們也可以透過 @Configuration 類中基於 Java 的 @Bean 定義或基於 XML 的 Bean 定義輕鬆地在容器中宣告此類的例項,當然在許多情況下,這些變體可能更受青睞,因為它們提供了更高的控制級別。

我們現在可以啟動應用程式,並透過類似 https://:8080/baremvc/compare?input1=Donkey&amp;input2=dog 的 URL 訪問它,以測試新程式碼

後續步驟

我只是觸及了 Spring MVC 功能的冰山一角,但希望這篇部落格文章能讓您瞭解開始使用 Spring MVC 是多麼容易,以及框架中的一些核心概念是如何關聯在一起的。此時可能也適合提及,為了更容易理解核心概念,我的示例中有一些(希望非常明顯)的處理方式與在更大或生產應用程式中的方式不同,例如,將訊息硬編碼在 Java 程式碼內部,或一些包組織方式。

現在我鼓勵您進一步學習和嘗試 Spring MVC 的全部功能和綜合能力,包括請求到控制器和方法的對映、資料繫結和驗證、區域設定和主題支援,以及能夠根據需要進行定製以處理幾乎所有符合動作-響應模型的 Web 層用例。

在此學習過程中,一個有價值的資源是 Keith Donald 的Spring MVC 3 Showcase,其中包含演示 Spring MVC 大多數功能的工作程式碼,您可以輕鬆將其載入到 SpringSource Tool Suite (STS) 或其他 Eclipse 環境中進行實驗。順便說一句,如果您不熟悉 STS,我應該提一下,它是用於試驗 Spring 技術集的一個很棒的工具,因為它對 Spring 的出色支援以及內建的專案模板等功能。在這段簡短的影片錄製中,我展示瞭如何透過 STS 模板開始構建一個新的 Spring MVC 應用程式。

附錄 - 依賴

無論您使用哪種構建系統(如今,我通常更喜歡 Gradle 或 Maven),上面的程式碼都應該可以工作。這裡提供了一個相對簡潔的 Maven POM 示例檔案,用於構建上述專案,可用作基礎,或幫助理解所需的依賴項。實際上,它並沒有儘可能地簡潔,因為我明確地將 commons-logging 替換為 SLF4J,並添加了一些可選的倉庫和 Maven 外掛。唯一不太明顯的地方可能是對 CGLIB 的需求,這是 Spring 在使用 @Configuration 時所需的可選依賴項。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>xyz.sample</groupId>
	<artifactId>baremvc</artifactId>
	<name>Sprring MVC sample project</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.6</java-version>
		<org.springframework-version>3.0.6.RELEASE</org.springframework-version>
		<org.slf4j-version>1.6.1</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				 </exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- CGLIB, only required and used for @Configuration usage -->
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib-nodep</artifactId>
			<version>2.2</version>
		</dependency>

		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.16</version>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>

	</dependencies>
	<repositories>
		<repository>
			<id>org.springframework.maven.release</id>
			<name>Spring Maven Release Repository</name>
			<url>http://maven.springframework.org/release</url>
			<releases><enabled>true</enabled></releases>
			<snapshots><enabled>false</enabled></snapshots>
		</repository>
		<!-- For testing against latest Spring snapshots -->
		<repository>
			<id>org.springframework.maven.snapshot</id>
			<name>Spring Maven Snapshot Repository</name>
			<url>http://maven.springframework.org/snapshot</url>
			<releases><enabled>false</enabled></releases>
			<snapshots><enabled>true</enabled></snapshots>
		</repository>
		<!-- For developing against latest Spring milestones -->
		<repository>
			<id>org.springframework.maven.milestone</id>
			<name>Spring Maven Milestone Repository</name>
			<url>http://maven.springframework.org/milestone</url>
			<snapshots><enabled>false</enabled></snapshots>
		</repository>
	</repositories>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java-version}</source>
					<target>${java-version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<configuration>
					<warName>baremvc</warName>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>tomcat-maven-plugin</artifactId>
				<version>1.1</version>
			</plugin>
		</plugins>
	</build>
</project>

訂閱 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲取支援

Tanzu Spring 透過一項簡單的訂閱,提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

近期活動

檢視 Spring 社群的所有近期活動。

檢視全部