?
This document uses PHP Chinese website manual Release
現(xiàn)時(shí)對(duì)于一些類型的配置數(shù)據(jù)有一個(gè)趨勢(shì),就是偏愛注解方式而不是XML文件。為了方便實(shí)現(xiàn),Spring現(xiàn)在(從2.5開始)提供了使用注解配置MVC框架下的組件的支持。
Spring 2.5為MVC控制器引入了一種基于注解的編程模型,在其中使用諸如@RequestMapping
、@RequestParam
、@ModelAttribute
,等等。
這種注解支持在Servlet MVC和Portlet MVC中均可使用。通過這種方式實(shí)現(xiàn)的控制器不必由特定的基類繼承而來,或者實(shí)現(xiàn)特定的接口。
更進(jìn)一步的,它們通常并不直接依賴于Servlet或Portlet API,雖然如果需要,它們可以方便的訪問Servlet或Portlet的功能。
Spring發(fā)行版本附帶了PetClinic示例,它是一個(gè)在簡(jiǎn)單的表單處理的上下文中,
利用了本節(jié)中說明的注解支持的Web應(yīng)用程序。
可以在“samples/petclinic
”目錄中找到PetClinic應(yīng)用程序。
另外一個(gè)建立在基于注解的Web MVC上的示例應(yīng)用程序,請(qǐng)見imagedb。
這個(gè)示例集中在無狀態(tài)的multi-action控制器,包括多段文件上傳的處理。
可以在“samples/imagedb
”目錄找到imagedb應(yīng)用程序。
下面的章節(jié)記錄了這些注解以及通常如何使用它們。
只有對(duì)應(yīng)的HandlerMapping
(為了實(shí)現(xiàn)類型級(jí)別的注解)和/或HandlerAdapter
(為了實(shí)現(xiàn)方法級(jí)別的注解)出現(xiàn)在dispatcher中時(shí),
@RequestMapping
才會(huì)被處理。
這在DispatcherServlet
和DispatcherPortlet
中都是缺省的行為。
然而,如果是在定義自己的HandlerMappings
或HandlerAdapters
,
就需要確保一個(gè)對(duì)應(yīng)的自定義的DefaultAnnotationHandlerMapping
和/或AnnotationMethodHandlerAdapter
同樣被定義――假設(shè)想要使用@RequestMapping
。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean class="org.springframework.web.servlet.mvc.DefaultAnnotationHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.AnnotationMethodHandlerAdapter"/> ... (controller bean definitions) ... </beans>
如果你想要自定義映射策略,顯式的定義一個(gè)DefaultAnnotationHandlerMapping
和/或AnnotationMethodHandlerAdapter
也有實(shí)際意義。
例如,指定一個(gè)自定義的PathMatcher
或者WebBindingInitializer
(見下面)。
注解@Controller
指明一個(gè)特定的類承擔(dān)控制器的職責(zé),
而沒有擴(kuò)展任何控制器基類或者引用Servlet API的必要。當(dāng)然,如果需要還是可以引用特定Servlet功能。
注解@Controller
的基本目標(biāo)是擔(dān)任所注解的類的原型的角色,指明它的職責(zé)。
Dispatcher將會(huì)在這樣被注解的類中掃描映射的方法,探測(cè)注解@RequestMapping
(見下一節(jié))。
所注解的控制器bean可以被顯式定義,這個(gè)過程是在dispatcher的上下文中使用一個(gè)標(biāo)準(zhǔn)的Spring bean定義完成的。
然而,@Controller
原型也允許自動(dòng)探測(cè),就像Spring 2.5對(duì)探測(cè)組件的類以及為它們自動(dòng)注冊(cè)bean定義的普遍支持一樣。
要實(shí)現(xiàn)對(duì)這樣的所注解的控制器的自動(dòng)探測(cè),必須要向配置中加入組件掃描的部分。 通過使用在下面的XML片段中所展示出的spring-context schema,這很容易實(shí)現(xiàn):
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="org.springframework.samples.petclinic.web"/> ... </beans>
注解@RequestMapping
被用于映射如“/editPet.do”這樣的URL到一個(gè)完整的類或者一個(gè)特定的處理方法。
典型的,頂層的注解映射一個(gè)特定的請(qǐng)求路徑(或者路徑模式)到一個(gè)表單控制器,另外的方法一級(jí)的注解可以縮小這個(gè)主要映射的范圍,包括對(duì)于一個(gè)特定的HTTP請(qǐng)求方法(“GET/POST”)或者特定的HTTP請(qǐng)求參數(shù)。
@RequestMapping
在類型一級(jí)也可以被用于Controller
接口的普通實(shí)現(xiàn)。
在這種情況下,請(qǐng)求處理的代碼會(huì)遵循傳統(tǒng)的handleRequest
模樣,而控制器的映射將會(huì)通過一個(gè)@RequestMapping
注解體現(xiàn)。
這對(duì)于預(yù)先構(gòu)建的Controller
基類,諸如SimpleFormController
,也一樣有效。
在下面的討論中,我們將會(huì)關(guān)注基于通過注解實(shí)現(xiàn)的處理方法的控制器。
下面是一個(gè)使用了這種注解的表單控制器的例子,它選自PetClinic:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { private final Clinic clinic; @Autowired public EditPetForm(Clinic clinic) { this.clinic = clinic; } @ModelAttribute("types") public Collection<PetType> populatePetTypes() { return this.clinic.getPetTypes(); } @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } else { this.clinic.storePet(pet); status.setComplete(); return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); } } }
對(duì)于一個(gè)傳統(tǒng)的multi-action控制器,由于控制器會(huì)響應(yīng)多個(gè)URL,URL就通常被直接映射到方法上。
下面是一個(gè)使用了@RequestMapping
的multi-action控制器的例子,它選自PetClinic:
@Controller public class ClinicController { private final Clinic clinic; @Autowired public ClinicController(Clinic clinic) { this.clinic = clinic; } /** * Custom handler for the welcome view. * Note that this handler relies on the RequestToViewNameTranslator to * determine the logical view name based on the request URL: "/welcome.do" * -> "welcome". */ @RequestMapping("/welcome.do") public void welcomeHandler() { } /** * Custom handler for displaying vets. * Note that this handler returns a plain {@link ModelMap} object instead of * a ModelAndView, thus leveraging convention-based model attribute names. * It relies on the RequestToViewNameTranslator to determine the logical * view name based on the request URL: "/vets.do" -> "vets". * * @return a ModelMap with the model attributes for the view */ @RequestMapping("/vets.do") public ModelMap vetsHandler() { return new ModelMap(this.clinic.getVets()); } /** * Custom handler for displaying an owner. * Note that this handler returns a plain {@link ModelMap} object instead of * a ModelAndView, thus leveraging convention-based model attribute names. * It relies on the RequestToViewNameTranslator to determine the logical * view name based on the request URL: "/owner.do" -> "owner". * * @param ownerId the ID of the owner to display * @return a ModelMap with the model attributes for the view */ @RequestMapping("/owner.do") public ModelMap ownerHandler(@RequestParam("ownerId") int ownerId) { return new ModelMap(this.clinic.loadOwner(ownerId)); } }
使用@RequestMapping
注解的處理器方法允許具有非常靈活的外觀。
它們可以擁有下面類型的參數(shù),在任意的順序下(除非是對(duì)于驗(yàn)證結(jié)果,它需要緊跟在對(duì)應(yīng)的命令對(duì)象后面,如果需要):
請(qǐng)求和/或響應(yīng)對(duì)象(Servlet API或者Portlet API)。 可以選擇任何特定的請(qǐng)求/響應(yīng)類型,例如,ServletRequest/HttpServletRequest或者PortletRequest/ActionRequest/RenderRequest。 注意那個(gè)Portlet的例子里,一個(gè)被顯式聲明了的action/render參數(shù)被用于映射特定的請(qǐng)求類型到一個(gè)處理方法(在沒有提供其他信息來區(qū)分action和render requests的情況下)。
會(huì)話對(duì)象(Servlet API或者Portlet API):不管是HttpSession還是PortletSession。
一個(gè)此種類型的參數(shù)將會(huì)保證出現(xiàn)一個(gè)對(duì)應(yīng)的會(huì)話。這樣就造成,這樣一個(gè)參數(shù)永遠(yuǎn)也不可以是null
。
注意會(huì)話訪問可以并不是線程安全的,特別是在Servlet環(huán)境中:如果允許多個(gè)請(qǐng)求同時(shí)訪問一個(gè)會(huì)話,就考慮把AnnotationMethodHandlerAdapter
的“synchronizeOnSession”旗標(biāo)置為“true”
org.springframework.web.context.request.WebRequest
或org.springframework.web.context.request.NativeWebRequest
。
允許像訪問請(qǐng)求/會(huì)話屬性一樣的訪問一般的請(qǐng)求參數(shù),而不是鎖定在原生的Servlet/Portlet API上。
java.util.Locale
用于當(dāng)前請(qǐng)求區(qū)域?qū)傩裕ㄓ煽捎玫淖罱咏膮^(qū)域?qū)傩越馕銎鳑Q定,也就是,
在Servlet環(huán)境中配置好的LocaleResolver
以及在Portlet環(huán)境中的portal locale)。
java.io.InputStream
/java.io.Reader
用于訪問請(qǐng)求的內(nèi)容。
這將是Servlet/Portlet API暴露出的天然的InputStream/Reader。
java.io.OutputStream
/java.io.Writer
用于生成響應(yīng)的內(nèi)容。
這將是Servlet/Portlet API暴露出的天然的OutputStream/Writer。
以@RequestParam
注解的參數(shù)用于訪問特定的Servlet/Portlet請(qǐng)求參數(shù)。
參數(shù)的值將被轉(zhuǎn)換為聲明的方法參數(shù)類型。
java.util.Map
/org.springframework.ui.Model
/org.springframework.ui.ModelMap
用于充實(shí)將被暴露到Web視圖的隱含模型。
綁定參數(shù)到的命令/表單對(duì)象:帶有自定義的類型轉(zhuǎn)換的bean屬性或者域,依賴于@InitBinder
方法和/或HandlerAdapter配置――參見AnnotationMethodHandlerAdapter
的“webBindingInitializer
”屬性。
這樣的命令對(duì)象,包括它們的驗(yàn)證結(jié)果,將會(huì)暴露為模型屬性,默認(rèn)的會(huì)在屬性注解中使用非限定的命令類名(例如,對(duì)于類型“mypackage.OrderAddress”使用“orderAddress”)。
為聲明一個(gè)特定的模型屬性名稱指定一個(gè)參數(shù)級(jí)別的ModelAttribute
注解。
org.springframework.validation.Errors
/org.springframework.validation.BindingResult
驗(yàn)證結(jié)果用于前面的一個(gè)命令/表單對(duì)象(前面緊接的參數(shù))。
org.springframework.web.bind.support.SessionStatus
狀態(tài)處理用于把表單處理過程標(biāo)記為已完成(觸發(fā)會(huì)話屬性的清理,這些會(huì)話屬性是在句柄類型級(jí)別由@SessionAttributes
注解指示出的)。
@RequestParam
注解是用于在控制器中綁定請(qǐng)求參數(shù)到方法參數(shù)。
下面取自PetClinic實(shí)例程序的代碼片段說明了這種用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... @RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ...
使用這個(gè)注解的參數(shù)默認(rèn)是必需的,但是可以把@RequestParam
的required
屬性置為false
從而讓這個(gè)參數(shù)可選(例如,@RequestParam(value="id", required="false")
)。
@ModelAttribute
在控制器中有兩種使用場(chǎng)景。
當(dāng)作為一個(gè)方法參數(shù)時(shí),@ModelAttribute
用于映射一個(gè)模型屬性到特定的注解的方法參數(shù)(見下面的processSubmit()
方法)。
這是控制器獲得持有表單數(shù)據(jù)的對(duì)象引用的方法。另外,這個(gè)參數(shù)也可以被聲明為特定類型的表單支持對(duì)象,而不是一般的java.lang.Object
,這就增加了類型安全性。
@ModelAttribute
也用于在方法級(jí)別為模型提供引用數(shù)據(jù)(見下面的populatePetTypes()
方法)。
在這種用法中,方法編寫可以包含與上面描述的@RequestMapping
注解相同的類型。
注意:使用@ModelAttribute
注解的方法將會(huì)在選定的使用@RequestMapping
注解的方法之前執(zhí)行。
它們有效的使用特定的屬性預(yù)先填充隱含的模型,這些屬性常常來自一個(gè)數(shù)據(jù)庫。
這樣一個(gè)屬性也就可以通過在選定的方法中使用@ModelAttribute
注解的句柄方法參數(shù)來訪問了,潛在的可以應(yīng)用綁定和驗(yàn)證。
下面的代碼片段展示了此注解的這兩種用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... @ModelAttribute("types") public Collection<PetType> populatePetTypes() { return this.clinic.getPetTypes(); } @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } else { this.clinic.storePet(pet); status.setComplete(); return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); } } }
類型級(jí)別的@SessionAttributes
注解使用一個(gè)特定的句柄聲明會(huì)話屬性。
這通常會(huì)列出模型屬性的名稱,這些屬性應(yīng)被透明的保存在會(huì)話或者對(duì)話存儲(chǔ)中,用于在后續(xù)的請(qǐng)求之間作為表單支持beans。
下面的代碼片段展示了此注解的這種用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... }
為了通過Spring的WebDataBinder
使用PropertyEditors等自定義請(qǐng)求參數(shù)綁定,可以或者使用@InitBinder
――在控制器之內(nèi)的注解的方法,
或者通過提供一個(gè)定制的WebBindingInitializer
把配置具體化。
使用@InitBinder
注解控制器方法,可以在控制器類內(nèi)部直接配置Web數(shù)據(jù)綁定。
@InitBinder
指定初始化WebDataBinder
的方法,
后者被用于填充注解的句柄方法的命令和表單對(duì)象參數(shù)。
這個(gè)init-binder方法支持@RequestMapping
支持的全部參數(shù),除了命令/表單對(duì)象和對(duì)應(yīng)的驗(yàn)證結(jié)果對(duì)象。
Init-binder方法必須沒有返回值。因此,它們常被聲明為void
。
典型的參數(shù),包括 WebDataBinder
以及WebRequest
或者java.util.Locale
,允許代碼注冊(cè)上下文特定的編輯器。
下面的例子說明了@InitBinder
的用法,為所有的java.util.Date
表單屬性配置一個(gè)CustomDateEditor
。
@Controller public class MyFormController { @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } // ... }
為了外化數(shù)據(jù)綁定初始化的過程,可以提供一個(gè)WebBindingInitializer
接口的自定義實(shí)現(xiàn)。
通過為一個(gè)AnnotationMethodHandlerAdapter
提供一個(gè)定制的bean配置可以使它啟用,這樣就覆蓋了默認(rèn)配置。
下面取自PetClinic應(yīng)用的例子展示了一個(gè)使用WebBindingInitializer
接口的自定義實(shí)現(xiàn)的配置――org.springframework.samples.petclinic.web.ClinicBindingInitializer
,
完成多個(gè)PetClinic控制器都需要的PropertyEditors的配置。
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="cacheSeconds" value="0" /> <property name="webBindingInitializer"> <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> </property> </bean>