控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴注入)是實現IoC的一種方法,也有人認為DI只是IoC的另一種說法。沒有IoC的程序中我們使用面向對象編程對象的創建與對象間的依賴關系完全硬編碼在程序中,對象的創建由程序自己控制,控制反轉后將對象的創建轉移給第三方,個人認為所謂控制反轉就是:獲得依賴對象的方式反轉了。
IoC是Spring框架的核心內容,使用多種方式完美的實現了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置實現IoC。Spring容器在初始化時先讀取配置文件,根據配置文件或元數據創建與組織對象存入容器中,程序使用時再從Ioc容器中取出需要的對象。
采用XML方式配置Bean的時候,Bean的定義信息是和實現分離的,而采用注解的方式可以把兩者合為一體,Bean的定義信息直接以注解的形式定義在實現類中,從而達到了零配置的目的。
一、使用XML配置的方式實現IOC
假設項目中需要完成對圖書的數據訪問服務,我們定義好了IBookDAO接口與BookDAO實現類
IBookDAO接口如下:
1
2
3
4
5
6
7
8
9
10
11
|
package com.zhangguo.Spring051.ioc01; /** * 圖書數據訪問接口 */ public interface IBookDAO { /** * 添加圖書 */ public String addBook(String bookname); } |
BookDAO實現類如下:
1
2
3
4
5
6
7
8
9
10
11
|
package com.zhangguo.Spring051.ioc01; /** * 圖書數據訪問實現類 */ public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加圖書" +bookname+ "成功!" ; } } |
Maven項目的pom.xml如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
< project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >com.zhangguo</ groupId > < artifactId >Spring051</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >jar</ packaging > < name >Spring051</ name > < url >http://maven.apache.org</ url > < properties > < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > < spring.version >4.3.0.RELEASE</ spring.version > </ properties > < dependencies > < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < scope >test</ scope > < version >4.10</ version > </ dependency > < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context</ artifactId > < version >${spring.version}</ version > </ dependency > < dependency > < groupId >org.aspectj</ groupId > < artifactId >aspectjweaver</ artifactId > < version >1.8.9</ version > </ dependency > < dependency > < groupId >cglib</ groupId > < artifactId >cglib</ artifactId > < version >3.2.4</ version > </ dependency > </ dependencies > </ project > |
業務類BookService如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package com.zhangguo.Spring051.ioc01; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 圖書業務類 */ public class BookService { IBookDAO bookDAO; public BookService() { //容器 ApplicationContext ctx= new ClassPathXmlApplicationContext( "IOCBeans01.xml" ); //從容器中獲得id為bookdao的bean bookDAO=(IBookDAO)ctx.getBean( "bookdao" ); } public void storeBook(String bookname){ System.out.println( "圖書上貨" ); String result=bookDAO.addBook(bookname); System.out.println(result); } } |
容器的配置文件IOCBeans01.xml如下:
1
2
3
4
5
6
7
8
|
<? 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:p = "http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> < bean id = "bookdao" class = "com.zhangguo.Spring051.ioc01.BookDAO" ></ bean > </ beans > |
測試類Test如下:
1
2
3
4
5
6
7
8
9
10
|
package com.zhangguo.Spring051.ioc01; public class Test { @org .junit.Test public void testStoreBook() { BookService bookservice= new BookService(); bookservice.storeBook( "《Spring MVC權威指南 第一版》" ); } } |
運行結果:
二、使用Spring注解配置IOC
上一個示例是使用傳統的xml配置完成IOC的,如果內容比較多則配置需花費很多時間,通過注解可以減輕工作量,但注解后修改要麻煩一些,偶合度會增加,應該根據需要選擇合適的方法。
2.1、修改BookDAO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.zhangguo.Spring051.ioc02; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書數據訪問實現類 */ @Component ( "bookdaoObj" ) public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加圖書" +bookname+ "成功!" ; } } |
在類上增加了一個注解Component,在類的開頭使用了@Component注解,它可以被Spring容器識別,啟動Spring后,會自動把它轉成容器管理的Bean。
除了@Component外,Spring提供了3個功能基本和@Component等效的注解,分別對應于用于對DAO,Service,和Controller進行注解。
1:@Repository 用于對DAO實現類進行注解。
2:@Service 用于對業務層注解,但是目前該功能與 @Component 相同。
3:@Constroller用于對控制層注解,但是目前該功能與 @Component 相同。
2.2、修改BookService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package com.zhangguo.Spring051.ioc02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Component public class BookService { IBookDAO bookDAO; public void storeBook(String bookname){ //容器 ApplicationContext ctx= new ClassPathXmlApplicationContext( "IOCBeans02.xml" ); //從容器中獲得id為bookdao的bean bookDAO=(IBookDAO)ctx.getBean( "bookdaoObj" ); System.out.println( "圖書上貨" ); String result=bookDAO.addBook(bookname); System.out.println(result); } } |
將構造方法中的代碼直接寫在了storeBook方法中,避免循環加載的問題。
2.3、修改IOC配置文件IOCBeans02.xml
1
2
3
4
5
6
7
8
9
10
11
|
<? 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:p = "http://www.springframework.org/schema/p" xmlns:context = "http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> < context:component-scan base-package = "com.zhangguo.Spring051.ioc02" ></ context:component-scan > </ beans > |
粗體字是新增的xml命名空間與模式約束文件位置。增加了注解掃描的范圍,指定了一個包,可以通過屬性設置更加精確的范圍如:
<context>標記常用屬性配置:
resource-pattern:對指定的基包下面的子包進行選取
<context>子標記:
include-filter:指定需要包含的包
exclude-filter:指定需要排除的包
1
2
3
4
5
6
7
8
9
|
<!-- 自動掃描com.zhangguo.anno.bo中的類進行掃描 --> < context:component-scan base-package = "com.zhangguo.anno" resource-pattern = "bo/*.class" /> < context:component-scan base-package = "com.zhangguo.anno" > < context:include-filter type = "aspectj“ expression=" com.zhangguo.anno.dao.*.*"/> < context:exclude-filter type=“aspectj” expression=“com.zhangguo.anno.entity.*.*”/> </ context:component-scan > |
include-filter表示需要包含的目標類型,exclude-filter表示需要排除的目標類型,type表示采的過濾類型,共有如下5種類型:
Filter Type | Examples Expression | Description |
annotation | org.example.SomeAnnotation | 注解了SomeAnnotation的類 |
assignable | org.example.SomeClass | 所有擴展或者實現SomeClass的類 |
aspectj | org.example..*Service+ | AspectJ語法表示org.example包下所有包含Service的類及其子類 |
regex | org\.example\.Default.* | Regelar Expression,正則表達式 |
custom | org.example.MyTypeFilter | 通過代碼過濾,實現org.springframework.core.type.TypeFilter接口 |
expression表示過濾的表達式。
1
2
3
4
|
<!-- 1、如果僅希望掃描特定的類而非基包下的所有類,可使用resource-pattern屬性過濾特定的類 --> < context:component-scan base-package = "com.zhangguo.Spring051" resource-pattern = "ioc04/A*.class" > </ context:component-scan > |
只掃描com.zhangguo.Spring051.ioc04下所有名稱以A開始的類。
1
2
3
4
5
6
|
<!--2、掃描注解了org.springframework.stereotype.Repository的類 exclude-filter表示排除,include-filter表示包含,可以有多個--> < context:component-scan base-package = "com.zhangguo.Spring051.ioc04" > < context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Repository" /> < context:include-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> </ context:component-scan > |
1
2
3
4
5
|
<!--3、aspectj類型,掃描dao下所有的類,排除entity下所有的類--> < context:component-scan base-package = "com.zhangguo.anno" > < context:include-filter type = "aspectj" expression = "com.zhangguo.anno.dao.*.*" /> < context:exclude-filter type = "aspectj" expression = "com.zhangguo.anno.entity.*.*" /> </ context:component-scan > |
2.4、測試類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.zhangguo.Spring051.ioc02; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org .junit.Test public void testStoreBook() { //容器 ApplicationContext ctx= new ClassPathXmlApplicationContext( "IOCBeans02.xml" ); BookService bookservice=ctx.getBean(BookService. class ); bookservice.storeBook( "《Spring MVC權威指南 第二版》" ); } } |
運行結果:
2.5、小結
從配置文件中我們可以看出我們并沒有聲明bookdaoObj與BookService類型的對象,但還是從容器中獲得了實例并成功運行了,原因是:在類的開頭使用了@Component注解,它可以被Spring容器識別,啟動Spring后,會自動把它轉成容器管理的Bean。
三、自動裝配
從上一個示例中可以看出有兩個位置都使用了ApplicationContext初始化容器后獲得需要的Bean,可以通過自動裝配簡化。
3.1、修改BookDAO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.zhangguo.Spring051.ioc03; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書數據訪問實現類 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加圖書" +bookname+ "成功!" ; } } |
把注解修改成了Repository,比Component更貼切一些,非必要。
3.2、修改BookService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.zhangguo.Spring051.ioc03; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { @Autowired IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println( "圖書上貨" ); String result=bookDAO.addBook(bookname); System.out.println(result); } } |
將類BookService上的注解替換成了Service;在bookDao成員變量上增加了一個注解@Autowired,該注解的作用是:可以對成員變量、方法和構造函數進行注解,來完成自動裝配的工作,通俗來說就是會根據類型從容器中自動查到到一個Bean給bookDAO字段。@Autowired是根據類型進行自動裝配的,如果需要按名稱進行裝配,則需要配合@Qualifier。另外可以使用其它注解,@ Resource :等同于@Qualifier,@Inject:等同于@ Autowired。
@Service用于注解業務層組件(我們通常定義的service層就用這個)
@Controller用于注解控制層組件(如struts中的action)
@Repository用于注解數據訪問組件,即DAO組件
@Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行注解。
裝配注解主要有:@Autowired、@Qualifier、@Resource,它們的特點是:
1、@Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照類型來裝配注入;
2、@Autowired默認是按照類型裝配注入的,如果想按照名稱來轉配注入,則需要結合@Qualifier一起使用;
3、@Resource注解是又J2EE提供,而@Autowired是由spring提供,故減少系統對spring的依賴建議使用@Resource的方式;如果Maven項目是1.5的JRE則需換成更高版本的。
4、@Resource和@Autowired都可以書寫注解在字段或者該字段的setter方法之上
5、@Autowired 可以對成員變量、方法以及構造函數進行注釋,而 @Qualifier 的注解對象是成員變量、方法入參、構造函數入參。
6、@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。
7、@Autowired 注釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,通過屬性required可以設置非必要。
8、@Resource裝配順序
8.1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
8.2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
8.3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
8.4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package com.zhangguo.Spring051.ioc05; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { public IBookDAO getDaoofbook() { return daoofbook; } /* @Autowired @Qualifier("bookdao02") public void setDaoofbook(IBookDAO daoofbook) { this.daoofbook = daoofbook; }*/ @Resource(name="bookdao02") public void setDaoofbook(IBookDAO daoofbook) { this.daoofbook = daoofbook; } /* @Autowired @Qualifier("bookdao02") */ IBookDAO daoofbook; /* public BookService(@Qualifier("bookdao02") IBookDAO daoofbook) { this.daoofbook=daoofbook; }*/ public void storeBook(String bookname){ System.out.println( "圖書上貨" ); String result=daoofbook.addBook(bookname); System.out.println(result); } } |
3.3、測試運行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.zhangguo.Spring051.ioc03; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { @org .junit.Test public void testStoreBook() { //容器 ApplicationContext ctx= new ClassPathXmlApplicationContext( "IOCBeans03.xml" ); BookService bookservice=ctx.getBean(BookService. class ); bookservice.storeBook( "《Spring MVC權威指南 第三版》" ); } } |
運行結果:
四、零配置實現IOC
所謂的零配置就是不再使用xml文件來初始化容器,使用一個類型來替代,
IBookDAO代碼如下:
1
2
3
4
5
6
7
8
9
10
11
|
package com.zhangguo.Spring051.ioc06; /** * 圖書數據訪問接口 */ public interface IBookDAO { /** * 添加圖書 */ public String addBook(String bookname); } |
IBookDAO的實現類BookDAO代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; import org.springframework.stereotype.Repository; /** * 圖書數據訪問實現類 */ @Repository public class BookDAO implements IBookDAO { public String addBook(String bookname) { return "添加圖書" +bookname+ "成功!" ; } } |
在BookDAO類上注解了@Repository當初始化時該類將被容器管理會生成一個Bean,可以通過構造方法測試。
業務層BookService代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.zhangguo.Spring051.ioc06; import javax.annotation.Resource; import org.springframework.stereotype.Service; /** * 圖書業務類 */ @Service public class BookService { @Resource IBookDAO bookDAO; public void storeBook(String bookname){ System.out.println( "圖書上貨" ); String result=bookDAO.addBook(bookname); System.out.println(result); } } |
類BookService將對容器管理因為注解了@Service,初始化時會生成一個單例的Bean,類型為BookService。在字段bookDAO上注解了@Resource,用于自動裝配,Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean才會按照類型來裝配注入。
新增一個用于替代原xml配置文件的ApplicationCfg類,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.zhangguo.Spring051.ioc06; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * 容器的配置類 */ @Configuration @ComponentScan (basePackages= "com.zhangguo.Spring051.ioc06" ) public class ApplicationCfg { @Bean public User getUser(){ return new User( "成功" ); } } |
@Configuration相當于配置文件中的<beans/>,ComponentScan相當于配置文件中的context:component-scan,屬性也一樣設置
,@Bean相當于<bean/>,只能注解在方法和注解上,一般在方法上使用,源碼中描述:@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),方法名相當于id。中間使用到了User,User類的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.zhangguo.Spring051.ioc06; import org.springframework.stereotype.Component; @Component ( "user1" ) public class User { public User() { System.out.println( "創建User對象" ); } public User(String msg) { System.out.println( "創建User對象" +msg); } public void show(){ System.out.println( "一個學生對象!" ); } } |
初始化容器的代碼與以前有一些不一樣,具體如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package com.zhangguo.Spring051.ioc06; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test { @org .junit.Test public void testStoreBook() { //容器,注解配置應用程序容器,Spring通過反射ApplicationCfg.class初始化容器 ApplicationContext ctx= new AnnotationConfigApplicationContext(ApplicationCfg. class ); BookService bookservice=ctx.getBean(BookService. class ); bookservice.storeBook( "《Spring MVC權威指南 第四版》" ); User user1=ctx.getBean( "user1" ,User. class ); user1.show(); User getUser=ctx.getBean( "getUser" ,User. class ); getUser.show(); } } |
容器的初始化通過一個類型完成,Spring通過反射ApplicationCfg.class初始化容器,中間user1與getUser是否為相同的Bean呢?
答案是否定的,因為在ApplicationCfg中聲明的方法getUser當相于在xml文件中定義了一個<bean id="getUser" class="..."/>,在User類上注解@Component("user1")相當于另一個<bean id="user1" class="..."/>。
運行結果:
小結:使用零配置和注解雖然方便,不需要編寫麻煩的xml文件,但并非為了取代xml,應該根據實例需要選擇,或二者結合使用,畢竟使用一個類作為容器的配置信息是硬編碼的,不好在發布后修改。
五、示例下載
下載地址:SpringIoC.rar
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/best/p/5727935.html