自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring IoC是如何進行依賴注入的

開發(fā) 前端
DI(Dependency Injection),Spring IoC 不是一種技術(shù),而是一種思想,通過這種思想,能夠指導(dǎo)我們設(shè)計出松耦合的程序代碼。

一、依賴注入(DI)

DI(Dependency Injection),Spring IoC 不是一種技術(shù),而是一種思想,通過這種思想,能夠指導(dǎo)我們設(shè)計出松耦合的程序代碼。而Spring IoC這個思想的作用體現(xiàn)在兩個方面,一是如何將Bean裝配到容器中去以及如何從容器中獲取Bean,二是如何解決Bean之間的依賴關(guān)系,換句話說,就是如果由IoC容器來管理依賴關(guān)系,當(dāng)一個Bean需要依賴另外一個Bean時,IoC容器如何實現(xiàn)這樣的依賴關(guān)系。

解決Spring中Bean之間的依賴的實現(xiàn)方式,在Spring的概念中就被稱之為依賴注入(Dependency Injection,DI)。普遍認(rèn)為的Spring依賴注入的實現(xiàn)方式有三種:構(gòu)造方法注入、setter方法注入、注解注入。但,就我而言,我認(rèn)為應(yīng)該劃分為兩種形式——基于XML注入和基于注解注入,然后再細(xì)分為下面的形式:

Spring IoC是如何進行依賴注入的

基于XML的注入方式是我們最先學(xué)習(xí)和使用的方式,也是最熟悉的方式,就簡單的做個介紹,舉個例子。

