1. 程式人生 > >Spring MVC學習回顧

Spring MVC學習回顧

順序 技術 patch 這一 slow .html produces 前綴 處理請求

Spring MVC是現在新項目中使用最多的MVC框架,超越了Structs2成為MVC框架的首選。今天抽時間看了4.2.x的官網翻譯文檔及相關代碼,博客,將印象比較深的幾點記錄一下。

一、應用Spring MVC

首先引入相關依賴,以maven項目管理為例,先在pom.xml引入spring-core,spring-beans,spring-context,spring-web,spring-webmvc相關的jar包。然後再web.xml中配置前端集中控制器DispatcherServlet,在initparam中可以指定配置文件路徑contextConfigLocation,然後Web容器啟動時初始化DispatcherServlet時會加載配置文件,生成一個Ioc容器,並擁有ServletContext對象。編寫Controller控制器,通過註解標記Controller組件、請求與處理方法映射URI、請求參數、與返回值可把Java bean對象變成為Controller,實現對應請求的處理。

一般初始化DispatherServlet時生成的Ioc容器,主要是Controller對象的bean容器,但在激進型項目可直接當做整個Web應用的Ioc容器,代替傳統型項目裏配置的Ioc容器,後者通過Spring監聽器ContextLoaderLister監聽Web容器的初始化、關閉操作來觸發初始化Spring Ioc容器,一般用作Service、Dao等Bean實例容器,它可以看作前者的父Ioc容器(子Ioc容器可以使用父Ioc容器中定義的Bean,但父Ioc容器不能使用子的)。如果Ioc容器中有的bean聲明周期範圍是request或session的,可在web.xml配置請求監聽器,監聽每次請求的創建及銷毀操作。

其工作流程為:
1、用戶發送請求至前端控制器DispatcherServlet
2、DispatcherServlet收到請求調用HandlerMapping處理器映射器。
3、處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(二者組成HandlerExecutionChain),並將其一並返回給DispatcherServlet。
4、DispatcherServlet通過HandlerAdapter處理器適配器調用處理器
5、執行處理器(Controller,也叫後端控制器)。
6、Controller執行完成返回ModelAndView
7、HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet

8、DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
9、ViewReslover解析後返回具體View
10、DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中)。
11、DispatcherServlet對用戶進行響應

簡而言之,SpringMVC通過DispatcherServlet這個前端控制器(也叫中央調度器,我認為中央調度器更能體現其作用),來調用mvc的三大件:Controller、Model、View。這樣就保證MVC的每一個組件只與DispatcherServlet耦合,而彼此之間獨立運行,大大降低了程序的耦合性。

再來說一下,SpringMVC這個框架時如何實現MVC模式的。
註意SpringMVC中並沒有涉及有關於Controller接口規範的實現,SpringMVC是通過調用Handler來實現Controller這一層的。

SpringMVC使用了適配器模式,前端控制器使用HandlerAdapter來調用不同的Controller,然後才是Controller調用Model產生數據模型;
產生的數據模型將會再次返回到前端控制器,並由前端控制器決定使用不同的模板引擎將頁面進行渲染。

二、<mvc:anotation-driven/>與<mvc:default-servlet-handle>

<mvc:annotation-driven /> 會自動註冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,是spring MVC為@Controllers分發請求所必須的,即解決了@Controller註解使用的前提配置。

