Spring及Spring MVC總結

一、Spring概述

  Spring是一個分層的Java SE/EE應用一站式的輕量級開源框架,其從持久層、業務層到表現層都擁有相應的支持,幾乎爲企業應用提供了所需的一切。Spring主要用來開發Java應用,但是有些擴展是針對構建J2EE平臺的web應用。Spring 框架目標是簡化Java企業級應用開發,並通過POJO爲基礎的編程模型促進良好的編程習慣。它的核心是IOC和AOP,如下圖所示,整個Spring框架按其所屬功能可以劃分爲五個主要模塊,這五個模塊幾乎爲企業應用提供了所需的一切,從持久層、業務層到表現層都擁有相應的支持,這就是爲什麼稱Spring是一站式框架的原因。

Spring體系結構.png-50.8kB

1、核心模塊(Core Container)

  Spring的核心模塊實現了IoC的功能,它將類和類之間的依賴從代碼中脫離出來,用配置的方式進行依賴關係描述。由IoC容器負責類的創建、管理、獲取等。BeanFactory接口是Spring框架的核心接口,實現了容器很多核心的功能。

  Context模塊構建於核心模塊之上,擴展了BeanFactory的功能,包括國際化,資源加載,郵件服務,任務調度等多項功能。ApplicationContext是Context模塊的核心接口。

  表達式語言(Expression Language)是統一表達式語言(EL)的一個擴展,支持設置和獲取對象屬性,調用對象方法,操作數組、集合等。使用它可以很方便的通過表達式和Spring IoC容器進行交互。

2、AOP模塊

  Spring AOP模塊提供了滿足AOP Alliance規範的實現,還整合了AspectJ這種AOP語言級的框架。通過AOP能降低耦合。AOP模塊用於給Spring應用做面向切面的開發, 很多支持由AOP聯盟提供,這樣就確保了Spring和其他AOP框架的共通性。這個模塊將元數據編程引入Spring。

3、數據訪問集成模塊(Data Access/Integration)

  該模塊包括了JDBC、ORM、OXM、JMS和事務管理。

  事務模塊:該模塊用於Spring管理事務,只要是Spring管理對象都能得到Spring管理事務的好處,無需在代碼中進行事務控制了,而且支持編程和聲明性的事務管理。

  JDBC模塊:提供了一個JBDC的樣例模板,使用這些模板能消除傳統冗長的JDBC編碼還有必須的事務控制,而且能享受到Spring管理事務的好處。

  ORM模塊:提供與流行的“對象-關係”映射框架的無縫集成,包括hibernate、JPA、MyBatis等。而且可以使用Spring事務管理,無需額外控制事務。

  OXM模塊:提供了一個對Object/XML映射實現,將Java對象映射成XML數據,或者將XML數據映射成java對象,Object/XML映射實現包括JAXB、Castor、XMLBeans和XStream。

  JMS模塊:用於JMS(Java Messaging Service),提供一套 “消息生產者、消息消費者”模板用於更加簡單的使用JMS,JMS用於在兩個應用程序之間,或分佈式系統中發送消息,進行異步通信。

4、Web模塊

  該模塊建立在ApplicationContext模塊之上,提供了Web應用的功能,如文件上傳、FreeMarker等。Spring可以整合Struts2等MVC框架。此外,Spring自己提供了MVC框架Spring MVC

5、測試模塊

  Spring可以用非容器依賴的編程方式進行幾乎所有的測試工作,支持JUnit和TestNG等測試框架。

二、Spring框架問答

1、解釋JDBC抽象和DAO模塊

  通過使用JDBC抽象和DAO模塊,保證數據庫代碼的簡潔,並能避免數據庫資源錯誤關閉導致的問題,它在各種不同的數據庫的錯誤信息之上,提供了一個統一的異常訪問層。它還利用Spring的AOP模塊給Spring應用中的對象提供事務管理服務。

