天天看點

Spring實戰——Spring MVC的進階技術

先回顧一下Spring實戰——建構Spring Web應用程式中使用java配置建構的SpringMVC架構。以下會提供SpringMVC配置的替代方案。

一、SpringMVC配置的替代方案

注意:在Spring實戰——建構Spring Web應用程式一文中說過:使用java配置SpringMVC時隻支援Servlet3.0的容器,據我所知,目前在企業裡大部分還是使用的xml進行配置。

1、使用純xml配置SpringMVC

web.xml配置内容:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  	<!-- 設定根上下文配置檔案位置-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
	<!-- 注冊ContextLoaderListener -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
  
    <!-- 注冊DispatcherServlet -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<!-- 将DispatcherServlet映射到"/"-->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>
           

applicationContext.xml

<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="com.mfc"></context:component-scan>
           

spring-mvc.xml

<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="com.mfc.ctrl"></context:component-scan>
	
	<mvc:annotation-driven></mvc:annotation-driven>
	<mvc:default-servlet-handler/>
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
           

完成以上配置即可實作SpringMVC的測試了。

二、處理multipart形式的資料(就是檔案上傳)

參考以前的部落格:springMVC上傳圖檔

三、處理異常

1、Spring提供了多種方式将異常轉換為響應

      ①特定的Spring異常将會自動映射為指定的HTTP狀态碼

      ②異常上可以添加@responseStatus注解,進而将其映射為某一個指定的HTTP狀态碼

      ③在方法上面添加@ExceptionHandler注解,使其處理異常

2、Spring的一些異常會預設映射為HTTP狀态碼

Spring異常 HTTP狀态碼
BindException 400 - Bad Request
ConversionNotSupportedException 500 - Internal Server Error
HttpMediaTypeNotAcceptableException 406 - Not Acceptable
HttpMediaTypeNotSupportedException 415 - Unsupported Media Type
HttpMessageNotReadableException 400 - Bad Request
HttpMessageNotWritableException 500 - Internal Server Error
HttpRequestMethodNotSupportedException 405 - Method Not Allowed
MethodArgumentNotValidException 400 - Bad Request
MissingServletRequestParameterException 400 - Bad Request
MissingServletRequestPartException 400 - Bad Request
NoSuchRequestHandlingMethodException 404 - Not Found
TypeMismatchException 400 - Bad Request

3、處理異常的方式:

①可以在Controller的業務方法中處理,再Catch代碼塊中傳回到指定的錯誤頁面,如下:

@RequestMapping("/testPage")
	public String testPage(@Valid TUser tUser, 
			@RequestParam("file")MultipartFile file,
			HttpServletRequest request,
			Model model){
		String path = request.getSession().getServletContext().getRealPath("upload");
		String name = file.getOriginalFilename();
		File targetFile = new File(path, name);
		try {
			file.transferTo(targetFile);
			System.out.println("upload/"+name);
		} catch (IllegalStateException e) {
			e.printStackTrace();
			return "errorPage";
		} catch (IOException e) {
			e.printStackTrace();
			return "errorPage";
		}
		model.addAttribute("path", "/upload/"+name);
		return "testPage";
	}
           

②以上方法每個Controller中捕獲異常都需要return一次錯誤頁面,會比較麻煩,可以使用@ExceptionHandler注解,在Controller中寫一個專門處理處理異常的方法,這樣一來這個處理異常的方法就可以處理同一個控制器中所有處理器方法抛出的異常,如下(下面這段代碼可以加在每一個Controller的方法中):

/**
	 * 本方法可以處理本控制器中所有的異常
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public String errorPage(){
		return "errorPage";
	}
           

③在②中的方法雖然統一處理了目前Controller中的所有異常,但是如果在每一個Controller類中都添加一個處理異常的方法難免比較麻煩,這時我們可以将這個處理異常的方法寫在所有Controller都要繼承的父類裡面,此時這一個方法就可以處理所有Controller的異常了。還有一個更簡單的方法就是為控制器添加通知,使用@ControllerAdvice注解,如下(下面這個類可以處理所有Controller中的異常了):

/**
 * @author 74790
 * 異常通知控制器
 */
@ControllerAdvice
public class ExceptionCtrl {
	@ExceptionHandler(Exception.class)
	public String errorPage(){
		return "errorPage";
	}
}
           

四、跨重定向請求傳遞資料

1、當一個請求在Controller的方法内走完邏輯程式時,需要重定向到另一個Controller的方法,此時就需要用到重定向了,也就是redirect。問題來了,目前一個Controller重定向時,原始的請求就結束了,就會發起一個新的GET請求,此時在發起重定向的方法怎麼發送資料給重定向的目标方法呢?兩種方法:

     ①使用URL模闆以路徑變量和 / 或查詢參數的形式傳遞資料

     ②通過flash屬性發送資料

2、通過URL傳遞資料:

下面兩個方法中,testPage會向testRedirect重定向,并會帶着參數name和path傳過去。

@RequestMapping("/testPage")
	public String testPage(@Valid TUser tUser, 
			@RequestParam("file")MultipartFile file,
			HttpServletRequest request,
			Model model){
		String path = request.getSession().getServletContext().getRealPath("upload");
		String name = file.getOriginalFilename();
		File targetFile = new File(path, name);
		try {
			file.transferTo(targetFile);
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		model.addAttribute("path", "/upload/"+name);
		model.addAttribute("name", name);
		return "redirect:../testCtrl/testRedirect/{name}";
	}
	
	@RequestMapping("testRedirect/{name}")
	public String testRedirect(Model model,@PathVariable("name")String name,
			@RequestParam("path")String path){
		System.out.println(name);
		System.out.println(path);
		model.addAttribute("path", path);
		return "testPage";
	}
	
           

3、在2中的方法重定向時通過URL帶過去了兩個參數name和path,但是這種方法始終隻能傳遞一些簡答的資料。如果在testPage查出了一個對象,而在testRedirect中需要使用這個對象,這時就需要把整個對象傳遞過去(當然,你也可以傳遞查詢到這個對象的條件,然後再testRedirect中再查詢一遍,但是這樣似乎不太好),此時需要用flash,但是這裡注意,在需要重定向的方法中已經不是使用的Model了,而是使用的RedirectAttributes,RedirectAttributes提供了Model的所有功能。代碼如下:

@RequestMapping("/testPage")
	public String testPage(@Valid TUser tUser, 
			@RequestParam("file")MultipartFile file,
			HttpServletRequest request,
			RedirectAttributes model){
		String path = request.getSession().getServletContext().getRealPath("upload");
		String name = file.getOriginalFilename();
		File targetFile = new File(path, name);
		try {
			file.transferTo(targetFile);
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		model.addAttribute("path", "/upload/"+name);
		model.addAttribute("name", name);
		model.addFlashAttribute("tUser", tUser);
		return "redirect:../testCtrl/testRedirect/{name}";
	}
	
	@RequestMapping("testRedirect/{name}")
	public String testRedirect(Model model,@PathVariable("name")String name,
			@RequestParam("path")String path){
		//本方法中TUser對象已經直接存在了flash中,在testPage對應的頁面上使用EL表達式 ${tUser.userName} 就可以擷取到userName
		System.out.println(name);
		System.out.println(path);
		//判斷flash中是否有tUser這個對象
		boolean flag = model.containsAttribute("tUser");
		System.out.println(flag);
		model.addAttribute("path", path);
		return "testPage";
	}
	
           
Spring實戰——Spring MVC的進階技術