同時它還提供了:數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,讀寫XML的支持(JAXB,讀寫JSON的支持(Jackson)。我們處理響應ajax請求時,就使用到了對json的支持(配置之後,在加入了jackson的core和mapper包之後,不寫配置文件也能自動轉換成json)。

而且,當對action寫JUnit單元測試時,要從spring IOC容器中取DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,來完成測試,取的時候要知道正是<mvc:annotation-driven />這一句註冊的這兩個bean。

需要註意的是,在spring mvc 3.1以上,DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter對應變更為:
DefaultAnnotationHandlerMapping -> RequestMappingHandlerMapping
AnnotationMethodHandlerAdapter -> RequestMappingHandlerAdapter
AnnotationMethodHandlerExceptionResolver -> ExceptionHandlerExceptionResolver

以上都在使用了annotation-driven後自動註冊。而且對應分別提供了AbstractHandlerMethodMapping , AbstractHandlerMethodAdapter和 AbstractHandlerMethodExceptionResolver以便於讓用戶更方便的實現自定義的實現類。

菜鳥之路——Spring MVC(十二)<mvc:annotation-driven/>做了什麽

優雅REST風格的資源URL不希望帶 .html 或 .do 等後綴.由於早期的Spring MVC不能很好地處理靜態資源,所以在web.xml中配置DispatcherServlet的請求映射,往往使用 *.do 、 *.xhtml等方式。這就決定了請求URL必須是一個帶後綴的URL,而無法采用真正的REST風格的URL。

如果將DispatcherServlet請求映射配置為"/",則Spring MVC將捕獲Web容器所有的請求,包括靜態資源的請求,Spring MVC會將它們當成一個普通請求處理,因此找不到對應處理器將導致錯誤。

如何讓Spring框架能夠捕獲所有URL的請求,同時又將靜態資源的請求轉由Web容器處理,是可將DispatcherServlet的請求映射配置為"/"的前提。由於REST是Spring3.0最重要的功能之一,所以Spring團隊很看重靜態資源處理這項任務,給出了堪稱經典的兩種解決方案。
方法1.采用<mvc:default-servlet-handler />

<mvc:default-servlet-handler />
在springMVC-servlet.xml中配置<mvc:default-servlet-handler />後,會在Spring MVC上下文中定義一個org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它會像一個檢查員,對進入DispatcherServlet的URL進行篩查,如果發現是靜態資源的請求,就將該請求轉由Web應用服務器默認的Servlet處理,如果不是靜態資源的請求,才由DispatcherServlet繼續處理。

一般Web應用服務器默認的Servlet名稱是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web應用服務器的默認Servlet名稱不是"default",則需要通過default-servlet-name屬性顯示指定:

<mvc:default-servlet-handler default-servlet-name="所使用的Web服務器默認使用的Servlet名稱" />

方法2.采用<mvc:resources />

<mvc:default-servlet-handler />將靜態資源的處理經由Spring MVC框架交回Web應用服務器處理。而<mvc:resources />更進一步,由Spring MVC框架自己處理靜態資源,並添加一些有用的附加值功能。

首先,<mvc:resources />允許靜態資源放在任何地方,如WEB-INF目錄下、類路徑下等,你甚至可以將JavaScript等靜態文件打到JAR包中。通過location屬性指定靜態資源的位置,由於location屬性是Resources類型,因此可以使用諸如"classpath:"等的資源前綴指定資源位置。傳統Web容器的靜態資源只能放在Web容器的根路徑下,<mvc:resources />完全打破了這個限制。

其次,<mvc:resources />依據當前著名的Page Speed、YSlow等瀏覽器優化原則對靜態資源提供優化。你可以通過cacheSeconds屬性指定靜態資源在瀏覽器端的緩存時間,一般可將該時間設置為一年,以充分利用瀏覽器端的緩存。在輸出靜態資源時,會根據配置設置好響應報文頭的Expires 和 Cache-Control值。

在接收到靜態資源的獲取請求時,會檢查請求頭的Last-Modified值,如果靜態資源沒有發生變化,則直接返回303相應狀態碼,提示客戶端使用瀏覽器緩存的數據,而非將靜態資源的內容輸出到客戶端,以充分節省帶寬,提高程序性能。

在springMVC-servlet中添加如下配置:

<mvc:resources location="/,classpath:/META-INF/publicResources/" mapping="/resources/**"/>

http://www.cnblogs.com/dflmg/p/6393416.html

<mvc:annotation-driven/>與<mvc:default-servlet-handler/>之間的一個問題

在做項目的時候,我希望靜態資源由WEB服務器默認的Servlet來處理,所以我在配置文件中添加了如下的語句:

<mvc:default-servlet-handler/>

但是我再次運行項目,並訪問資源的時候,發現訪問@RequestMapping("/path1/path2")都不能訪問了,之前沒有添加的時候是能夠訪問的。

解決方案是,在配置文件中再添加一句代碼:

<mvc:annotation-driven/>

這樣做的原因是:

技術分享

當兩種標簽都沒有的時候,框架默認註冊的有AnnotationMethodHandlerAdapter這個bean,所以能夠處理@RequestMapping這個註解,但是只配置了<mvc:default-servlet-handler/>時所註冊的三個bean都不能處理@RequestMapping註解,因此無法找到相應的Controller,進而無法進行訪問路徑的映射,當兩種標簽都有的時候,<mvc:annotation-driven/>會註冊一個RequestMappingHandlerAdapter的bean,這個bean能夠處理@RequestMapping這個註解。

三、常用註解

1、@Controller

在SpringMVC 中,控制器Controller 負責處理由DispatcherServlet 分發的請求,它把用戶請求的數據經過業務處理層處理之後封裝成一個Model ,然後再把該Model 返回給對應的View 進行展示。在SpringMVC 中提供了一個非常簡便的定義Controller 的方法,你無需繼承特定的類或實現特定的接口,只需使用@Controller 標記一個類是Controller ,然後使用@RequestMapping 和@RequestParam 等一些註解用以定義URL 請求和Controller 方法之間的映射,這樣的Controller 就能被外界訪問到。此外Controller 不會直接依賴於HttpServletRequest 和HttpServletResponse 等HttpServlet 對象,它們可以通過Controller 的方法參數靈活的獲取到。

@Controller 用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。分發處理器將會掃描使用了該註解的類的方法,並檢測該方法是否使用了@RequestMapping 註解。@Controller 只是定義了一個控制器類,而使用@RequestMapping 註解的方法才是真正處理請求的處理器。單單使用@Controller 標記在一個類上還不能真正意義上的說它就是SpringMVC 的一個控制器類,因為這個時候Spring 還不認識它。那麽要如何做Spring 才能認識它呢?這個時候就需要我們把這個控制器類交給Spring 來管理。有兩種方式:

  (1)在SpringMVC 的配置文件中定義MyController 的bean 對象。

  (2)在SpringMVC 的配置文件中告訴Spring 該到哪裏去找標記為@Controller 的Controller 控制器。

<!--方式一-->
<bean class="com.host.app.web.controller.MyController"/>
<!--方式二-->
< context:component-scan base-package = "com.host.app.web" />//路徑寫到controller的上一層(掃描包詳解見下面淺析)

2、@RequestMapping

RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。

RequestMapping註解有六個屬性,下面我們把她分成三類進行說明(下面有相應示例)。

1、 value, method;

value: 指定請求的實際地址,指定的地址可以是URI Template 模式(後面將會說明);

method: 指定請求的method類型, GET、POST、PUT、DELETE等;

2、consumes,produces

consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;

produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;

3、params,headers

params: 指定request中必須包含某些參數值是,才讓該方法處理。

headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。

3、@Resource和@Autowired

@Resource和@Autowired都是做bean的註入時使用,其實@Resource並不是Spring的註解,它的包是javax.annotation.Resource,需要導入,但是Spring支持該註解的註入。

1、共同點

兩者都可以寫在字段和setter方法上。兩者如果都寫在字段上,那麽就不需要再寫setter方法。

2、不同點

(1)@Autowired

@Autowired為Spring提供的註解,需要導入包org.springframework.beans.factory.annotation.Autowired;只按照byType註入。

技術分享
public class TestServiceImpl {
    // 下面兩種@Autowired只要使用一種即可
    @Autowired
    private UserDao userDao; // 用於字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用於屬性的方法上
        this.userDao = userDao;
    }
}
技術分享