1. 通過構(gòu)造方法注入

  1. public class UserServiceImpl implements UserService { 
  2.  
  3.     private UserDao userDao; 
  4.  
  5.     public UserServiceImpl(UserDao userDao) { 
  6.         this.userDao = userDao; 
  7.     } 
  8.  
  9.     /**繼承自UserService的方法**/ 

首先定義一個服務(wù)層UserServiceImpl,然后在其內(nèi)部增加對dao層的引用userDao。

接下來就是添加一個構(gòu)造方法public UserServiceImpl(UserDao userDao)以待Spring通過這個方法為userDao注入實例。

  1. <!--注冊userDao--> 
  2. <bean id="userDao" class="com.klasdq.sb.c1.di.dao.impl.UserDaoImpl"></bean> 
  3.  
  4. <!--注冊userService 并注入userDao--> 
  5. <bean id="userService" class="com.klasdq.sb.c1.di.service.impl.UserServiceImpl"> 
  6.         <constructor-arg name="userDao" ref="userDao"></constructor-arg> 
  7. </bean> 

最后在Spring XML配置文件中注入相應(yīng)的bean實例。

通過構(gòu)造方法的注入,必須要注入類中具有對應(yīng)的構(gòu)造方法,若沒有對應(yīng)的構(gòu)造方法,會出現(xiàn)報錯。

2. 通過setter方法注入

修改UserServiceImpl.java為:

  1. public class UserServiceImpl implements UserService { 
  2.  
  3.     private UserDao userDao; 
  4.  
  5.     public void setUserDao(UserDao userDao) { 
  6.         this.userDao = userDao; 
  7.     } 
  8.  
  9.     /**繼承自UserService的方法**/ 

再修改XML文件內(nèi)容為:

  1. <!--注冊userDao--> 
  2. <bean id="userDao" class="com.klasdq.sb.c1.di.dao.impl.UserDaoImpl"></bean> 
  3.  
  4. <!--注冊userService 并注入userDao--> 
  5. <bean id="userService" class="com.klasdq.sb.c1.di.service.impl.UserServiceImpl"> 
  6.         <property name="userDao" ref="userDao"></property> 
  7. </bean> 

這兩種方式的區(qū)別在于,一、UserServiceImpl.java可以不用添加構(gòu)造方法,但是必須存在一個無參構(gòu)造方法(如public UserServiceImpl(),示例里面沒寫,是因為java默認(rèn)會提供一個無參構(gòu)造方法)以供Spring 容器注冊生成Bean(如userService)。二、XML文件中,采用構(gòu)造方法注入時,需要使用這對標(biāo)簽;而在setter方法注入時,使用 標(biāo)簽。

在XML注入過程中,除了使用ref=""引用之外,還可以使用value=""設(shè)定具體的值,其效果和使用注解@Value差不多。

二、基于注解的依賴注入

三、Autowired

源碼:

  1. @Target({ElementType.CONSTRUCTOR, 
  2.          ElementType.METHOD, 
  3.          ElementType.PARAMETER, 
  4.          ElementType.FIELD, 
  5.          ElementType.ANNOTATION_TYPE}) 
  6. @Retention(RetentionPolicy.RUNTIME) 
  7. @Documented 
  8. public @interface Autowired { 
  9.     boolean required() default true; 

@Autowired是基于注解的依賴注入的關(guān)鍵點,它的源碼非常簡單,只有一個參數(shù)request(),這個參數(shù)的作用是標(biāo)識注入Bean是否一定要注入,也就是說,在Spring容器沒有找到相應(yīng)Bean時,如果其值為true,就會報出異常;如果其值為false,就不會出現(xiàn)異常,但在使用過程中,如果容器一直不對Bean進行注入,那么有可能出現(xiàn)空指針異常。

另外一點就是,源碼當(dāng)中的@Target所包含的參數(shù)正好就是基于注解的依賴注入的注入方式種類,@Target決定了@Autowired能夠標(biāo)注在哪些類型上面。

1. 通過構(gòu)造方法注入:

  1. @Service("userService") 
  2. public class UserServiceImpl implements UserService { 
  3.  
  4.     private UserDao userDao; 
  5.  
  6.     @Autowired 
  7.     public UserServiceImpl(UserDao userDao) { 
  8.         this.userDao = userDao; 
  9.     } 
  10.     /**繼承自UserService的方法**/ 

根據(jù)開發(fā)文檔的說法,這種只有一個構(gòu)造方法的情況,自Spring4.3以后,就不再需要添加@Autowired標(biāo)注,也可以。但是,如果有多個構(gòu)造方法時,是必須要對其中一個方法標(biāo)注@Autowired,不然Spring會報出異常。

2. 通過setter方法注入

  1. @Service("userService") 
  2. public class UserServiceImpl implements UserService { 
  3.  
  4.     private UserDao userDao; 
  5.  
  6.     @Autowired 
  7.     public void setUserDao(UserDao userDao) { 
  8.         this.userDao = userDao; 
  9.     } 
  10.     /**繼承自UserService的方法**/ 

3. 通過字段注入

  1. @Service("userService") 
  2. public class UserServiceImpl implements UserService { 
  3.  
  4.     @Autowired 
  5.     private UserDao userDao; 
  6.  
  7.     /**繼承自UserService的方法**/ 

4. 通過方法入?yún)⒆⑷?/strong>

上面三種注入方式,都是比較熟悉的就不再多做闡述了。重點說一下參數(shù)注入,其實方法入?yún)⒆⑷敕绞礁杏X上是和構(gòu)造方法、setter方法注入形式差不多,相當(dāng)于將構(gòu)造方法、setter方法上的注解@Autowired放到入?yún)⒌奈恢?。說起來可能有些抽象,直接看例子:

  1. @Component 
  2. public class UserDaoImpl implements UserDao { 
  3.     //簡單返回一個User,模擬數(shù)據(jù)庫查找過程 
  4.     @Override 
  5.     public User getUser(Long id, String name){ 
  6.         User user = new User(); 
  7.         user.setId(id); 
  8.         user.setName(name); 
  9.         user.setAccount("12345678911"); 
  10.         user.setPassword("******"); 
  11.         user.setOtherInfo("this is a test account"); 
  12.         return user; 
  13.     } 
  1. //UserService類 
  2. @Service("userService") 
  3. public class UserServiceImpl implements UserService { 
  4.  
  5.     private UserDao userDao; 
  6.  
  7.    public UserServiceImpl(@Autowired UserDao userDao, 
  8.                           @Autowired User user) { 
  9.        System.out.println("UserServiceImpl: "+user); 
  10.        this.userDao = userDao; 
  11.    } 
  12.  
  13.     @Override 
  14.     public User getUser(Long id, String name){ 
  15.         return userDao.getUser(id,name); 
  16.     } 
  1. //簡單的配置類 
  2. //作用就是為標(biāo)有@Componet(@Service也算)注解的類 生成Bean 
  3. //同時 為@Autowired標(biāo)識下的Bean(對象) 注入實例 
  4. @Configuration 
  5. @ComponentScan 
  6. public class DIConfig { 
  7.  
  8.     //用于Service類中入?yún)ser的注入 
  9.     @Bean 
  10.     public User getUser(){ 
  11.         User u = new User(); 
  12.         u.setName("user inject into service"); 
  13.         return u; 
  14.     } 
  1. //測試類 
  2. //注意:使用JUnit4測試時,如果需要使用@Autowired注入那么必須添加 
  3. //@RunWith    標(biāo)注使用Spring方式啟動(或者SpringBootRunner) 
  4. //@ContextConfiguration  掃描配置類 
  5. @RunWith(SpringRunner.class) 
  6. @ContextConfiguration(classes = DIConfig.class) 
  7. public class DITest { 
  8.  
  9.     //如果不添加測試類上兩個注解,會注入失敗 
  10.     @Autowired 
  11.     private UserService userService; 
  12.  
  13.     @Test 
  14.     public void testAutowired(){ System.out.println(userService.getUser(1L,"name")); 
  15.     } 

運行測試方法之后就得到以下結(jié)果:

public UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)中的輸出結(jié)果:

Spring IoC是如何進行依賴注入的

public void testAutowired()測試方法中的輸出結(jié)果:

Spring IoC是如何進行依賴注入的

注意這里public UserServiceImpl(@Autowired UserDao userDao,@Autowired User user)的入?yún)ⅲ?/p>

userDao是UserServiceImpl的字段,但user不是。也就是說,我們可以在構(gòu)造方法中添加任意參數(shù),只要是我們需要的,不一定要求該參數(shù)是類中屬性字段。

此外還有需要注意的是,這里所說的方法,不是任意的方法,而是構(gòu)造方法或setter方法,這種public void initService(@Autowired UserDao userDao)自定義的方法是無法完成注入的。

四、@Primary 和 @Qualifier

在上面的例子中,我們注入使用到的bean,都只是容器中只有一個Bean實例的情況。那么當(dāng)容器當(dāng)中出現(xiàn)多個同類型的Bean時,如何處理呢?

修改配置類代碼如下:

  1. @Configuration 
  2. @ComponentScan 
  3. public class DIConfig { 
  4.  
  5.     @Bean 
  6.     public User getUser(){ 
  7.         User u = new User(); 
  8.         u.setName("this is user"); 
  9.         return u; 
  10.     } 
  11.  
  12.     @Bean 
  13.     public User getUser2(){ 
  14.         User u = new User(); 
  15.         u.setName("this is user2"); 
  16.         return u; 
  17.     } 

修改測試類:

  1. @RunWith(SpringRunner.class) 
  2. @ContextConfiguration(classes = DIConfig.class) 
  3. public class DITest { 
  4.  
  5.     @Autowired 
  6.     private User user; 
  7.  
  8.     @Test 
  9.     public void testAutowiredPriamry(){ 
  10.         System.out.println(user); 
  11.     } 

當(dāng)不做其他處理時,結(jié)果為:

Spring IoC是如何進行依賴注入的

因為有兩個User Bean(getUser , getUser2 ,@Bean未注明的情況下,默認(rèn)方法名為Bean Name)的存在,所以Spring無法確定使用那個進行注入。

修改方式:

  • 在@Bean中設(shè)置name,如@Bean(name="user"),當(dāng)名字能夠匹配上private User user;時,也能完成注入。
  • 將private User user改寫成getUser或getUser2任意一個,也能完成注入。道理和上面一樣,Spring首先會按照type進行匹配,如果無法匹配,再按照名字匹配,都匹配不上時,自然拋出異常。

除此之外呢,Spring為我們提供了兩個注解來消除依賴注入時的歧義問題。

  • @Primary
    1. @Target({ElementType.TYPE,    // 類、接口、枚舉類型 
    2.          ElementType.METHOD})// 方法 
    3. @Retention(RetentionPolicy.RUNTIME) 
    4. @Documented 
    5. public @interface Primary { 

@Primary是一個設(shè)定相同類型Bean優(yōu)先級的注解,也就是說,一旦在某個類型上添加@Priamry,當(dāng)注入時,沒有明確指定Bean時,就會注入被@Priamry標(biāo)識的Bean。

  1. @Configuration 
  2. @ComponentScan 
  3. public class DIConfig { 
  4.  
  5.     @Primary 
  6.     @Bean 
  7.     public User getUser(){ 
  8.         User u = new User(); 
  9.         u.setName("this is user"); 
  10.         return u; 
  11.     } 
  12.  
  13.     @Bean 
  14.     public User getUser2(){ 
  15.         User u = new User(); 
  16.         u.setName("this is user2"); 
  17.         return u; 
  18.     } 

比如上面這樣,在getUser()上添加相應(yīng)注解,測試方法也能正常運行。

但是這種方法的問題就在于@Priamry可以用在很多類上,如果同一類型有多個Bean被標(biāo)注了@Primary,那么@Priamry就失去了應(yīng)有的效果。

  • @Qualifier

因此,Spring又提供了@Qualifier這個注解,直接標(biāo)注在@Autowired注入的Bean上,為其明確指定注入某個Bean。

  1. @Target({ElementType.FIELD,  
  2.          ElementType.METHOD,  
  3.          ElementType.PARAMETER,  
  4.          ElementType.TYPE,  
  5.          ElementType.ANNOTATION_TYPE}) 
  6. @Retention(RetentionPolicy.RUNTIME) 
  7. @Inherited 
  8. @Documented 
  9. public @interface Qualifier { 
  10.     String value() default ""; 

@Qualifier可以出現(xiàn)任何@Autowired能夠出現(xiàn)的地方,與之配套使用。比如下面這樣:

  1. @RunWith(SpringRunner.class) 
  2. @ContextConfiguration(classes = DIConfig.class) 
  3. public class DITest {  
  4.  
  5.     //直接指定使用getUser2進行注入 
  6.     @Autowired 
  7.     @Qualifier("getUser2") 
  8.     private User user; 
  9.  
  10.     @Test 
  11.     public void testAutowiredPriamry(){ 
  12.         System.out.println(user); 
  13.     } 

這兩種注解都可以消除歧義,推薦使用@Bean(name="xxx")和@Qualifier(value="xxx")組合使用的方式`。但是如果開發(fā)環(huán)境中沒有歧義的存在,自然也就不需要使用這些了。

當(dāng)然,上面只是對于@Autowired一些常用介紹,如果想要了解更多,可以查看Annotation-based Container Configuration。這個參考文檔當(dāng)中有著更加詳細(xì)、豐富的介紹。

總結(jié)

總得來說,Spring是如何實現(xiàn)IoC的呢?首先,Spring提供了一個獲取和管理Bean的IoC容器。然后,再提供了一套依賴注入的機制去幫助IoC容器更好地管理各個Bean之間的依賴關(guān)系,從而更好地實現(xiàn)IoC的思想。一個Bean不可能完全脫離其他Bean的依賴關(guān)系而獨立存在,當(dāng)一個Bean需要其他Bean的引入才能初始化時,就需要依賴注入這個機制。

舉例來說,假如存在一個A類想要去調(diào)用B接口的方法或者說需要B接口的一個實例。

傳統(tǒng)的程序流程是,使用一個C類實現(xiàn)B接口,然后A類創(chuàng)建一個C類的實例,從而調(diào)用其方法。

在Spring的依賴注入過程中就變成了,A類只需要在自己的內(nèi)部添加一個注入接口(廣義上的接口,不是interface這個接口),這個接口可以是構(gòu)造方法,也可以是setter方法或者說其他形式;同時添加一個對B接口的引用(private B b;)。

當(dāng)真正需要生成A類的實例時,Spring IoC容器根據(jù)A類提供的接口,為其注入相應(yīng)的Bean,而這個Bean可以是C類(class C implements B{}),也可以D類(class D implements B{})等等;具體是誰,根據(jù)Bean的裝配策略和IoC容器中的Bean來確定,不再由開發(fā)人員管理。

本文授權(quán)轉(zhuǎn)載自公眾號「良許Linux」。良許,世界500強外企Linux開發(fā)工程師,公眾號里分享大量Linux干貨,歡迎關(guān)注!

 

責(zé)任編輯:趙寧寧 來源: 今日頭條
相關(guān)推薦

2011-05-31 10:00:21

Android Spring 依賴注入

2013-04-15 17:55:12

Windows認(rèn)證安全認(rèn)證

2013-04-16 10:33:58

Windows 安全認(rèn)微軟

2024-05-08 08:16:11

2023-10-17 00:01:34

Linux操作系統(tǒng)

2010-08-02 16:33:11

Flex Spring

2021-05-06 07:58:57

Spring BeanIOCAOP

2021-01-22 06:35:44

IoCxml驅(qū)動技術(shù)

2022-04-30 08:50:11

控制反轉(zhuǎn)Spring依賴注入

2016-03-21 17:08:54

Java Spring注解區(qū)別

2014-09-19 10:46:36

LuaCC++

2020-12-29 08:34:08

spring循環(huán)依賴開發(fā)

2011-03-29 09:51:58

GuiceIOC

2017-08-16 16:00:05

PHPcontainer依賴注入

2009-09-08 15:22:20

Spring依賴注入

2020-04-06 14:50:43

MySQLSQL數(shù)據(jù)庫

2024-05-13 11:12:08

FO-DICOM開源應(yīng)用開發(fā)

2021-03-15 10:23:44

IT支出技術(shù)投資CIO

2020-07-14 14:59:00

控制反轉(zhuǎn)依賴注入容器

2011-03-01 13:45:41

Spring3Annotation
點贊
收藏

51CTO技術(shù)棧公眾號