久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    Spring IoC是如何進(jìn)行依賴注入的

     小世界的野孩子 2022-06-10 發(fā)布于北京

    依賴注入(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ì)分為下面的形式:

    DI實現(xiàn).png

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

    • 通過構(gòu)造方法注入
    public class UserServiceImpl implements UserService {
        
        private UserDao userDao;
        
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
        
        /**繼承自UserService的方法**/
    }
    

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

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

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

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

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

    • 通過setter方法注入

    修改UserServiceImpl.java為:

    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
        
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        
        /**繼承自UserService的方法**/
    }
    

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

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

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

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

    基于注解的依賴注入

    @Autowired
    • 源碼
    @Target({ElementType.CONSTRUCTOR,
             ElementType.METHOD,
             ElementType.PARAMETER,
             ElementType.FIELD,
             ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
        boolean required() default true;
    }
    

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

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

    • 通過構(gòu)造方法注入
    @Service("userService")
    public class UserServiceImpl implements UserService {
        
        private UserDao userDao;
        
        @Autowired
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
        /**繼承自UserService的方法**/
    }
    

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

    • 通過setter方法注入
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        private UserDao userDao;
    
    	@Autowired
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        /**繼承自UserService的方法**/
    }
    
    • 通過字段注入
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
    	@Autowired
        private UserDao userDao;
        
        /**繼承自UserService的方法**/
    }
    
    • 通過方法入?yún)⒆⑷?/strong>

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

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

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

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

    di-test1.png

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

    di-test2.png

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

    userDaoUserServiceImpl的字段,但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時,如何處理呢?

    修改配置類代碼如下:

    @Configuration
    @ComponentScan
    public class DIConfig {
    
        @Bean
        public User getUser(){
            User u = new User();
            u.setName("this is user");
            return u;
        }
    
        @Bean
        public User getUser2(){
            User u = new User();
            u.setName("this is user2");
            return u;
        }
    }
    

    修改測試類:

    @RunWith(SpringRunner.class)
    @ContextConfiguration(classes = DIConfig.class)
    public class DITest {
        
        @Autowired
        private User user;
    
        @Test
        public void testAutowiredPriamry(){
            System.out.println(user);
        }
    }
    

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

    di-test-priority.png

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

    修改方式:

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

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

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

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

    @Configuration
    @ComponentScan
    public class DIConfig {
    
        @Primary
        @Bean
        public User getUser(){
            User u = new User();
            u.setName("this is user");
            return u;
        }
    
        @Bean
        public User getUser2(){
            User u = new User();
            u.setName("this is user2");
            return u;
        }
    }
    

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

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

    • @Qualifier

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

    @Target({ElementType.FIELD, 
             ElementType.METHOD, 
             ElementType.PARAMETER, 
             ElementType.TYPE, 
             ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Qualifier {
        String value() default "";
    }
    

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

    @RunWith(SpringRunner.class)
    @ContextConfiguration(classes = DIConfig.class)
    public class DITest { 
    
        //直接指定使用getUser2進(jìn)行注入
        @Autowired
        @Qualifier("getUser2")
        private User user;
    
        @Test
        public void testAutowiredPriamry(){
            System.out.println(user);
        }
    }
    

    這兩種注解都可以消除歧義,推薦使用@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ā)人員管理。


    有收獲?希望老鐵們來個三連擊,給更多的人看到這篇文章

      本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評論

      發(fā)表

      請遵守用戶 評論公約

      類似文章 更多

      主站蜘蛛池模板: 亚洲精品色无码AV试看| 日产精品99久久久久久| 国产一区二区三区日韩精品| 中文字幕无码免费久久| 国产精品大片中文字幕| 好涨好硬好爽免费视频| 亚洲欧美日韩国产综合一区二区| 青青国产揄拍视频| 欧美人和黑人牲交网站上线| 久久精品国产福利一区二区| 国产一精品一AV一免费爽爽| 最新亚洲人成网站在线影院| 无码人妻一区二区三区免费N鬼沢 午夜三级A三级三点在线观看 | 亚洲人成网站77777在线观看| 成人国产精品日本在线观看| 久久国产精品波多野结衣AV| 成人无码视频在线观看免费播放| 国产精品无码不卡一区二区三区| 无码专区 人妻系列 在线| 色屁屁WWW影院免费观看入口 | 丁香五月亚洲综合在线国内自拍| 国产福利一区二区三区在线观看| 亚洲色欲色欱WWW在线| 午夜亚洲乱码伦小说区69堂| 国产迷姦播放在线观看| 久久综合伊人77777| 1000部拍拍拍18勿入免费视频 | 国产精品有码在线观看| 一本一本久久AA综合精品| 亚洲国产欧美一区二区好看电影| 久久精品亚洲乱码伦伦中文| 国产亚洲AV电影院之毛片| 婷婷综合久久狠狠色成人网| 任你躁国产自任一区二区三区| 日本精品不卡一二三区| 无码熟妇人妻AV在线电影| 亚洲性色AV一区二区三区| 最新国产AV最新国产在钱| 4hu44四虎www在线影院麻豆| 亚洲高清揄拍自拍| 99RE6在线观看国产精品|