1. 程式人生 > >通過Java代碼裝配Bean

通過Java代碼裝配Bean

情況 對象 gap 調用 tis one gen 情況下 代碼

上面梳理了通過註解來隱式的完成了組件的掃描和自動裝配,下面來學習下如何通過顯式的配置的裝配bean

二、通過Java類裝配bean

在前面定義了HelloWorldConfig類,並使用@ComponentScan和@Configuration註解,@Configuration註解表明了這個類是一個java配置類,該類用在獲取Spring應用上下文時,告訴Spring創建bean的細節,通過@ComponentScan,我們啟用了Spring的自動組件掃描,現在就讓我們來看如果通過java類來顯式的配置bean,下面我們通過一個音樂播放器的案例來實踐一下。

我們播放音樂,首先需要一個播放器,然後需要音樂資源,首先我們定義一個播放器接口和音樂資源接口

package com.seven.springTest.service;

// 播放器
public interface MediaPlayer {  
    void play();
}
package com.seven.springTest.service;

// 音樂資源
public interface MusicSource {

    void play();
}

本次播放音樂我們是光驅來播放cd音樂,下面我們來實現上面的接口,

package com.seven.springTest.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.seven.springTest.service.MusicSource;
import com.seven.springTest.service.MediaPlayer;

//定義光驅播放器
public class CDPlayer implements MediaPlayer {
    
    @Autowired
    // 定義一個音樂資源,這裏通過@Autowired來聲明需要註入MusicSource的依賴
    private MusicSource cd ;

    @Override
    public void play() {
        //播放音樂
        cd.play();
    }
}

實現音樂資源為光盤

package com.seven.springTest.service.impl;
import com.seven.springTest.service.MusicSource;

public class CDSource implements MusicSource {

    private String title = "七裏香";
    private String artist = "周傑倫";

    @Override
    public void play() {
        System.out.println("Playing " + title + " by " + artist);
    }
}

到目前為止我們已經完成播放器、音樂資源的接口定義和具體的實現,那麽我們如果告訴Spring應該創建哪麽bean,並為它們註入什麽依賴呢?在第一部分,我們通過@Component註解來隱式的告訴Spring,現在我們通過java類來配置bean組件。

@Bean

@Bean註解告訴Spring函數將返回一個對象,該對象需要註冊為Spring應用上下文中的bean,該方法中包含了產生bean實例的邏輯

package com.seven.springTest.Configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.seven.springTest.service.MusicSource;
import com.seven.springTest.service.MediaPlayer;
import com.seven.springTest.service.impl.CDPlayer;
import com.seven.springTest.service.impl.CDSource;

@Configuration
public class MediePlayerConfig {
    
    @Bean   //該方法返回的MusicSource對象需要註冊為Spring應用上下文中的bean
    public MusicSource cdsource(){
        return new CDSource();
    }
    
    @Bean  //該方法返回的MediaPlayer對象需要註冊為Spring應用上下文中的bean
    public MediaPlayer cdplayer(){
        return new CDPlayer();
    }
    
}

MediePlayerConfig類中,我們只添加了@Configuration註解,之前的@ComponentScan註解移除了,沒有配置啟動Spring的組件掃描,另外接口的實現類也沒有添加@Component註解,我們通過@Bean註解來告訴Spring哪些對象需要被註冊為Spring應用上下文中的bean。cdsource()方法返回了一個MusicSource類型的實例對象CDSource,該對象被註冊到Spring應用上下文,同樣的cdplayer()方法返回了一個MediaPlayer類型的實例CDPlayer註冊到Spring應用上下文中。下面我們來測試下

package com.seven.springTest.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.seven.springTest.Configuration.MediePlayerConfig;
import com.seven.springTest.Configuration.HelloWorldConfig;
import com.seven.springTest.service.MediaPlayer;

public class MediaPlayerTest {
    public static void main(String[] args) {
        //加載java配置類獲取Spring應用上下文
        ApplicationContext ac = new AnnotationConfigApplicationContext(MediePlayerConfig.class);
        //獲取播放器
        MediaPlayer player= ac.getBean(MediaPlayer.class);
        //播放
        player.play();
    }
}

我們在獲取播放器bean的時候,其實獲取到的就是MediePlayerConfig類中cdplayer()返回的對象CDPlayer,在CDPlayer中我們依賴MusicSource,通過@Autowired註解,Spring自動為該bean註入了對MusicSource的依賴,所以在測試代碼中我們只是獲取了MediaPlayer對象的實例player,至於player有哪些依賴,我們都不知道,都是由Spring容器來給我註入,這裏只關心播放器player,這就是Spring給我們帶來的便捷,我們不需要用代碼去管理對象的依賴關系,對象所有依賴的資源都有Spring容器來為我們註入。

隨著技術的發展,有一天光驅也可以插入U盤播放MP3音樂了,這個時候我們來實現一個MP3的音樂資源

package com.seven.springTest.service.impl;

import com.seven.springTest.service.MusicSource;

public class MP3Source implements MusicSource {
    
