SpringMVC 知識點整理

圖1

圖2

先放兩張圖,是網上找的。下面的文章很多都是其他地方直接copy過來的,尤其是這位仁兄(http://elf8848.iteye.com/blog/875830/),在此感謝,爲了加深記憶。

1.DispatcherServlet

使用Spring MVC,配置DispatcherServlet是第一步。
DispatcherServlet是一個Servlet,所以可以配置多個DispatcherServlet。
DispatcherServlet是前置控制器,配置在web.xml文件中的。攔截匹配的請求,Servlet攔截匹配規則要自已定義,把攔截下來的請求,依據某某規則分發到目標Controller(我們寫的Action)來處理。

<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>*.form</url-pattern>  
    </servlet-mapping>  
</web-app>  

example這個Servlet的名字是example,可以有多個DispatcherServlet,是通過名字來區分的。每一個DispatcherServlet有自己的WebApplicationContext上下文對象。

在DispatcherServlet的初始化過程中,框架會在web應用的 WEB-INF文件夾下尋找名爲[servlet-name]-servlet.xml 的配置文件,生成文件中定義的bean。

第二種寫法:

<servlet>  
    <servlet-name>springMVC</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <init-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath*:/springMVC.xml</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
<servlet-mapping>  
    <servlet-name>springMVC</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  

classpath*:/springMVC.xml 這段寫不寫有四種方式:
1、不寫,使用默認值:/WEB-INF/-servlet.xml
2、/WEB-INF/classes/springMVC.xml
3、classpath*:springMVC-mvc.xml
4、多個值用逗號分隔

Servlet攔截匹配規則可以自已定義,攔截哪種URL合適?

@RequestMapping的用法如下:

當映射爲@RequestMapping(“/user/add”)時,爲例:

例子一:

@RequestMapping(value="/departments")  
public String simplePattern(){  

  System.out.println("simplePattern method was called");  
  return "someResult";  

}  

當訪問//localhost/xxx/departments時,會用到。

例子二:

@RequestMapping(value="/departments")  
public String findDepatment(  
  @RequestParam("departmentId") String departmentId){  

    System.out.println("Find department with ID: " + departmentId);  
    return "someResult";  

}  

當訪問//localhost/xxx/departments?departmentId=00時用到

例子三:

@RequestMapping(value="/departments/{departmentId}")  
public String findDepatment(@PathVariable String departmentId){  

  System.out.println("Find department with ID: " + departmentId);  
  return "someResult";  

}  

當訪問//localhost/xxx/departments/23時

例子四:

@Controller  
@RequestMapping ( "/test/{variable1}" )  
public class MyController {  

    @RequestMapping ( "/departments/{variable2}" )  
    public ModelAndView showView( @PathVariable String variable1, @PathVariable ( "variable2" ) int variable2) {  
       ModelAndView modelAndView = new ModelAndView();  
       modelAndView.setViewName( "viewName" );  
       modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " );  
       return modelAndView;  
    }  
}   

當訪問//localhost/xxx/test/33/departments/23時

2.WebApplicationContext上下文

Spring會創建一個WebApplicationContext上下文,稱爲父上下文(父容器) ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。
可以使用Spring提供的工具類取出上下文對象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

DispatcherServlet是一個Servlet,可以同時配置多個,每個 DispatcherServlet有一個自己的上下文對象(WebApplicationContext),稱爲子上下文(子容器),子上下文可以訪問父上下文中的內容,但父上下文不能訪問子上下文中的內容。 它也保存在 ServletContext中,key是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名稱。當一個Request對象產生時,會把這個子上下文對象(WebApplicationContext)保存在Request對象中,key是DispatcherServlet.class.getName() + “.CONTEXT”。
可以使用工具類取出上下文對象:RequestContextUtils.getWebApplicationContext(request);

說明 :Spring 並沒有限制我們,必須使用父子上下文。我們可以自己決定如何使用。

方案一,傳統型:
父上下文容器中保存數據源、服務層、DAO層、事務的Bean。
子上下文容器中保存Mvc相關的Action的Bean.
事務控制在服務層。
由於父上下文容器不能訪問子上下文容器中內容,事務的Bean在父上下文容器中,無法訪問子上下文容器中內容,就無法對子上下文容器中Action進行AOP(事務)。
當然,做爲“傳統型”方案,也沒有必要這要做。

方案二,激進型:
Java世界的“面向接口編程”的思想是正確的,但在增刪改查爲主業務的系統裏,Dao層接口,Dao層實現類,Service層接口,Service層實現類,Action父類,Action。再加上衆多的O(vo\po\bo)和jsp頁面。寫一個小功能 7、8個類就寫出來了。 開發者說我就是想接點私活兒,和PHP,ASP搶搶飯碗,但我又是Java程序員。最好的結果是大項目能做好,小項目能做快。所以“激進型”方案就出現了—–沒有接口、沒有Service層、還可以沒有衆多的O(vo\po\bo)。那沒有Service層事務控制在哪一層?只好上升的Action層。
本文不想說這是不是正確的思想,我想說的是Spring不會限制你這樣做。
由於有了父子上下文,你將無法實現這一目標。解決方案是隻使用子上下文容器,不要父上下文容器 。所以數據源、服務層、DAO層、事務的Bean、Action的Bean都放在子上下文容器中。就可以實現了,事務(註解事務)就正常工作了。這樣纔夠激進。
總結:不使用listener監聽器來加載spring的配置文件,只使用DispatcherServlet來加載spring的配置,不要父子上下文,只使用一個DispatcherServlet,事情就簡單了,什麼麻煩事兒也沒有了。*

3. 一個典型的servlet.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:tx="http://www.springframework.org/schema/tx"  
    xmlns:context="http://www.springframework.org/schema/context"    
    xmlns:mvc="http://www.springframework.org/schema/mvc"    
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
    http://www.springframework.org/schema/tx   
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd  
    http://www.springframework.org/schema/mvc  
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">  


    <!-- 自動掃描的包名 包括如下常見註解
    @Controller 聲明Action組件
    @Service    聲明Service組件    @Service("myMovieLister") 
    @Repository 聲明Dao組件
    @Component   泛指組件, 當不好歸類時. 
    @RequestMapping("/menu")  請求映射
    @Resource  用於注入,( j2ee提供的 ) 默認按名稱裝配,@Resource(name="beanName") 
    @Autowired 用於注入,(srping提供的) 默認按類型裝配 
    @Transactional( rollbackFor={Exception.class}) 事務管理
    @ResponseBody
    @Scope("prototype")   
    設定bean的作用域-->  

    <context:component-scan base-package="com.app,com.core,JUnit4" ></context:component-scan>  

    <!-- 默認的註解映射的支持 會自動註冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,是spring MVC爲@Controllers分發請求所必須的。
並提供了:數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,讀寫XML的支持(JAXB),讀寫JSON的支持(Jackson)。-->  
    <mvc:annotation-driven />  

    <!-- 視圖解釋類 -->  
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
        <property name="prefix" value="/WEB-INF/jsp/"/>  
        <property name="suffix" value=".jsp"/><!--可爲空,方便實現自已的依據擴展名來選擇視圖解釋類的邏輯  -->  
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />  
    </bean>  

    <!-- 攔截器,可以有多個攔截器 -->  
    <mvc:interceptors>  
        <bean class="com.core.mvc.MyInteceptor" />  
    </mvc:interceptors>       

    <!-- 對靜態資源文件的訪問  方案一 (二選一) -->  
    <mvc:default-servlet-handler/>  

    <!-- 對靜態資源文件的訪問  方案二 (二選一)-->  
    <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>   
    <!-- 匹配URL  /images/**  的URL被當做靜態資源,由Spring讀出到內存中再響應http。-->  
    <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>  
    <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>  

</beans>   

4. 如何讓靜態資源請求不被攔截

如果你的DispatcherServlet攔截”/”,爲了實現REST風格,攔截了所有的請求,那麼同時對.js,.jpg等靜態文件的訪問也就被攔截了。

方案一:
解決的辦法是在web.xml中加入

<servlet-mapping>   
    <servlet-name>default</servlet-name>  
    <url-pattern>*.jpg</url-pattern>     
</servlet-mapping>    
<servlet-mapping>       
    <servlet-name>default</servlet-name>    
    <url-pattern>*.js</url-pattern>    
</servlet-mapping>    
<servlet-mapping>        
    <servlet-name>default</servlet-name>       
    <url-pattern>*.css</url-pattern>      
</servlet-mapping>    

在dispatchServlet前面,讓tomcat自行去先過濾掉。
Tomcat, Jetty, JBoss, and GlassFish 自帶的默認Servlet的名字 – “default”
Google App Engine 自帶的 默認Servlet的名字 – “_ah_default”
Resin 自帶的 默認Servlet的名字 – “resin-file”
WebLogic 自帶的 默認Servlet的名字 – “FileServlet”
WebSphere 自帶的 默認Servlet的名字 – “SimpleFileServlet”

方案二: 在spring3.0.4以後版本提供了mvc:resources
方案三 ,使用 <mvc:default-servlet-handler/> 會把”/**” url,註冊到SimpleUrlHandlerMapping的urlMap中,把對靜態資源的訪問由HandlerMapping轉到org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler處理並返回.
DefaultServletHttpRequestHandler使用就是各個Servlet容器自己的默認Servlet.

方案2的優先級要比方案3來的高,所以一個請求如果是請求靜態的資源,是按優先級來的。

OK.看完了servlet.xml,我們來看下Controller這個類

5.Controller如何定義及含義

Controller 負責處理由DispatcherServlet 分發的請求,當一個請求到來時,spring怎麼知道該用哪個函數去處理呢?

@Controller  
public class MyController {  

    @RequestMapping ( "/showView" )  
    public ModelAndView showView() {  
       ModelAndView modelAndView = new ModelAndView();  
       modelAndView.setViewName( "viewName" );  
       modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " );  
       return modelAndView;  
    }  

}   

如上就是一個典型的Controller定義,spring會掃描加了@Controller前綴的類,並且,如果他的函數中有@RequestMapping字樣,則會認爲這是個處理類。
那麼如何讓spring去做這個動作?兩個辦法,一個是用之前說的context:component-scan標籤。
第二種就是顯式定義

<bean class="com.host.app.web.controller.MyController"/>

6. Spring中的攔截器
Spring爲我們提供了:
org.springframework.web.servlet.HandlerInterceptor接口,
org.springframework.web.servlet.handler.HandlerInterceptorAdapter適配器,
實現這個接口或繼承此類,可以非常方便的實現自己的攔截器。

有以下三個方法:

Action之前執行:
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler);

生成視圖之前執行
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView);

最後執行,可用於釋放資源
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)

分別實現預處理、後處理(調用了Service並返回ModelAndView,但未進行頁面渲染)、返回處理(已經渲染了頁面)
在preHandle中,可以進行編碼、安全控制等處理;
在postHandle中,有機會修改ModelAndView;
在afterCompletion中,可以根據ex是否爲null判斷是否發生了異常,進行日誌記錄。
參數中的Object handler是下一個攔截器。

攔截器的定義有幾種方式:
第一種,什麼都欄,

 <mvc:interceptors>  
    <bean class="com.app.mvc.MyInteceptor" />  
</mvc:interceptors>  

第二種,攔一部分url匹配的

<mvc:interceptors >    
  <mvc:interceptor>    
        <mvc:mapping path="/user/*" /> <!-- /user/*  -->    
        <bean class="com.mvc.MyInteceptor"></bean>    
    </mvc:interceptor>    
</mvc:interceptors>   

第三種,指定我們自己的攔截器,前提是沒有事先使用了< mvc:annotation-driven />

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">       
 <property name="interceptors">       
     <list>       
         <bean class="com.mvc.MyInteceptor"></bean>      
     </list>       
 </property>       
</bean>   

7,異常處理

<!-- 總錯誤處理-->  
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
    <property name="defaultErrorView">    
        <value>/error/error</value>  
    </property>  
    <property name="defaultStatusCode">    
        <value>500</value>  
    </property>     
<property name="warnLogCategory">    
        <value>org.springframework.web.servlet.handler.SimpleMappingExceptionResolver</value>  
    </property>     
</bean>  

這裏主要的類是SimpleMappingExceptionResolver類,和他的父類AbstractHandlerExceptionResolver類。
具體可以配置哪些屬性,我是通過查看源碼知道的。
你也可以實現HandlerExceptionResolver接口,寫一個自己的異常處理程序。spring的擴展性是很好的。

通過SimpleMappingExceptionResolver我們可以將不同的異常映射到不同的jsp頁面(通過exceptionMappings屬性的配置)。

同時我們也可以爲所有的異常指定一個默認的異常提示頁面(通過defaultErrorView屬性的配置),如果所拋出的異常在exceptionMappings中沒有對應的映射,則Spring將用此默認配置顯示異常信息。
注意這裏配置的異常顯示界面均僅包括主文件名,至於文件路徑和後綴已經在viewResolver中指定。如/error/error表示/error/error.jsp

8.如何把全局異常記錄到日誌中

在前的配置中,其中有一個屬性warnLogCategory,值是“SimpleMappingExceptionResolver類的全限定名”。我是在SimpleMappingExceptionResolver類父類AbstractHandlerExceptionResolver類中找到這個屬性的。查看源碼後得知:如果warnLogCategory不爲空,spring就會使用apache的org.apache.commons.logging.Log日誌工具,記錄這個異常,級別是warn。
值:“org.springframework.web.servlet.handler.SimpleMappingExceptionResolver”,是“SimpleMappingExceptionResolver類的全限定名”。這個值不是隨便寫的。 因爲我在log4j的配置文件中還要加入log4j.logger.org.springframework.web.servlet.handler.SimpleMappingExceptionResolver=WARN,保證這個級別是warn的日誌一定會被記錄,即使log4j的根日誌級別是ERROR。

9.轉發與重定向

可以通過redirect/forward:url方式轉到另一個Action進行連續的處理。
可以通過redirect:url 防止表單重複提交 。
寫法如下:
return "forward:/order/add";
return "redirect:/index.jsp";

10.處理ajax請求

1、引入下面兩個jar包,我用的是1.7.2,好像1.4.2版本以上都可以,下載地址: http://wiki.fasterxml.com/JacksonDownload
jackson-core-asl-1.7.2.jar
jackson-mapper-asl-1.7.2.jar

2、spring的配置文件中要有這一行,才能使用到spring內置支持的json轉換。如果你手工把POJO轉成json就可以不須要使用spring內置支持的json轉換。

3、使用@ResponseBody註解, 表示該方法的返回結果直接寫入HTTP response body中

/** 
 * ajax測試 
* http://127.0.0.1/mvc/order/ajax 
 */  

@RequestMapping("/ajax")  
@ResponseBody  
public Object ajax(HttpServletRequest request){  
    List<String> list=new ArrayList<String>();  
    list.add("電視");  
nbsp;       list.add("洗衣機");  
    list.add("冰箱");  
    list.add("電腦");  
    list.add("汽車");  
    list.add("空調");  
    list.add("自行車");  
    list.add("飲水機");  
    list.add("熱水器");  
    return list;  
}  

11.可用的ViewResolver實現類(留待補充)

12.ModelAndView詳解
ModelAndView, 顧名思義,代表了MVC Web程序中Model與View的對象,不過它只是方便一次返回這兩個對象的holder,Model與View兩者仍是分離的概念。

最簡單的ModelAndView是持有View的名稱返回,之後View名稱被view resolver,也就是實作org.springframework.web.servlet.View接口的實例解析,例如 InternalResourceView或JstlView等等:
ModelAndView(String viewName)

 如果要返回Model對象,則可以使用Map來收集這些Model對象,然後設定給ModelAndView,使用下面這個版本的ModelAndView:
ModelAndView(String viewName, Map model)

 Map對象中設定好key與value值,之後可以在視圖中取出,如果只是要返回一個Model對象,則可以使用下面這個ModelAndView版本:
ModelAndView(String viewName, String modelName, Object modelObject)

 藉由modelName,可以在視圖中取出Model並顯示。
 ModelAndView類別提供實作View接口的對象來作View的參數:
ModelAndView(View view)
ModelAndView(View view, Map model)
ModelAndView(View view, String modelName, Object modelObject)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章