2、 解釋對象/關係映射集成模塊

  Spring 通過提供ORM模塊,支持我們在直接JDBC之上使用一個對象/關係映射映射(ORM)工具,Spring支持集成主流的ORM框架,如Hiberate、JDO和iBATIS SQL Maps。Spring的事務管理同樣支持以上所有ORM框架及JDBC。

3、 解釋Web模塊

  Spring的Web模塊是構建在application context 模塊基礎之上的,它提供了一個適合web應用的上下文。這個模塊也包括支持多種面向web的任務,如透明地處理多個文件上傳請求和程序級請求參數的綁定到你的業務對象。它也有對Jakarta Struts的支持。

4、ApplicationContext通常的實現是什麼

  FileSystemXmlApplicationContext :此容器從一個XML文件中加載beans的定義,XML Bean配置文件的全路徑名必須提供給它的構造函數
  ClassPathXmlApplicationContext:此容器也從一個XML文件中加載beans的定義,這裏,你需要正確設置classpath因爲這個容器將在classpath裏找bean配置
  WebXmlApplicationContext:此容器加載一個XML文件,此文件定義了一個WEB應用的所有bean

5、Bean工廠和Application contexts 有什麼區別

  Application contexts提供一種方法處理文本消息,一個通常的做法是加載文件資源(比如鏡像),它們可以向註冊爲監聽器的bean發佈事件。另外,在容器或容器內的對象上執行的那些不得不由bean工廠以程序化方式處理的操作,可以在Application contexts中以聲明的方式處理。Application contexts實現了MessageSource接口,該接口的實現以可插拔的方式提供獲取本地化消息的方法。

6、Spring IoC容器依賴的含義

  Spring IoC容器的依賴有兩層含義:Bean依賴容器和容器注入Bean的依賴資源
  Bean依賴容器:也就是說Bean要依賴於容器,這裏的依賴是指容器負責創建Bean並管理Bean的生命週期,正是由於由容器來控制創建Bean並注入依賴,也就是控制權被反轉了,這也正是IoC名字的由來,此處的有依賴是指Bean和容器之間的依賴關係。
  容器注入Bean的依賴資源:容器負責注入Bean的依賴資源,依賴資源可以是Bean、外部文件、常量數據等,在Java中都反映爲對象,並且由容器負責組裝Bean之間的依賴關係,此處的依賴是指Bean之間的依賴關係,可以認爲是傳統類與類之間的“關聯”、“聚合”、“組合”關係。

7、有哪些不同類型的依賴注入方式

  構造器依賴注入:構造器依賴注入通過容器觸發一個類的構造器來實現的,該類有一系列參數,每個參數代表一個對其他類的依賴。

  Setter方法注入:Setter方法注入是容器通過調用無參構造器或無參static工廠 方法實例化bean之後,調用該bean的setter方法,即實現了基於setter的依賴注入。

8、哪種依賴注入方式更建議使用,構造器注入,還是 Setter方法注入

  兩種依賴方式都可以使用,構造器注入和Setter方法注入。最好的解決方案是用構造器參數實現強制依賴,setter方法實現可選依賴。

9、什麼是Spring beans

  Spring beans是那些形成Spring應用的主幹的Java對象。它們被Spring IOC容器初始化、裝配和管理。這些beans通過容器中配置的元數據創建。比如,以XML文件中<bean/> 的形式定義。

  Spring框架定義的beans都是單例beans。在bean tag中有個屬性”singleton”,如果它被賦爲TRUE,bean 就是單例,否則就是一個 prototype bean。默認是TRUE,所以所有在Spring框架中的beans 缺省都是單例。

10、如何給Spring 容器提供配置元數據

  這裏有三種重要的方法給Spring 容器提供配置元數據:
  1、XML配置文件
  2、基於註解的配置
  3、基於java的配置

