IoC和Spring的三種注入方式

名詞解釋

依賴倒置原則(DIP):一種軟件軟件架構設計原則
  • 上層模塊不應該依賴於下層模塊,它們共同依賴於一個抽象。

  • 抽象不能依賴於具象,具象依賴於抽象。

  依賴倒置解決的問題是:如果上層模塊依賴下層模塊(依賴是指上層模塊在類內創建下層模塊實例,根據下層模塊提供的方法編寫上層模塊的功能),每當下層模塊需要修改,上層模塊也要一併修改

控制反轉(IoC):一種反轉流、依賴和接口的方式(DIP的具體實現方式)

  將設計好的類交給系統控制,而不是在類的內部控制,稱爲控制反轉

依賴注入(DI):IoC的一種實現方式,用來反轉依賴(IoC的具體實現方式)

  如果不直接在類內實例底層模塊,就要提供其他方法傳入要執行的底層模塊,即依賴注入

IoC容器:依賴注入的框架,用來映射依賴,管理對象創建和生存週期(DI框架)

具體實現

在Java平臺的Spring框架下,有三種依賴注入方式

  • 構造函數注入

  • setter注入

  • 基於註解的注入

    ​ 假設使用場景爲用戶使用媒體播放器播放媒體文件,下面演示三種IoC實現方式

構造函數注入

Media接口和實現類
package example1;

public interface MediaInterface {
}
package example1;

public class Media implements MediaInterface{
    public Media() {
        System.out.println("創建媒體文件");
    }
}
MediaPlayer接口和實現類
package example1;

public interface MediaPlayerInterface {
    void play();
}
package example1;

public class MediaPlayer implements MediaPlayerInterface {
    private MediaInterface media;

    public MediaPlayer(MediaInterface media) {
        this.media = media;
        System.out.println("創建播放器");
    }

    @Override
    public void play() {
        System.out.println("播放器播放");
    }
}
User接口和實現類
package example1;

public interface UserInterface {
    void play();
}

package example1;

public class User implements UserInterface {
    MediaPlayerInterface mediaPlayer;

    public User(MediaPlayerInterface mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
        System.out.println("產生用戶");
    }

    @Override
    public void play() {
        mediaPlayer.play();
    }
}

Beans配置文件
<?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.xsd">

    <bean id="user" class="example1.User">
        <constructor-arg ref="mediaPlayer"/>
    </bean>

    <bean id="mediaPlayer" class="example1.MediaPlayer">
        <constructor-arg ref="media"/>
    </bean>

    <bean id="media" class="example1.Media"/>

</beans>
主函數入口
package example1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        User user = (User) context.getBean("user");
        user.play();
    }
}

運行結果

創建媒體文件
創建播放器
產生用戶
播放器播放

setter注入

  所有接口文件不變,修改實現類和Beans配置文件

Media類
package example1;

public class Media implements MediaInterface{
    public Media() {
        System.out.println("創建媒體文件");
    }
}
MediaPlayer類
package example1;

public class MediaPlayer implements MediaPlayerInterface {
    private MediaInterface media;

    public void setMedia(MediaInterface media) {
        this.media = media;
        System.out.println("創建播放器");
    }

    @Override
    public void play() {
        System.out.println("播放器播放");
    }
}

User類
package example1;

public class User implements UserInterface {
    MediaPlayerInterface mediaPlayer;

    public void setMediaPlayer(MediaPlayerInterface mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
        System.out.println("產生用戶");
    }

    @Override
    public void play() {
        mediaPlayer.play();
    }
}

Beans配置文件
<?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.xsd">

    <bean id="user" class="example1.User">
        <property name="mediaPlayer" ref="mediaPlayer"/>
    </bean>

    <bean id="mediaPlayer" class="example1.MediaPlayer">
        <property name="media" ref="media"/>
    </bean>

    <bean id="media" class="example1.Media"/>

</beans>

基於註解的注入

  Spring提供了多種註解應對各種情況,這裏只用到最簡單的 @Component@Autowired

@Component

作用:將當前類對象存入Spring容器中,默認value是類名首字母小寫

使用範圍:類前

@Autowired

作用:按照類型從Spring容器中選擇匹配數據類型注入

使用範圍:屬性前,方法前

Media類
package example1;

import org.springframework.stereotype.Component;

@Component
public class Media implements MediaInterface {
    public Media() {
        System.out.println("創建媒體文件");
    }
}
MediaPlayer類
package example1;

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

@Component
public class MediaPlayer implements MediaPlayerInterface {

    @Autowired
    private MediaInterface media;

    @Override
    public void play() {
        System.out.println("播放器播放");
    }
}
User類
package example1;

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

@Component
public class User implements UserInterface {
    @Autowired
    MediaPlayerInterface mediaPlayer;

    @Override
    public void play() {
        mediaPlayer.play();
    }
}
Beans配置文件
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="example1"/>

</beans>

  顯然,在這些代碼中並沒有實例化 MediaMediaPlayerUser 類,即這些類並不是由我們直接在類內控制,而是全部交給了系統控制,這就是控制反轉。這些類之間的依賴關係,也全部交給了xml文件來管理,而不是在代碼內部產生依賴。

  假如要寫一個新的下層類,那麼只要下層類實現上層類需要的操作,然後放入Spring容器中,Spring就會自動完成組裝,而不需要我們在類內更改依賴關係

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章