名詞解釋
依賴倒置原則(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>
顯然,在這些代碼中並沒有實例化 Media
、MediaPlayer
、User
類,即這些類並不是由我們直接在類內控制,而是全部交給了系統控制,這就是控制反轉。這些類之間的依賴關係,也全部交給了xml文件來管理,而不是在代碼內部產生依賴。
假如要寫一個新的下層類,那麼只要下層類實現上層類需要的操作,然後放入Spring容器中,Spring就會自動完成組裝,而不需要我們在類內更改依賴關係