11、怎樣定義類的作用域

  當使用<bean> 標籤在Spring裏定義一個bean時,我們還能給這個bean聲明一個作用域。它可以通過bean定義中的scope屬性來定義。如,當Spring要在需要的時候每次都生產一個新的bean實例,bean的scope屬性被指定爲prototype。另一方面,如果一個bean每次使用的時候必須返回同一個實例,這個bean的scope屬性必須設爲 singleton。

12、 解釋Spring支持的幾種bean的作用域

  singleton : bean在每個Spring ioc 容器中只有一個實例。

  prototype:一個bean的定義可以有多個實例。

  request:每次http請求都會創建一個bean,該作用域僅在基於web的Spring ApplicationContext情形下有效。

  session:在一個HTTP Session中,一個bean定義對應一個實例。該作用域僅在基於web的Spring ApplicationContext情形下有效。

  global-session:在一個全局的HTTP Session中,一個bean定義對應一個實例。該作用域僅在基於web的Spring ApplicationContext情形下有效。

  缺省的Spring bean的作用域是Singleton。

13、 重要的bean生命週期方法有哪些,它們能重載嗎?

  有兩個重要的bean 生命週期方法,第一個是setup , 它是在容器加載bean的時候被調用。第二個方法是teardown 它是在容器卸載類的時候被調用。

  <bean>標籤有兩個重要的屬性(init-method和destroy-method)。用它們你可以自己定製初始化方法和註銷方法,它們也有相應的註解(@PostConstruct和@PreDestroy)。

14、 什麼是Spring的內部bean

  當一個bean僅被用作另一個bean的屬性時,它能被聲明爲一個內部bean,爲了定義inner bean,在Spring 的基於XML的 配置元數據中,可以在<property/>或<constructor-arg/> 元素內使用<bean/> 元素,內部bean通常是匿名的,它們的Scope一般是prototype。

15、 在 Spring中如何注入一個java集合

  Spring提供以下幾種集合的配置元素:

  <list>類型用於注入一列值,允許有相同的值。

  <set> 類型用於注入一組值,不允許有相同的值。

  <map> 類型用於注入一組鍵值對,鍵和值都可以爲任意類型。

  <props>類型用於注入一組鍵值對,鍵和值都只能爲String類型。

16、 什麼是bean裝配和自動裝配

  裝配或bean裝配是指在Spring 容器中把bean組裝到一起,前提是容器需要知道bean的依賴關係,如何通過依賴注入來把它們裝配到一起。

  Spring容器能夠自動裝配相互合作的bean,這意味着容器不需要<constructor-arg>和<property>配置,能通過Bean工廠自動處理bean之間的協作。

17、解釋不同方式的自動裝配

  有五種自動裝配的方式,可以用來指導Spring容器用自動裝配方式來進行依賴注入。

  no:默認的方式是不進行自動裝配,通過顯式設置ref屬性來進行裝配。

  byName:通過參數名自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,之後容器試圖匹配、裝配和該bean的屬性具有相同名字的bean。

  byType:通過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,之後容器試圖匹配、裝配和該bean的屬性具有相同類型的bean。如果有多個bean符合條件,則拋出錯誤。

  constructor:這個方式類似於byType, 但是要提供給構造器參數,如果沒有確定的帶參數的構造器參數類型,將會拋出異常。

  autodetect:首先嚐試使用constructor來自動裝配,如果無法工作,則使用byType方式。

18、自動裝配有哪些侷限性

  自動裝配的侷限性是:

  重寫: 你仍需用<constructor-arg>和 <property> 配置來定義依賴,意味着總要重寫自動裝配。

  基本數據類型:你不能自動裝配簡單的屬性,如基本數據類型,String字符串,和類。

  模糊特性:自動裝配不如顯式裝配精確,如果有可能,建議使用顯式裝配。

19、什麼是基於Java的Spring註解配置

  基於Java的配置,允許你在少量的Java註解的幫助下,進行你的大部分Spring配置而非通過XML文件。

  以@Configuration 註解爲例,它用來標記類可以當做一個bean的定義,被Spring IOC容器使用。另一個例子是@Bean註解,它表示此方法將要返回一個對象,作爲一個bean註冊進Spring應用上下文。