@Autowired註解是按照類型(byType)裝配依賴對象,默認情況下它要求依賴對象必須存在,如果允許null值,可以設置它的required屬性為false。如果我們想使用按照名稱(byName)來裝配,可以結合@Qualifier註解一起使用。如下:

public class TestServiceImpl {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao; 
}

(2)@Resource

@Resource默認按照ByName自動註入,由J2EE提供,需要導入包javax.annotation.Resource。@Resource有兩個重要的屬性:name和type,而Spring將@Resource註解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。所以,如果使用name屬性,則使用byName的自動註入策略,而使用type屬性時則使用byType自動註入策略。如果既不制定name也不制定type屬性,這時將通過反射機制使用byName自動註入策略。

技術分享
public class TestServiceImpl {
    // 下面兩種@Resource只要使用一種即可
    @Resource(name="userDao")
    private UserDao userDao; // 用於字段上
    
    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用於屬性的setter方法上
        this.userDao = userDao;
    }
}
技術分享

註:最好是將@Resource放在setter方法上,因為這樣更符合面向對象的思想,通過set、get去操作屬性,而不是直接去操作屬性。

@Resource裝配順序:

①如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常。

②如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常。

③如果指定了type,則從上下文中找到類似匹配的唯一bean進行裝配,找不到或是找到多個,都會拋出異常。

