[toc]
spring web mvc 基於註解的優化
我寫的註解是按照spring web的部件分類寫的,這樣的話比較方便查看,大家感覺有用的話可以分享個別人,希望對對更多的人有幫助。畢竟零基礎開始學這塊是感覺是比較亂,這裏寫的都比較簡單,關於這幾個部件的詳細介紹在我寫的spring web mvc 中有介紹,不懂得可以去看下。
註解
DispatcherServlet
-
DispatcherServlet 應用的其實就是一個“前端控制器”的設計模式。
-
DispatcherServlet其實就是個 Servlet (它繼承自 HttpServlet 基類) ,同樣也需要在你web應用的** web.xml 配置文件下聲明**。你需要在 web.xml 文件中把你希
望 DispatcherServlet 處理的請求映射到對應的URL上去。
配置DispatcherServlet就是標準的Java EE Servlet配置;下面的代碼就展示了對 DispatcherServlet 和路徑映射的聲明:
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>
</web-app>
在上面的例子中,所有路徑以 /example 開頭的請求都會被名字爲 example 的 DispatcherServlet 處理。
下面是一段這種基於代碼配置的例子,它與上面定義的 web.xml 配置文件是等效的。
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
ServletRegistration.Dynamic registration = container.addServlet("dispatcher",new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/example/*");
}
}
WebApplicationInitializer 是Spring MVC提供的一個接口,它會查找你所有基於代碼的配置,並應用它們來初始化Servlet 3版本以上的web容器。它有一個抽象的實現 AbstractDispatcherServletInitializer ,用以簡化 DispatcherServlet 的註冊工作:你只需要指定其servlet映射(mapping) 即可。
控制器(Controller)
控制器作爲應用程序邏輯的處理入口,它會負責去調用你已經實現的一些服務。通常,一個控制器會接收並解析用戶的請求,然後把它轉換成一個模型交給視圖,由視圖渲染出頁面最終呈現給用戶。
你可以在你的控制器實現上添加 **@RequestMapping 、 @RequestParam** 、 **@ModelAttribute** 等註解。註解特性既支持基於Servlet的MVC,也可支持基於Portlet的MVC。通過此種方式實現的控制器既無需繼承某個特定的基類,也無需實現某些特定的接口。
@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
@Controller 註解和 @RequestMapping 註解支持多樣的方法名和方法簽名。在上面這個例子中,方法接受一個 Model 類型的參數並返回一個字符串 String 類型的視圖名。但事實上,方法所支持的參數和返回值有非常多的選擇。
使用@Controller註解定義一個控制器
@Controller 註解表明了一個類是作爲控制器的角色而存在的。Spring不要求你去繼承任何控制器基類,也不要求你去實現Servlet的那套API。
@Controller 註解可以認爲是被標註類的原型(stereotype) ,表明了這個類所承擔的角色。分派器(DispatcherServlet ) 會掃描所有註解了 @Controller 的類,檢測其中通過 @RequestMapping 註解配置的方法 。
你也可以不使用 @Controller 註解而顯式地去定義被註解的bean,這點通過標準的Spring bean的定義方式,在dispather的上下文屬性下配置即可做到。但是 @Controller 原型是可以被框架自動檢測的,Spring支持classpath路徑下組件類的自動檢測,以及對已定義bean的自動註冊。
在配置中加入組件掃描的配置代碼來開啓框架對註解控制器的自動檢測
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.web"/>
<!-- base-package的值爲所在包的名稱 -->
</beans>
@RequestMapping註解映射請求路徑
使用 @RequestMapping 註解來將請求URL,如 /appointments 等,映射到整個類上或某個特定的處理器方法上。一般來說,類級別的註解負責將一個特定(或符合某種模式) 的請求路徑映射到一個控制器上,同時通過方法級別的註解來細化映射,即根據特定的HTTP請求方法(“GET”“POST”方法等) 、HTTP請求中是否攜帶特定參數等條件,將請求映射到匹配的方法上。
用 @RequestMapping 註解:
-
@RequestMapping(path = "/new", method = RequestMethod.GET)
-
@RequestMapping(path = "/new", method = RequestMethod.POST)
註解的表單控制器的例子
註解@RequestMapping被用於映射如“/editPet.do”這樣的URL到一個完整的類或者一個特定的處理方法。 典型的,頂層的註解映射一個特定的請求路徑(或者路徑模式)到一個表單控制器,另外的方法一級的註解可以縮小這個主要映射的範圍,包括對於一個特定的HTTP請求方法(“GET/POST”)或者特定的HTTP請求參數。
@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();
}
}
}
使用@RequestMapping註解的處理器方法允許具有非常靈活的外觀。 它們可以擁有很多類型的參數,比如
請求和/或響應對象(Servlet API或者Portlet API)。
會話對象(Servlet API或者Portlet API):不管是HttpSession還是PortletSession。
支持的方法返回類型
- ModelAndView
- 對象其中model隱含填充了命令對象,以及註解了 @ModelAttribute 字段
的存取器被調用所返回的值。
向頁面傳值的幾種方法
- 向頁面傳值的第一種方式:
- 通過ModelAndView。
將處理方法的返回值類型設置爲ModelAndView。
@RequestMapping("/login4.do")
public ModelAndView login4(User user){
System.out.println("HelloController的"+ "login4方法...");
System.out.println(user.getUsername() + " " + user.getPwd());
/*
* ModelAndView(String viewName,Map data);
* viewName:視圖名。
* data:處理結果。
*/
//將處理結果封裝成一個Map對象。
Map<String,Object> data = new HashMap<String,Object>();
//相當於執行了request.setAttribute
data.put("username", user.getUsername());
data.put("user", user);
ModelAndView mav = new ModelAndView("index",data);
return mav;
}
- 向頁面傳值的第二種方式:
- 通過ModelMap對象。
要將ModelMap對象作爲方法的入參。
@RequestMapping("/login5.do")
public String login5(User user,ModelMap mm){
System.out.println("HelloController的"+ "login5方法...");
System.out.println(user.getUsername() + " " + user.getPwd());
//將處理結果添加到ModelMap。
//相當於request.setAttribute
mm.addAttribute("user", user);
return "index";
}
- 向頁面傳值的第三種方式:
-
使用request。
@RequestMapping("/login6.do") public String login6(HttpServletRequest request){ System.out.println("login6方法..."); String username = request.getParameter("username"); System.out.println(username); request.setAttribute("username", username); return "index"; }
- 向頁面傳值的第四種方式:
-
使用session。
@RequestMapping("/login7.do")
public String login7(User user,HttpSession session){
System.out.println("login7方法...");
System.out.println(user.getUsername());
session.setAttribute("user", user);
return "index";
}
- String 對象,其值會被解析成一個邏輯視圖名。其中,model將默認填充了命令對象以及註解了 @ModelAttribute 字段的存取器被調用所返回的值。handler方法也可以增加一個 Model 類型的方法參數來增強model
下面爲讀取請求參數值的幾種方式
- 讀取請求參數值的第一種方式:
- 在處理方法裏面,添加request作爲 方法的入參即可。
前端控制器會將request對象作爲參數傳進來。
@RequestMapping("/login.do")
public String login(HttpServletRequest request){
System.out.println("HelloController的"+ "login方法...");
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
System.out.println("username:" + username + " pwd:" + pwd);
return "index";
}
- 讀取請求參數值的第二種方式:
- 將處理方法的入參與請求參數名保持一致。
注:如果不一致,可以使用@RequestParam(請求參數名)。
@RequestMapping("/login2.do")
public String login2(String username,@RequestParam("pwd") String pwd1){
System.out.println("HelloController的"+ "login2方法...");
System.out.println("username:" + username + " pwd:" + pwd1);
return "index";
}
- 讀取請求參數值的第三種方式:
- 將請求參數封裝成一個javabean。
@RequestMapping("/login3.do")
public String login3(User user){
System.out.println("HelloController的"+ "login3方法...");
System.out.println("username:"+ user.getUsername() + " pwd:" + user.getPwd());
return "index";
}
重定向
- 重定向的第一種方式:
- 如果處理方法的返回值是String,
則在重定向地址前添加"redirect:"作爲前綴。
@RequestMapping("/login8.do")
public String login8(User user){
System.out.println("login8方法...");
System.out.println(user.getUsername());
return "redirect:toIndex.do";
}
- 重定向的第二種方式:
- 如果處理方法的返回值是ModelAndView。
@RequestMapping("/login9.do")
public ModelAndView login9(User user){
RedirectView rv =
new RedirectView("toIndex.do");
ModelAndView mav =
new ModelAndView(rv);
return mav;
}
-
Model 對象
其中視圖名稱默認由 RequestToViewNameTranslator 決定,model隱含填充了命令對象以及註解了 @ModelAttribute 字段的存取器被調用所返回的值 -
Map 對象
用於暴露model,其中視圖名稱默認由 RequestToViewNameTranslator 決定,model隱含填充了命令對象以及註解了 @ModelAttribute 字段的存取器被調用所返回的值 - View 對象
-
其中model隱含填充了命令對象,以及註解了 @ModelAttribute 字段的存取器被調用所返回的值。handler方法也可以增加一個 Model 類型的方法參數來增強model
其他就不說了主要講的就這幾種。
使用@RequestParam綁定請求參數到方法參數
@RequestParam註解是用於在控制器中綁定請求參數到方法參數
@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";
}
// ...
使用這個註解的參數默認是必需的,但是可以把@RequestParam的required屬性置爲false從而讓這個參數可選(例如,@RequestParam(value="id", required="false"))。
使用@ModelAttribute提供一個從模型到數據的鏈接
當作爲一個方法參數時,@ModelAttribute用於映射一個模型屬性到特定的註解的方法參數
這是控制器獲得持有表單數據的對象引用的方法。另外,這個參數也可以被聲明爲特定類型的表單支持對象,而不是一般的java.lang.Object,這就增加了類型安全性。
使用@SessionAttributes指定存儲在會話中的屬性
類型級別的@SessionAttributes註解使用一個特定的句柄聲明會話屬性。 這通常會列出模型屬性的名稱,這些屬性應被透明的保存在會話或者對話存儲中,用於在後續的請求之間作爲表單支持beans。
@Controller
@RequestMapping("/editPet.do")
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
**使用@RequestBody註解映射請求體**
方法參數中的 @RequestBody 註解暗示了方法參數應該被綁定了HTTP請求體的值
@RequestMapping(path = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer)throws IOException {
writer.write(body);
}
請求體到方法參數的轉換是由 HttpMessageConverter 完成的。 HttpMessageConverter 負責將HTTP請求信息轉換成對象,以及將對象轉換回一個HTTP響應體。
定義@RequestMapping註解的處理方法
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<util:list >
<ref bean="stringHttpMessageConverter"/>
<ref bean="marshallingHttpMessageConverter"/>
</util:list>
</property
</bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="castorMarshaller"/>
<property name="unmarshaller" ref="castorMarshaller"/>
</bean>
<bean class="org.springframework.oxm.castor.CastorMarshaller"/>
註解了 @RequestBody 的方法參數還可以被 @Valid 註解,這樣框架會使用已配置的 Validator 實例來對該參數進行驗證。
使用@ResponseBody註解映射響應體
@ResponseBody 註解與 @RequestBody 註解類似。 @ResponseBody 註解可被應用於方法上,標誌該方法的返回值(更正,原文是return type,看起來應該是返回值) 應該被直接寫回到HTTP響應體中去(而不會被被放置到Model中或被解釋爲一個視圖名) 。
**@ResponseBody**可以用來返回json對象的
@RequestMapping(path = "/something", method = RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
return "Hello World"
}
使用@RestController註解創建REST控制器
當今讓控制器實現一個REST API是非常常見的,這種場景下控制器只需要提供JSON、XML或其他自定義的媒體類型內容即可。你不需要在每個 @RequestMapping 方法上都增加一個 @ResponseBody 註解,更簡明的做法是,給你的控制器加上一@RestController 的註解。
@RestController 是一個原生內置的註解,它結合了 @ResponseBody @Controller 註解的功能。不僅如此,它也讓你的控制器更表義,而且在框架未來的發佈版本中,它也可能承載更多的意義。
處理器映射(Handler Mappings)
用戶需要在web應用的上下文中定義一個或多個的 HandlerMapping bean,用以將進入容器的web請求映射到合適的處理器方法上。允許在控制器上添加註解後,通常你就不必這麼做了,因爲RequestMappingHandlerMapping 類會自動查找所有註解了 @RequestMapping 的 @Controller 控制器bean。
配置一個攔截器的方法:
<beans>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<bean class="example.MyInterceptor"/>
</property>
</bean>
<beans>
使用HandlerInterceptor攔截請求
Spring的處理器映射機制包含了處理器攔截器。攔截器在你需要爲特定類型的請求應用一些功能時可能很有用,比如,檢查用戶身份等
處理器映射處理過程配置的攔截器,必須實現 org.springframework.web.servlet 包下的HandlerInterceptor 接口。這個接口定義了三個方法:
- preHandle(..) ,它在處理器實際執行之前會被執行;
- postHandle(..) ,它在處理器執行 完畢 以後被執行;
- afterCompletion(..) ,它在 整個請求處理完成 之後被執行。
這三個方法爲各種類型的前處理和後處理需求提供了足夠的靈活性。
preHandle(..) 方法返回一個boolean值。你可以通過這個方法來決定是否繼續執行處理鏈中的部件。當方法返回 true 時,處理器鏈會繼續執行;若方法返回 false ,DispatcherServlet 即認爲攔截器自身已經完成了對請求的處理(比如說,已經渲染了一個合適的視圖) ,那麼其餘的攔截器以及執行鏈中的其他處理器就不會再被執行了。
攔截器可以通過 interceptors 屬性來配置,該選項在所有繼承了 AbstractHandlerMapping 的處理器映射類 HandlerMapping 都提供了配置的接口。
<beans>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<list>
<ref bean="officeHoursInterceptor"/>
</list>
</property>
</bean>
<bean class="samples.TimeBasedAccessInterceptor">
<property name="openingTime" value="9"/>
<property name="closingTime" value="18"/>
</bean>
<beans>
具體怎麼實現攔截器在這裏就不寫了
視圖解析
Spring提供了一些視圖解析器,它們讓你能夠在瀏覽器中渲染模型,並支持你自由選用適合的視圖技術而不必與框架綁定到一起。Spring原生支持JSP視圖技術、Velocity模板技術和XSLT視圖等。
有兩個接口在Spring處理視圖相關事宜時至關重要,分別是視圖解析器接口 ViewResolver 和視圖接口本身 View 。
-
視圖解析器 ViewResolver 負責處理視圖名與實際視圖之間的映射關係。
-
視圖接口 View 負責準備請求,並將請求的渲染交給某種具體的視圖技術實現。
使用ViewResolver接口解析視圖
Spring MVC中所有控制器的處理器方法都必須返回一個邏輯視圖的名字,無論是顯式返回(比如返回一個 String 、 View 或者 ModelAndView ) 還是隱式返回(比如基於約定的返回) 。Spring中的視圖由一個視圖名標識,並由視圖解析器來渲染。
假設這裏使用的是JSP視圖技術
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
若返回一個 test 邏輯視圖名,那麼該視圖解析器會將請求轉發到 RequestDispatcher ,後者會將請求交給 /WEB-INF/jsp/test.jsp 視圖去渲染。
如果需要在應用中使用多種不同的視圖技術,你可以使用ResourceBundleViewResolver :
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views"/>
<property name="defaultParentView" value="parentView"/>
</bean>
ResourceBundleViewResolver 會檢索由bundle根路徑下所配置的 ResourceBundle ,對於每個視圖而言,其視圖類由 [viewname].(class) 屬性的值指定,其視圖url由 [viewname].url 屬性的值指定。
關於註解主要的就是這些大家可以參考參考