20、怎樣開啓註解裝配

  註解裝配在默認情況下是不開啓的,爲了使用註解裝配,我們必須在Spring配置文件中配置<context:annotation-config/>元素。

21、@Required註解

  這個註解表明bean的屬性必須在配置的時候設置,通過一個bean定義的顯式的屬性值或通過自動裝配,若@Required註解的bean屬性未被設置,容器將拋出BeanInitializationException。

22、@Autowired 註解

  @Autowired 註解提供了更細粒度的控制,包括在何處以及如何完成自動裝配。它的用法和@Required一樣,修飾setter方法、構造器、屬性或者具有任意名稱和/或多個參數的PN方法。

23、在Spring框架中如何更有效地使用JDBC

  使用Spring JDBC框架,資源管理和錯誤處理的代價都會被減輕。所以開發者只需寫statements和queries從數據庫存取數據,JDBC也可以在Spring框架提供的模板類的幫助下更有效地被使用,這個模板叫JdbcTemplate。

  JdbcTemplate類提供了很多便利的方法解決諸如把數據庫數據轉變成基本數據類型或對象,執行寫好的或可調用的數據庫操作語句,提供自定義的數據錯誤處理。

24、Spring對DAO的支持

  Spring對數據訪問對象(DAO)的支持旨在簡化它和數據訪問技術如JDBC,Hibernate or JDO的結合使用。這使我們可以方便地切換持久層。編碼時也不用擔心會捕獲每種技術特有的異常。

  

25、Spring支持的事務管理類型

  Spring支持兩種類型的事務管理:

  編程式事務管理:這意味你通過編程的方式管理事務,給你帶來極大的靈活性,但是難維護。

  聲明式事務管理:這意味着你可以將業務代碼和事務管理分離,你只需用註解和XML配置來管理事務。

26、實踐中更傾向用那種事務管理類型

  大多數使用Spring框架的用戶選擇聲明式事務管理,因爲它對應用代碼的影響最小,因此更符合一個無侵入的輕量級容器的思想。聲明式事務管理要優於編程式事務管理,雖然比編程式事務管理(這種方式允許你通過代碼控制事務)少了一點靈活性。

27、什麼是Spring的MVC框架

  Spring配備構建Web應用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring的MVC框架用控制反轉把業務對象和控制邏輯清晰地隔離。它也允許以聲明的方式把請求參數和業務對象綁定。

  Spring的MVC框架是圍繞DispatcherServlet來設計的,它用來處理所有的HTTP請求和響應。

  WebApplicationContext繼承了ApplicationContext 並增加了一些WEB應用必備的特有功能,它不同於一般的ApplicationContext ,因爲它能處理主題,並找到被關聯的servlet。

28、什麼是Spring MVC框架的控制器

  控制器提供一個訪問應用程序的行爲,此行爲通常通過服務接口實現。控制器解析用戶輸入並將其轉換爲一個由視圖呈現給用戶的模型。Spring用一個非常抽象的方式實現了一個控制層,允許用戶創建多種用途的控制器。

三、Spring與Spring MVC的關係及配置

  在Spring整體框架的核心概念中,容器的核心思想是管理Bean的整個生命週期。但在一個項目中,Spring容器往往不止一個,最常見的場景就是在一個項目中引入Spring和Spring MVC這兩個框架,其本質就是兩個容器:Spring是根容器,Spring MVC是其子容器。

  Spring和Spring MVC作爲Bean管理容器和MVC層的默認框架,已被衆多web應用採用。但是在實際應用中,初級開發者常常會因對Spring和Spring MVC的配置失當導致一些奇怪的異常現象,比如Controller的方法無法攔截、Bean被多次加載等問題,這種情況發生的根本原因在於開發者對Spring容器和Spring MVC容器之間的關係瞭解不夠深入。