④如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配。

@Resource的作用相當於@Autowired,只不過@Autowired按照byType自動註入。

4、@ModelAttribute和 @SessionAttributes

代表的是:該Controller的所有方法在調用前,先執行此@ModelAttribute方法,可用於註解和方法參數中,可以把這個@ModelAttribute特性,應用在BaseController當中,所有的Controller繼承BaseController,即可實現在調用Controller時,先執行@ModelAttribute方法。

@SessionAttributes即將值放到session作用域中,寫在class上面。

具體示例參見下面:使用 @ModelAttribute 和 @SessionAttributes 傳遞和保存數據

5、@PathVariable

用於將請求URL中的模板變量映射到功能處理方法的參數上,即取出uri模板中的變量作為參數。如:

技術分享
@Controller  
public class TestController {  
     @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  
     public String getLogin(@PathVariable("userId") String userId,  
         @PathVariable("roleId") String roleId){  
         System.out.println("User Id : " + userId);  
         System.out.println("Role Id : " + roleId);  
         return "hello";  
     }  
     @RequestMapping(value="/product/{productId}",method = RequestMethod.GET)  
     public String getProduct(@PathVariable("productId") String productId){  
           System.out.println("Product Id : " + productId);  
           return "hello";  
     }  
     @RequestMapping(value="/javabeat/{regexp1:[a-z-]+}",  
           method = RequestMethod.GET)  
     public String getRegExp(@PathVariable("regexp1") String regexp1){  
           System.out.println("URI Part 1 : " + regexp1);  
           return "hello";  
     }  
}
技術分享

6、@requestParam

@requestParam主要用於在SpringMVC後臺控制層獲取參數,類似一種是request.getParameter("name"),它有三個常用參數:defaultValue = "0", required = false, value = "isApp";defaultValue 表示設置默認值,required 銅過boolean設置是否是必須要傳入的參數,value 值表示接受的傳入的參數類型。

7、@ResponseBody

作用: 該註解用於將Controller的方法返回的對象,通過適當的HttpMessageConverter轉換為指定格式後,寫入到Response對象的body數據區。

使用時機:返回的數據不是html標簽的頁面,而是其他某種格式的數據時(如json、xml等)使用;

8、@Component

相當於通用的註解,當不知道一些類歸到哪個層時使用,但是不建議。

9、@Repository

用於註解dao層,在daoImpl類上面註解。

Spring4引入@RestController註解,相當於@Controller和@ResponseBody註解,以更方面的形式實現Restful風格的接口。

Spring MVC學習回顧