    private String title = "外婆";
    private String artist = "周傑倫";

    @Override
    public void play() {
        // TODO Auto-generated method stub
        System.out.println("MP3 Playing " + title + " by " + artist);
    }

}

在第一部分自動裝配中,如果Spring發現了多個bean滿足依賴關系,Spring就無法選擇了,那麽如果我們定義了MP3Source的實現,現在會不會也出現這樣的情況呢?通過運行程序,我們發現沒有產生任何影響,CDPlayer bean被註入的MusicSource依賴還是CDSource。這是因為我們在MediePlayerConfig中通過cdsource()告知了Spring產生bean的實現邏輯,那我們來修改下cdsource()

@Bean   //該方法返回的MusicSource對象需要註冊為Spring應用上下文中的bean
public MusicSource cdsource(){
    //返回MP3Source實例
    return new MP3Source();
}

我們再運行下測試方法,發現輸出內容變成了“==MP3 Playing 外婆 by 周傑倫==”,說明註入的依賴對象實現發生變化了,這是因為 cdsource()內實現的是返回MP3Source的實例。

同之前@Component一樣,添加@Bean註解的方法返回的bean也會被默認分配一個ID,默認情況下和方法名相同,如cdsource()方法返回的bean的ID就為“cdsource”,我們也可以指定bean的ID,如下:

@Bean(name="myCdplayer")      //該方法返回的MediaPlayer對象需要註冊為Spring應用上下文中的bean
public MediaPlayer cdplayer(){
    return new CDPlayer();
}

這樣在獲取bean的時候就可以通過ID來獲取

public class MediaPlayerTest {
    public static void main(String[] args) {
        //加載java配置類獲取Spring應用上下文
        ApplicationContext ac = new AnnotationConfigApplicationContext(MediePlayerConfig.class);
        //根據ID獲取bean
        MediaPlayer player= (MediaPlayer) ac.getBean("myCdplayer");
        //播放
        player.play();
    }
}

上面的案例中CDPlayer bean它依賴了MusicSource的依賴,我們在CDPlayer類中通過@Autowired聲明了CDPlayer需要的依賴,這裏還是一種通過註解隱式的配置,下面我們來通過java配置類來實現。

如果是顯式的配置,由於MediePlayerConfig中配置的bean都是通過方法返回的,所以需要在返回對象bean的方法裏註入依賴:

@Bean(name="myCdplayer")  //該方法返回的MediaPlayer對象需要註冊為Spring應用上下文中的bean
public MediaPlayer cdplayer(){
    return new CDPlayer(cdsource());   //通過對象的構造函數註入依賴
}

或者

@Bean(name="myCdplayer")  //該方法返回的MediaPlayer對象需要註冊為Spring應用上下文中的bean
public MediaPlayer cdplayer(MusicSource musicSource){
    return new CDPlayer(musicSource);   //通過對象的構造函數註入依賴
}

通過上面2中方式配置,Spring都可以對CDPlayer中的MusicSource對象完成依賴註入,下面我們在定義一個bean配置

@Bean(name="myCdplayer")  //該方法返回的MediaPlayer對象需要註冊為Spring應用上下文中的bean
public MediaPlayer cdplayer(){
    return new CDPlayer(cdsource());   //通過對象的構造函數註入依賴
}

@Bean(name="otherCdplayer")   //定義另外一個bean對象,
public MediaPlayer othercdplayer(){
    return new CDPlayer(cdsource());
}

MediaPlayer接口增加一個獲取播放資源的方法

package com.seven.springTest.service;

public interface MediaPlayer {

    /**
    * 獲取播放器加載的資源 
    * @return MusicSource
    */
    MusicSource getResource();

    /**
     * 播放
     */
    void play();
}

解下列,我們修改下Test代碼

package com.seven.springTest.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.seven.springTest.Configuration.MediePlayerConfig;
import com.seven.springTest.Configuration.HelloWorldConfig;
import com.seven.springTest.service.MediaPlayer;

public class MediaPlayerTest {
    public static void main(String[] args) {
        // 加載java配置類獲取Spring應用上下文
        ApplicationContext ac = new AnnotationConfigApplicationContext(MediePlayerConfig.class);
        // 獲取播放器
        MediaPlayer player = (MediaPlayer) ac.getBean("myCdplayer");
        MediaPlayer otherplayer = (MediaPlayer) ac.getBean("otherCdplayer");
        if (player.getResource().equals(otherplayer.getResource())) {
            System.out.println("true");
        }
        // 播放
        //player.play();
    }
}

運行後,我們發現輸出“true”,這說明的什麽情況呢,我們在cdplayer()和othercdplayer()方法中在調用CDPlayer(cdsource())構造時,通過cdsource()獲取的音樂資源對象是相同的,在默認情況下,Spring中的bean都是單例的,Spring會攔截對cdsource()的調用,並確保返回的是Spring創建的bean,也就是Spring本身在第一次調用cdsource()所創建的bean。

通過Java代碼裝配Bean