1. Spring容器、SpringMVC容器與ServletContext之間的關係

  在Web容器中配置Spring時,你可能已經司空見慣於web.xml文件中的以下配置代碼,下面我們以該代碼片段爲基礎來了解Spring容器、Spring MVC容器與ServletContext之間的關係。要想理解這三者的關係,需要先熟悉Spring是怎樣在web容器(Tomcat和Jetty等)中啓動起來的。Spring的啓動過程其實就是其Spring IOC容器的啓動過程。特別地,對於web程序而言,IOC容器啓動過程即是建立上下文的過程。

<web-app>
    ...
    <!-- 利用Spring提供的ContextLoaderListener監聽器去監聽ServletContext對象的創建,並初始化WebApplicationContext對象 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Context Configuration locations for Spring XML files(默認查找/WEB-INF/applicationContext.xml) -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- 配置Spring MVC的前端控制器:DispatchcerServlet -->
    <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>
    ...
</web-app>

Spring的啓動過程

  (1). 對於一個web應用,其部署在web容器中,web容器提供給它一個全局的上下文環境,這個上下文就是ServletContext,其爲後面的Spring IOC容器提供了宿主環境

   (2). 在web.xml中會提供有contextLoaderListener。在web容器啓動時,會觸發容器初始化事件,此時contextLoaderListener會監聽到這個事件,其contextInitialized方法會被調用。在這個方法中,Spring會初始化一個啓動上下文,這個上下文被稱爲根上下文,即WebApplicationContext。WebApplicationContext是一個接口,確切的說,其實際的實現類是XmlWebApplicationContext,它就是Spring的IOC容器,其對應的Bean定義的配置由web.xml中的<context-param>標籤指定。在這個IoC容器初始化完畢後,Spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE爲屬性Key,將其存儲到ServletContext中,便於獲取;

  (3). ContextLoaderListener監聽器初始化完畢後,開始初始化web.xml中配置的Servlet,這個Servlet可以配置多個,以最常見的DispatcherServlet爲例,這個Servlet實際上是一個標準的前端控制器,用以轉發、匹配、處理每個Servlet請求。DispatcherServlet上下文在初始化的時候會建立自己的IoC上下文,用以持有Spring MVC相關的bean。特別地,在建立DispatcherServlet自己的IoC上下文前,會利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先從ServletContext中獲取之前的根上下文(即WebApplicationContext)作爲自己上下文的parent上下文。有了這個parent上下文之後,再初始化自己持有的上下文。這個DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化處理器映射、視圖解析等。這個servlet自己持有的上下文默認實現類也是mlWebApplicationContext。初始化完畢後,Spring以與Servlet的名字相關(此處不是簡單的以servlet名爲Key,而是通過一些轉換,具體可自行查看源碼)的屬性爲屬性Key,也將其存到ServletContext中,以便後續使用。這樣每個Servlet就持有自己的上下文,即擁有自己獨立的bean空間,同時各個servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定義的那些bean。

Spring容器與Spring MVC容器的聯繫與區別

  在ContextLoaderListener中創建的Spring容器主要用於整個Web應用程序需要共享的一些組件,比如DAO、數據庫的ConnectionFactory等;而由DispatcherServlet創建的Spring MVC容器主要用於和該Servlet相關的一些組件,比如Controller、ViewResovler等。它們之間的關係如下:

  (1). 作用範圍:子容器(Spring MVC容器)可以訪問父容器(Spring容器)的Bean,父容器(Spring容器)不能訪問子容器(SpringMVC容器)的Bean。也就是說,當在Spring MVC容器中getBean時,如果在自己的容器中找不到對應的bean,則會去父容器中去找,這也解釋了爲什麼由Spring MVC容器創建的Controller可以獲取到Spring容器創建的Service組件的原因

  (2). 具體實現:在Spring的具體實現上,子容器和父容器都是通過ServletContext的setAttribute方法放到ServletContext中的。但是,ContextLoaderListener會先於DispatcherServlet創建ApplicationContext,DispatcherServlet在創建ApplicationContext時會先找到由ContextLoaderListener所創建的ApplicationContext,再將後者的ApplicationContext作爲參數傳給DispatcherServlet的ApplicationContext的setParent()方法。也就是說,子容器的創建依賴於父容器的創建,父容器先於子容器創建。在Spring源代碼中,你可以在FrameServlet.Java中找到如下代碼:

