springMVC详解以及注解说明
springMVC詳解以及注解說明
基于注釋(Annotation)的配置有越來越流行的趨勢,Spring 2.5 順應這種趨勢,提供了完全基于注釋配置 Bean、裝配 Bean 的功能,您可以使用基于注釋的 Spring IoC 替換原來基于 XML 的配置。本文通過實例詳細講述了 Spring 2.5 基于注釋 IoC 功能的使用。
概述
注釋配置相對于 XML 配置具有很多的優勢:
· 它可以充分利用 Java 的反射機制獲取類結構信息,這些信息可以有效減少配置的工作。如使用 JPA 注釋配置 ORM 映射時,我們就不需要指定 PO 的屬性名、類型等信息,如果關系表字段和 PO 屬性名、類型都一致,您甚至無需編寫任務屬性映射信息——因為這些信息都可以通過 Java 反射機制獲取。
· 注釋和 Java 代碼位于一個文件中,而 XML 配置采用獨立的配置文件,大多數配置信息在程序開發完成后都不會調整,如果配置信息和 Java 代碼放在一起,有助于增強程序的內聚性。而采用獨立的 XML 配置文件,程序員在編寫一個功能時,往往需要在程序文件和配置文件中不停切換,這種思維上的不連貫會降低開發效率。
因此在很多情況下,注釋配置比 XML 配置更受歡迎,注釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多注釋類,現在您已經可以使用注釋配置完成大部分 XML 配置的功能。在這篇文章里,我們將向您講述使用注釋進行 Bean 定義和依賴注入的內容。
Spring2.5的注釋
Spring 2.5 提供了?AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor這四個主要的關于Annotation的BeanPostProcessor。
我們可以使用<context:annotation-config /> 來方便地、一次性的聲明者四個BeanPostProcessor。
1.Autowired... 提供對 Spring 特有的 Autowired 和 Qualifier 注釋。
2.CommonAnotation... 用于支持 JSR 250 的注釋
3.Persistence...用于 JPA 的 PersistenceUnit 和PersistenceContext 注釋
4.Required... 用于檢查被 Required 注釋標記的屬性是否被設定
?
原來我們是怎么做的
在使用注釋配置之前,先來回顧一下傳統上是如何配置 Bean 并完成 Bean 之間依賴關系的建立。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類需要在 Spring 容器中配置為 Bean:
Office 僅有一個屬性:
清單?1. Office.java
| package com.baobaotao; public class Office { ????private String officeNo =”001”; ????//省略?get/setter ????@Override ????public String toString() { ????????return "officeNo:" + officeNo; ????} } |
Car 擁有兩個屬性:
清單?2. Car.java
| package com.baobaotao; public class Car { ????private String brand; ????private double price; ????//?省略?get/setter ????@Override ????public String toString() { ????????return "brand:" + brand + "," + "price:" + price; ????} } |
Boss 擁有 Office 和 Car 類型的兩個屬性:
清單?3. Boss.java
| package com.baobaotao; public class Boss { ????private Car car; ????private Office office; ????//?省略?get/setter ????@Override ????public String toString() { ????????return "car:" + car + "\n" + "office:" + office; ????} } |
我們在 Spring 容器中將 Office 和 Car 聲明為 Bean,并注入到 BossBean 中:下面是使用傳統 XML 完成這個工作的配置文件 beans.xml:
清單?4. beans.xml將以上三個類配置成?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" ????xsi:schemaLocation="http://www.springframework.org/schema/beans ?http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> ????<bean id="boss" class="com.baobaotao.Boss"> ????????<property name="car" ref="car"/> ????????<property name="office" ref="office" /> ????</bean> ????<bean id="office" class="com.baobaotao.Office"> ????????<property name="officeNo" value="002"/> ????</bean> ????<bean id="car" class="com.baobaotao.Car" scope="singleton"> ????????<property name="brand" value="紅旗?CA72"/> ????????<property name="price" value="2000"/> ????</bean> </beans> |
當我們運行以下代碼時,控制臺將正確打出 boss 的信息:
清單?5.?測試類:AnnoIoCTest.java
| import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AnnoIoCTest { ????public static void main(String[] args) { ????????String[] locations = {"beans.xml"}; ????????ApplicationContext ctx = ????????????new ClassPathXmlApplicationContext(locations); ????????Boss boss = (Boss) ctx.getBean("boss"); ????????System.out.println(boss); ????} } |
這說明 Spring 容器已經正確完成了 Bean 創建和裝配的工作。
使用?@Autowired注釋(按類型匹配)
Spring 2.5 引入了 @Autowired 注釋,它可以對類成員變量、方法及構造函數進行標注,完成自動裝配的工作。來看一下使用 @Autowired 進行成員變量自動注入的代碼:
清單?6.?使用?@Autowired?注釋的?Boss.java
| ??????????????? package com.baobaotao; import org.springframework.beans.factory.annotation.Autowired; public class Boss { ????@Autowired ????private Car car; ????@Autowired ????private Office office; ????… } |
Spring 通過一個BeanPostProcessor 對 @Autowired 進行解析,所以要讓@Autowired起作用必須事先在?Spring?容器中聲明AutowiredAnnotationBeanPostProcessorBean。
清單?7.?讓?@Autowired?注釋工作起來
| <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ????xsi:schemaLocation="http://www.springframework.org/schema/beans ?http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> ????<!--?該?BeanPostProcessor?將自動起作用,對標注?@Autowired的?Bean進行自動注入?--> ????<bean class="org.springframework.beans.factory.annotation. ????????AutowiredAnnotationBeanPostProcessor"/> ????<!--?移除?boss Bean?的屬性注入配置的信息?--> ????<bean id="boss" class="com.baobaotao.Boss"/> ????<bean id="office" class="com.baobaotao.Office"> ????????<property name="officeNo" value="001"/> ????</bean> ????<bean id="car" class="com.baobaotao.Car" scope="singleton"> ????????<property name="brand" value="紅旗?CA72"/> ????????<property name="price" value="2000"/> ????</bean> </beans> |
這樣,當 Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor將掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有 @Autowired 注釋時就找到和其匹配(默認按類型匹配)的 Bean,并注入到對應的地方中去。
按照上面的配置,Spring 將直接采用 Java 反射機制對 Boss 中的 car 和 office 這兩個私有成員變量進行自動注入。所以對成員變量使用 @Autowired 后,您大可將它們的?setter方法(setCar()?和?setOffice())從?Boss中刪除。
當然,您也可以通過?@Autowired對方法或構造函數進行標注,來看下面的代碼:
清單?8.?將?@Autowired?注釋標注在?Setter?方法上
| ??????????????? package com.baobaotao; public class Boss { ????private Car car; ????private Office office; ?????@Autowired ????public void setCar(Car car) { ????????this.car = car; ????} ? ????@Autowired ????public void setOffice(Office office) { ????????this.office = office; ????} ????… } |
這時,@Autowired?將查找被標注的方法的入參類型的?Bean,并調用方法自動注入這些?Bean。而下面的使用方法則對構造函數進行標注:
清單?9.?將?@Autowired?注釋標注在構造函數上
| ??????????????? package com.baobaotao; public class Boss { ????private Car car; ????private Office office; ? ????@Autowired ????public Boss(Car car ,Office office){ ????????this.car = car; ????????this.office = office ; ????} ? ????… } |
由于 Boss() 構造函數有兩個入參,分別是 car 和 office,@Autowired 將分別尋找和它們類型匹配的 Bean,將它們作為 Boss(Car car,Office office) 的入參來創建 Boss Bean。
?
當候選?Bean數目不為?1?時的應對方法
在默認情況下使用 @Autowired 注釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個。當找不到一個匹配的 Bean 時,Spring 容器將拋出BeanCreationException 異常,并指出必須至少擁有一個匹配的 Bean。我們可以來做一個實驗:
清單?10.?候選?Bean?數目為?0時
| ??????????????? <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ?????xsi:schemaLocation="http://www.springframework.org/schema/beans ?http://www.springframework.org/schema/beans/spring-beans-2.5.xsd "> ? ????<bean class="org.springframework.beans.factory.annotation. ????????AutowiredAnnotationBeanPostProcessor"/> ????<bean id="boss" class="com.baobaotao.Boss"/> ????<!--?將?office Bean?注釋掉?--> ????<!-- <bean id="office" class="com.baobaotao.Office"> ????<property name="officeNo" value="001"/> ????</bean>--> ????<bean id="car" class="com.baobaotao.Car" scope="singleton"> ????????<property name="brand" value="紅旗?CA72"/> ????????<property name="price" value="2000"/> ????</bean> </beans> |
由于 office Bean 被注釋掉了,所以 Spring 容器中將沒有類型為 Office 的 Bean 了,而 Boss 的 office 屬性標注了 @Autowired,當啟動 Spring 容器時,異常就產生了。
當不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用@Autowired(required = false),這等于告訴?Spring:在找不到匹配?Bean?時也不報錯。來看一下具體的例子:
清單?11.?使用?@Autowired(required = false)
| ??????????????? package com.baobaotao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required; public class Boss { ????private Car car; ????private Office office; ????@Autowired ????public void setCar(Car car) { ????????this.car = car; ????} ????@Autowired(required = false) ????public void setOffice(Office office) { ????????this.office = office; ????} ????… } |
當然,一般情況下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自動注入而又允許不注入的情況一般僅會在開發期或測試期碰到(如為了快速啟動 Spring 容器,僅引入一些模塊的 Spring 配置文件),所以@Autowired(required = false) 會很少用到。
和找不到一個類型匹配 Bean 相反的一個錯誤是:如果 Spring 容器中擁有多個候選 Bean,Spring 容器在啟動時也會拋出BeanCreationException 異常。來看下面的例子:
清單?12.?在?beans.xml?中配置兩個?Office?類型的?Bean
| ??????????????? … <bean id="office" class="com.baobaotao.Office"> ????<property name="officeNo" value="001"/> </bean> <bean id="office2" class="com.baobaotao.Office"> ????<property name="officeNo" value="001"/> </bean> … |
我們在 Spring 容器中配置了兩個類型為 Office 類型的 Bean,當對 Boss 的 office 成員變量進行自動注入時,Spring 容器將無法確定到底要用哪一個 Bean,因此異常發生了。
Spring 允許我們通過?@Qualifier注釋指定注入?Bean的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:
清單?13.?使用?@Qualifier?注釋指定注入?Bean?的名稱
| ??????????????? @Autowired public void setOffice(@Qualifier("office")Office office) { ????this.office = office; } |
@Qualifier("office")中的office是Bean?的名稱,所以?@Autowired和@Qualifier?結合使用時,自動注入的策略就從?byType?轉變成byName?了。
@Autowired 可以對成員變量、方法以及構造函數進行注釋,而 @Qualifier 的標注對象是成員變量、方法入參、構造函數入參。正是由于注釋對象的不同,所以 Spring 不將 @Autowired 和 @Qualifier 統一成一個注釋類。下面是對成員變量和構造函數入參進行注釋的代碼:
對成員變量進行注釋:
清單?14.?對成員變量使用?@Qualifier注釋
| public class Boss { ????@Autowired ????private Car car; ? ????@Autowired ????@Qualifier("office") ????private Office office; ????… } |
對構造函數入參進行注釋:
清單?15.?對構造函數變量使用?@Qualifier注釋
| public class Boss { ????private Car car; ????private Office office; ????@Autowired ????public Boss(Car car , @Qualifier("office")Office office){ ????????this.car = car; ????????this.office = office ; ????} } |
@Qualifier只能和?@Autowired結合使用,是對@Autowired?有益的補充。一般來講,@Qualifier?對方法簽名中入參進行注釋會降低代碼的可讀性,而對成員變量注釋則相對好一些。
?
使用?JSR-250的注釋
Spring 不但支持自己定義的 @Autowired 的注釋,還支持幾個由 JSR-250 規范定義的注釋,它們分別是?@Resource、@PostConstruct以及?@PreDestroy。
@Resource
@Resource 的作用相當于 @Autowired,只不過 @Autowired按 byType 自動注入,面?@Resource?默認按byName自動注入罷了。@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring將?@Resource注釋的name?屬性解析為?Bean的名字,而type?屬性則解析為?Bean的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機制使用 byName 自動注入策略。
Resource 注釋類位于 Spring 發布包的?lib/j2ee/common-annotations.jar?類包中,因此在使用之前必須將其加入到項目的類庫中。來看一個使用@Resource 的例子:
清單?16.?使用?@Resource?注釋的?Boss.java
| ??????????????? package com.baobaotao; import javax.annotation.Resource; public class Boss { ????//?自動注入類型為?Car?的?Bean ????@Resource ????private Car car; ????//?自動注入?bean?名稱為?office的?Bean ????@Resource(name = "office") ????private Office office; } |
一般情況下,我們無需使用類似于@Resource(type=Car.class) 的注釋方式,因為 Bean 的類型信息可以通過 Java 反射從代碼中獲取。
要讓 JSR-250 的注釋生效,除了在?Bean類中標注這些注釋外,還需要在Spring容器中注冊一個負責處理這些注釋的?BeanPostProcessor:
| <bean ??class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/> |
CommonAnnotationBeanPostProcessor實現了 BeanPostProcessor 接口,它負責掃描使用了 JSR-250 注釋的 Bean,并對它們進行相應的操作。
@PostConstruct?和@PreDestroy
Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執行特定的操作,您既可以通過實現InitializingBean/DisposableBean 接口來定制初始化之后 / 銷毀之前的操作方法,也可以通過 <bean>元素的 init-method/destroy-method 屬性指定初始化之后 / 銷毀之前調用的操作方法。
JSR-250 為初始化之后/銷毀之前方法的指定定義了兩個注釋類,分別是@PostConstruct 和 @PreDestroy,這兩個注釋只能應用于方法上。標注了@PostConstruct注釋的方法將在類實例化后調用,而標注了?@PreDestroy的方法將在類銷毀之前調用。
清單?17.?使用?@PostConstruct?和?@PreDestroy?注釋的?Boss.java
| ??????????????? package com.baobaotao; import javax.annotation.Resource; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; public class Boss { ????@Resource ????private Car car; ????@Resource(name = "office") ????private Office office; ????@PostConstruct ????public void postConstruct1(){ ????????System.out.println("postConstruct1"); ????} ????@PreDestroy ????public void preDestroy1(){ ????????System.out.println("preDestroy1"); ????} ????… } |
您只需要在方法前標注@PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化后或銷毀之前被 Spring 容器執行了。
我們知道,不管是通過實現InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的init-method/destroy-method 屬性進行配置,都只能為 Bean 指定一個初始化 / 銷毀的方法。但是使用@PostConstruct 和 @PreDestroy 注釋卻可以指定多個初始化 / 銷毀方法,那些被標注@PostConstruct 或@PreDestroy 注釋的方法都會在初始化 / 銷毀時被執行。???? *********需要注意的是用了這兩個注解標注的方法是不可以有參數的,否則就會報錯:java.lang.IllegalStateException: Lifecycle method annotation requires a no-arg method? *****************************
通過以下的測試代碼,您將可以看到 Bean 的初始化 / 銷毀方法是如何被執行的:
清單?18.?測試類代碼
| ??????????????? package com.baobaotao; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AnnoIoCTest { ????public static void main(String[] args) { ????????String[] locations = {"beans.xml"}; ????????ClassPathXmlApplicationContext ctx = ????????????new ClassPathXmlApplicationContext(locations); ????????Boss boss = (Boss) ctx.getBean("boss"); ????????System.out.println(boss); ????????ctx.destroy();//?關閉?Spring?容器,以觸發?Bean銷毀方法的執行 ????} } |
這時,您將看到標注了@PostConstruct 的 postConstruct1() 方法將在 Spring 容器啟動時,創建 Boss Bean 的時候被觸發執行,而標注了 @PreDestroy 注釋的preDestroy1() 方法將在 Spring 容器關閉前銷毀 Boss Bean 的時候被觸發執行。
?
使用<context:annotation-config/>簡化配置
Spring 2.1 添加了一個新的 context的 Schema 命名空間,該命名空間對注釋驅動、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道注釋本身是不會做任何事情的,它僅提供元數據信息。要使元數據信息真正起作用,必須讓負責處理這些元數據的處理器工作起來。
而我們前面所介紹的AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor 就是處理這些注釋元數據的處理器。但是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 為我們提供了一種方便的注冊這些BeanPostProcessor 的方式,這就是 <context:annotation-config/>。請看下面的配置:
清單?19.?調整?beans.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:context="http://www.springframework.org/schema/context" ?????xsi:schemaLocation="http://www.springframework.org/schema/beans ?http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ?http://www.springframework.org/schema/context ?http://www.springframework.org/schema/context/spring-context-2.5.xsd"> ? ????<context:annotation-config/> ????<bean id="boss" class="com.baobaotao.Boss"/> ????<bean id="office" class="com.baobaotao.Office"> ????????<property name="officeNo" value="001"/> ????</bean> ????<bean id="car" class="com.baobaotao.Car" scope="singleton"> ????????<property name="brand" value="紅旗?CA72"/> ????????<property name="price" value="2000"/> ????</bean> </beans> |
<context:annotationconfig/>將隱式地向?Spring容器注冊AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor以及equiredAnnotationBeanPostProcessor這?4?個BeanPostProcessor。
在配置文件中使用 context 命名空間之前,必須在<beans> 元素中聲明 context 命名空間。
?
使用?@Component
雖然我們可以通過 @Autowired 或 @Resource 在 Bean 類中使用自動注入功能,但是 Bean 還是在 XML 文件中通過 <bean>進行定義 —— 也就是說,在 XML 配置文件中定義 Bean,通過 @Autowired 或 @Resource 為 Bean 的成員變量、方法入參或構造函數入參提供自動注入的功能。能否也通過注釋定義 Bean,從?XML?配置文件中完全移除Bean?定義的配置呢?答案是肯定的,我們通過Spring 2.5提供的?@Component?注釋就可以達到這個目標了。
下面,我們完全使用注釋定義 Bean 并完成 Bean 之間裝配:
清單?20.?使用?@Component?注釋的?Car.java
| ??????????????? package com.baobaotao; import org.springframework.stereotype.Component; @Component public class Car { ????… } |
僅需要在類定義處,使用 @Component 注釋就可以將一個類定義了 Spring 容器中的 Bean。下面的代碼將 Office 定義為一個 Bean:
清單?21.?使用?@Component?注釋的?Office.java
| ??????????????? package com.baobaotao; import org.springframework.stereotype.Component; @Component public class Office { ????private String officeNo = "001"; ????… } |
這樣,我們就可以在 Boss 類中通過 @Autowired 注入前面定義的 Car 和 Office Bean 了。
清單?22.?使用?@Component?注釋的?Boss.java
| ??????????????? package com.baobaotao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component("boss") public class Boss { ????@Autowired ????private Car car; ????@Autowired ????private Office office; ????… } |
@Component 有一個可選的入參,用于指定 Bean 的名稱,在 Boss 中,我們就將 Bean 名稱定義為“boss”。一般情況下,Bean 都是 singleton 的,需要注入 Bean 的地方僅需要通過 byType 策略就可以自動注入了,所以大可不必指定 Bean 的名稱。
在使用 @Component 注釋后,Spring容器必須啟用類掃描機制以啟用注釋驅動?Bean定義和注釋驅動Bean?自動注入的策略。Spring 2.5?對context?命名空間進行了擴展,提供了這一功能,請看下面的配置:
清單?23.?簡化版的?beans.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:context="http://www.springframework.org/schema/context" ????xsi:schemaLocation="http://www.springframework.org/schema/beans ?http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ?http://www.springframework.org/schema/context ?http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="com.baobaotao"/> </beans> |
這里,所有通過 <bean>元素定義 Bean 的配置內容已經被移除,僅需要添加一行<context:component-scan/> 配置就解決所有問題了——Spring XML 配置文件得到了極致的簡化(當然配置元數據還是需要的,只不過以注釋形式存在罷了)。<context:component-scan/>的 base-package 屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。
<context:component-scan/>還允許定義過濾器將基包下的某些類納入或排除。Spring 支持以下 4 種類型的過濾方式,通過下表說明:
表?1.?掃描過濾方式
| 過濾器類型 | 說明 |
| 注釋 | 假如 com.baobaotao.SomeAnnotation 是一個注釋類,我們可以將使用該注釋的類過濾出來。 |
| 類名指定 | 通過全限定類名進行過濾,如您可以指定將 com.baobaotao.Boss 納入掃描,而將 com.baobaotao.Car 排除在外。 |
| 正則表達式 | 通過正則表達式定義過濾的類,如下所示: com\.baobaotao\.Default.* |
| AspectJ 表達式 | 通過 AspectJ 表達式定義過濾的類,如下所示: com. baobaotao..*Service+ |
| ? | ? |
下面是一個簡單的例子:
| <context:component-scan base-package="com.baobaotao"> ????<context:include-filter type="regex" ????????expression="com\.baobaotao\.service\..*"/> ????<context:exclude-filter type="aspectj" ????????expression="com.baobaotao.util..*"/> </context:component-scan> |
值得注意的是<context:component-scan/> 配置項不但啟用了對類包進行掃描以實施注釋驅動 Bean 定義的功能,同時還啟用了注釋驅動自動注入的功能(即還隱式地在內部注冊了AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor),因此當使用<context:component-scan/>后,就可以將<context:annotation-config/>移除了。
默認情況下通過 @Component 定義的 Bean 都是 singleton 的,如果需要使用其它作用范圍的 Bean,可以通過 @Scope 注釋來達到目標,如以下代碼所示:
清單?24.?通過?@Scope?指定?Bean的作用范圍
| ??????????????? package com.baobaotao; import org.springframework.context.annotation.Scope; … @Scope("prototype") @Component("boss") public class Boss { ????… } |
這樣,當從 Spring 容器中獲取 boss Bean 時,每次返回的都是新的實例了。
?
具有特殊語義的注釋
Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應用程序采用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋,而用 @Component 對那些比較中立的類進行注釋。
?
注釋配置和?XML配置的適用場合
是否有了這些 IOC 注釋,我們就可以完全摒除原來 XML 配置的方式呢?答案是否定的。有以下幾點原因:
· 注釋配置不一定在先天上優于 XML 配置。如果 Bean 的依賴關系是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發生調整,那么注釋配置優于 XML 配置;反之如果這種依賴關系會在部署時發生調整,XML 配置顯然又優于注釋配置,因為注釋是對 Java 源代碼的調整,您需要重新改寫源代碼并重新編譯才可以實施調整。
· 如果 Bean 不是自己編寫的類(如 JdbcTemplate、SessionFactoryBean等),注釋配置將無法實施,此時 XML 配置是唯一可用的方式。
· 注釋配置往往是類級別的,而 XML 配置則可以表現得更加靈活。比如相比于 @Transaction事務注釋,使用 aop/tx 命名空間的事務配置更加靈活和簡單。
所以在實現應用中,我們往往需要同時使用注釋配置和 XML 配置,對于類級別且不會發生變動的配置可以優先考慮注釋配置;而對于那些第三方類以及容易發生調整的配置則應優先考慮使用 XML 配置。Spring 會在具體實施 Bean 創建和 Bean 注入之前將這兩種配置方式的元信息融合在一起。
?
小結
Spring 在 2.1 以后對注釋配置提供了強力的支持,注釋配置功能成為 Spring 2.5 的最大的亮點之一。合理地使用 Spring 2.5 的注釋配置,可以有效減少配置的工作量,提高程序的內聚性。但是這并不意味著傳統 XML 配置將走向消亡,在第三方類 Bean 的配置,以及那些諸如數據源、緩存池、持久層操作模板類、事務管理等內容的配置上,XML 配置依然擁有不可替代的地位。
?
?
?
Spring2.5?注解介紹(3.0通用)
?
??注解說明
??注冊注解處理器
??方式一:bean
<beanclass="org.springframework.beans.factory.annotation.
AutowiredAnnotationBeanPostProcessor"/>
??方式二:?命名空間<context:annotation-config/>
<context:annotationconfig />?將隱式地向Spring?容器注冊AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor?、?PersistenceAnnotationBeanPostProcessor?以及RequiredAnnotationBeanPostProcessor?這4?個BeanPostProcessor?。
??方式三:?命名空間<context:component-scan/>
如果要使注解工作,則必須配置component-scan?,實際上不需要再配置annotation-config。
base-package?屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。還允許定義過濾器將基包下的某些類納入或排除。
?
?
??Spring?支持以下4?種類型的過濾方式:
????????????????注解?org.example.SomeAnnotation?將所有使用SomeAnnotation?注解的類過濾出來
????????????????類名指定?org.example.SomeClass?過濾指定的類
????????????????正則表達式?com.kedacom.spring.annotation.web..*?通過正則表達式過濾一些類
????????????????AspectJ?表達式?org.example..*Service+?通過AspectJ?表達式過濾一些類
?
?
??正則表達式的過濾方式舉例:
<context:component-scanbase-package="com.casheen.spring.annotation">
<context:exclude-filtertype="regex"
expression="com.casheen.spring.annotation.web..*"/>
</context:component-scan>
??注解的過濾方式舉例:
<context:component-scanbase-package="com.netqin" >
<context:include-filtertype="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
?
啟用Spring MVC?注解
??啟動SpringMVC?的注解功能,完成請求和注解POJO?的映射
??<beanclass="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
?
?
注解介紹
????????????????@Controller
????????????????@Service
????????????????@Autowired
????????????????@RequestMapping
????????????????@RequestParam
????????????????@ModelAttribute
????????????????@Cacheable
????????????????@CacheFlush
????????????????@Resource
????????????????@PostConstruct
????????????????@PreDestroy
????????????????@Repository
????????????????@Component?(不推薦使用)
????????????????@Scope
????????????????@SessionAttributes
????????????????@InitBinder
?
@Component是通用標注,@Controller標注web控制器,@Service標注Servicec層的服務,@Respository標注DAO層的數據訪問
?
@Controller
????????????????例如
??????????????@Controller
?????????????public class SoftCreateController extends SimpleBaseController {}
????????????????或者
????????????@Controller("softCreateController")
????????????????說明
??????????????@Controller?負責注冊一個bean?到spring?上下文中,bean?的ID?默認為類名稱開頭字母小寫
?
?
?
@Service
????????????????例如
??????????????@Service
?????????????public class SoftCreateServiceImpl implements ISoftCreateService {}
????????????????或者
?????????????@Service("softCreateServiceImpl")
????????????????說明
??????????????@Service?負責注冊一個bean?到spring?上下文中,bean?的ID?默認為類名稱開頭字母小寫
?
?
?
@Autowired
????????????????例如
??????????????@Autowired
?????????????private ISoftPMService softPMService;
????????????????或者
?
????@Autowired(required=false)
???? private ISoftPMService softPMService = newSoftPMServiceImpl();
?
??????????說明
??? @Autowired?根據bean?類型從spring?上線文中進行查找,注冊類型必須唯一,否則報異常。與@Resource?的區別在于,@Resource?允許通過bean?名稱或bean?類型兩種方式進行查找@Autowired(required=false)?表示,如果spring?上下文中沒有找到該類型的bean?時,才會使用newSoftPMServiceImpl();
?
?
@RequestMapping
??類
@Controller?
@RequestMapping("/bbtForum.do")
public class BbtForumController {
????????????@RequestMapping(params = "method=listBoardTopic")
publicString listBoardTopic(int topicId,User user) {}
}
?
????方法
??????????????@RequestMapping("/softpg/downSoftPg.do")
?? ? ? ? ? ??@RequestMapping(value="/softpg/ajaxLoadSoftId.do",method = POST)
?????????????@RequestMapping(value = "/osu/product/detail.do", params = {"modify=false" }, method =POST)
????說明
??? @RequestMapping?可以聲明到類或方法上
?
??參數綁定說明
如果我們使用以下的?URL?請求:
http://localhost/bbtForum.do?method=listBoardTopic&topicId=1&userId=10&userName=tom
topicId URL?參數將綁定到?topicId?入參上,而?userId?和?userNameURL?參數將綁定到?user?對象的?userId?和?userName?屬性中。和?URL?請求中不允許沒有?topicId?參數不同,雖然?User?的?userId?屬性的類型是基本數據類型,但如果?URL?中不存在?userId?參數,Spring?也不會報錯,此時?user.userId?值為?0?。如果?User?對象擁有一個?dept.deptId?的級聯屬性,那么它將和?dept.deptIdURL?參數綁定。
?
?
@RequestParam
??參數綁定說明
@RequestParam("id")
http://localhost/bbtForum.do?method=listBoardTopic&id=1&userId=10&userName=tom
listBoardTopic(@RequestParam("id")inttopicId,User user)?中的?topicId?綁定到?id?這個?URL?參數,那么可以通過對入參使用?@RequestParam注解來達到目的
?
請求處理方法入參的可選類型
????????????????Java?基本數據類型和?String
????????????????????默認情況下將按名稱匹配的方式綁定到?URL?參數上,可以通過?@RequestParam?注解改變默認的綁定規則
?
????????????????request/response/session
?既可以是?ServletAPI?的也可以是?Portlet API?對應的對象,Spring?會將它們綁定到Servlet?和?Portlet?容器的相應對象上
?
??org.springframework.web.context.request.WebRequest
?內部包含了?request?對象
?
??java.util.Locale
綁定到?request?對應的?Locale?對象上
?
??????????java.io.InputStream/java.io.Reader
???????????????可以借此訪問?request?的內容
?
??????????java.io.OutputStream/ java.io.Writer
可以借此操作?response?的內容
?
???????????任何標注了?@RequestParam?注解的入參
???????????????被標注?@RequestParam?注解的入參將綁定到特定的?request?參數上。
?
???????????java.util.Map/ org.springframework.ui.ModelMap
它綁定?Spring MVC?框架中每個請求所創建的潛在的模型對象,它們可以被?Web?視圖對象訪問(如?JSP?)
?
???????????命令/?表單對象(注:一般稱綁定使用?HTTPGET?發送的?URL?參數的對象為命令對象,而稱綁定使用HTTP POST?發送的?URL?參數的對象為表單對象)
?????????????????????它們的屬性將以名稱匹配的規則綁定到?URL?參數上,同時完成類型的轉換。
????????????? ? ? ??而類型轉換的規則可以通過?@InitBinder?注解或通過?HandlerAdapter?的配置進行調?整
?????org.springframework.validation.Errors /org.springframework.validation.BindingResult
????????????? ? ? ??為屬性列表中的命令/?表單對象的校驗結果,注意檢驗結果參數必須緊跟在命令/?表單對象的后面
?
??????org.springframework.web.bind.support.SessionStatus
?可以通過該類型?status?對象顯式結束表單的處理,這相當于觸發?session?清除其中的通過@SessionAttributes?定義的屬性
?
請求處理方法返回值的可選類型
??void
此時邏輯視圖名由請求處理方法對應的?URL?確定,如以下的方法:
@RequestMapping("/welcome.do")
public void welcomeHandler() {}
對應的邏輯視圖名為?“?welcome?”?
??String
此時邏輯視圖名為返回的字符,如以下的方法:
@RequestMapping(method = RequestMethod.GET)
public String setupForm(@RequestParam("ownerId") int ownerId,ModelMap model) {
Owner owner = this.clinic.loadOwner(ownerId);
model.addAttribute(owner);
return "ownerForm";
}
對應的邏輯視圖名為?“?ownerForm?”?
??org.springframework.ui.ModelMap
和返回類型為?void?一樣,邏輯視圖名取決于對應請求的?URL?,如下面的例子:
@RequestMapping("/vets.do")
public ModelMap vetsHandler() {
return newModelMap(this.clinic.getVets());
}
對應的邏輯視圖名為?“?vets?”?,返回的?ModelMap?將被作為請求對應的模型對象,可以在?JSP?視圖頁面中訪問到。
?
??ModelAndView
當然還可以是傳統的?ModelAndView?。
?
?
@ModelAttribute
????????????????作用域:request
????????????????例如
? ? ? ? ? ??@RequestMapping("/base/userManageCooper/init.do")
? ? ?? ?? ? public StringhandleInit(@ModelAttribute("queryBean") ManagedUser sUser,Modelmodel,){
????????????????或者
??????????????@ModelAttribute("coopMap")//?將coopMap?返回到頁?面
????? ? ???publicMap<Long,CooperatorInfo> coopMapItems(){}
????????????????說明
?
@ModelAttribute?聲明在屬性上,表示該屬性的value?來源于model?里"queryBean"?,并被保存到model?里@ModelAttribute?聲明在方法上,表示該方法的返回值被保存到model?里
?
?
@Cacheable?和@CacheFlush
????????????????@Cacheable?:聲明一個方法的返回值應該被緩?存
??????????????例如:@Cacheable(modelId= "testCaching")
????????????????@CacheFlush?:聲明一個方法是清空緩存的觸發器
?????????????????例如:@CacheFlush(modelId= "testCaching")
????????????????說明
??????????????要配合緩存處理器使用,參考:?http://hanqunfeng.javaeye.com/blog/603719
?
?
@Resource
????????????????例如
??????????????@Resource
? ? ? ? ? ??? private DataSourcedataSource; // inject the bean named 'dataSource'
????????????????或者
?
@Resource(name="dataSource")
@Resource(type=DataSource.class)
?
??說明
@Resource?默認按bean?的name?進行查找,如果沒有找到會按type?進行查找,
此時與@Autowired?類?似
@PostConstruct?和@PreDestroy
??@PostConstruct
在方法上加上注解@PostConstruct?,這個方法就會在Bean?初始化之后被Spring?容器執?行
(注:Bean?初始化包括,實例化Bean?,并裝配Bean?的屬性(依賴注入))。
??@PreDestroy
在方法上加上注解@PreDestroy?,這個方法就會在Bean?被銷毀前被Spring?容器執行。
?
?
@Repository
??與@Controller?、@Service?類似,都是向spring?上下文中注冊bean?,不在贅述。
?
?
@Component?(不推薦使用)
??@Component
@Component?是所有受Spring?管理組件的通用形式,Spring?還提供了更加細化的注解形式:?@Repository?、@Service?、@Controller?,它們分別對應存儲層Bean?,業務層Bean?,和展示層Bean?。
目前版本(2.5?)中,這些注解與@Component?的語義是一樣的,完全通用,?在Spring?以后的版本中可能會給它們追加更多的語義。?所以,我們推薦使用@Repository?、@Service?、@Controller?來替代@Component?。
?
?
@Scope
????????????????例如
???????????????@Scope("session")
???????????????@Repository()
???????????????public class UserSessionBean implementsSerializable {}
?
????????????????說明
?
在使用XML?定義Bean?時,可以通過bean?的scope?屬性來定義一個Bean?的作用范圍,
同樣可以通過@Scope?注解來完成
?
?
@SessionAttributes
??說明
Spring?允許我們有選擇地指定?ModelMap?中的哪些屬性需要轉存到?session?中,
以便下一個請求屬對應的?ModelMap?的屬性列表中還能訪問到這些屬性。
這一功能是通過類定義處標注?@SessionAttributes?注解來實現的。
@SessionAttributes?只能聲明在類上,而不能聲明在方法上。
?
??例如
@SessionAttributes("currUser")//?將ModelMap?中屬性名為currUser?的屬性
@SessionAttributes({"attr1","attr2"})
@SessionAttributes(types = User.class)
@SessionAttributes(types = {User.class,Dept.class})
@SessionAttributes(types ={User.class,Dept.class},value={"attr1","attr2"})
@InitBinder
??說明
如果希望某個屬性編輯器僅作用于特定的?Controller?,
可以在?Controller?中定義一個標注?@InitBinder?注解的方法,
可以在該方法中向?Controller?了注冊若干個屬性編輯器
?
??例如
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,false));
}
總結
以上是生活随笔為你收集整理的springMVC详解以及注解说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis面试问题(一)
- 下一篇: scala学习笔记-Array、Arra