wac.setParent(parent);

  其中,wac即爲由DisptcherServlet創建的ApplicationContext,而parent則爲有ContextLoaderListener創建的ApplicationContext。此後,框架又會調用ServletContext的setAttribute()方法將wac加入到ServletContext中。

2. Spring容器和Spring MVC容器的配置

  在Spring整體框架的核心概念中,容器是核心思想,就是用來管理Bean的整個生命週期的,而在一個項目中,容器不一定只有一個,Spring中可以包括多個容器,而且容器間有上下層關係,目前最常見的一種場景就是在一個項目中引入Spring和Spring MVC這兩個框架,其實就是兩個容器:Spring是根容器,Spring MVC是其子容器。在上文中,我們提到,Spring MVC容器可以訪問Spring容器中的Bean,Spring容器不能訪問Spring MVC容器的Bean。但是,若開發者對Spring容器和Spring MVC容器之間的關係瞭解不夠深入,常常會因配置失當而導致同時配置Spring和Spring MVC時出現一些奇怪的異常,比如Controller的方法無法攔截、Bean被多次加載等問題。

  Spring容器先於Spring MVC容器進行創建,並且Spring MVC容器的創建依賴於Spring容器。在實際工程中,一個項目中會包括很多配置,根據不同的業務模塊來劃分,我們一般思路是各負其責,明確邊界,即:Spring根容器負責所有其他非controller的Bean的註冊,而Spring MVC容器只負責controller相關的Bean的註冊,下面我們演示這種配置方案。

  (1). Spring容器配置:Spring根容器負責所有其他非controller的Bean的註冊

<!-- 啓用註解掃描,並定義組件查找規則 ,除了@controller,掃描所有的Bean -->
    <context:component-scan base-package="cn.edu.tju.rico">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

  (2). SpringMVC容器配置:Spring MVC容器只負責controller相關的Bean的註冊,其中@ControllerAdvice用於對控制器進行增強,常用於實現全局的異常處理類:

<!-- 啓用註解掃描,並定義組件查找規則 ,mvc層只負責掃描@Controller、@ControllerAdvice -->
    <!-- base-package 如果多個,用“,”分隔 -->
    <context:component-scan base-package="cn.edu.tju.rico"
        use-default-filters="false">
        <!-- 掃描@Controller -->
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
        <!--控制器增強,使一個Contoller成爲全局的異常處理類,類中用@ExceptionHandler方法註解的方法可以處理所有Controller發生的異常 -->
        <context:include-filter type="annotation"
            expression="org.springframework.web.bind.annotation.ControllerAdvice" />
    </context:component-scan>

  在<context:component-scan>中可以添加use-default-filters,Spring配置中的use-default-filters用來指示是否自動掃描帶有@Component、@Repository、@Service和@Controller的類。默認爲true,即默認掃描。如果想要過濾其中這四個註解中的一個,比如@Repository,可以添加<context:exclude-filter />子標籤,如下:

<context:component-scan base-package="cn.edu.tju.rico" scoped-proxy="targetClass" use-default-filters="true">  
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>  
</context:component-scan>  

  而<context:include-filter/>子標籤是用來添加掃描註解的:

<context:component-scan base-package="cn.edu.tju.rico" scoped-proxy="targetClass" use-default-filters="false">  
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>  
</context:component-scan>  

引用及參考資料

Spring 深入淺出核心技術 (一)
Spring AOP無法攔截Controller中的方法
springmvc-servlet.xml中use-default-filters的作用
Spring與SpringMVC的容器關係分析
深入分析Spring 與 Spring MVC容器

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