目錄java
【SSH進階之路】一步步重構容器實現Spring框架——從一個簡單的容器開始(八)
【SSH進階之路】一步步重構容器實現Spring框架——解決容器對組件的「侵入式」管理的兩種方案--主動查找和控制反轉(九)
【SSH進階之路】一步步重構容器實現Spring框架——配置文件+反射實現IoC容器(十)
【SSH進階之路】一步步重構容器實現Spring框架——完全封裝,實現簡單靈活的Spring框架(十一)(已更新)sql
對於IOC的原理,咱們曾經寫過一篇博文,【SSH進階之路】Spring的IOC逐層深刻——爲何要使用IOC[實例講設計模式
解](二),對比學習能夠對同一個問題理解的更加深入。安全
上篇博文【SSH進階之路】一步步重構容器實現Spring框架——從一個簡單的容器開始(八),咱們爲了去掉接口服務器
對具體實現的依賴關係,封裝了一個特別簡陋的容器,最後給你們拋出了一個問題:如何讓組件再也不依賴容器?這篇框架
博文主要是經過兩種解決方案來解決這個問題,最後對比各自的優缺點。ide
解決方案之一就是使用服務定位器(Service Locator),咱們也能夠叫主動查找。服務定位器用來封裝複雜的查學習
找邏輯,同時對外開放簡單的查找方法,全部組件均可以將查找請求委派給服務定位器。this
服務定位器但是一個簡單的類,也能夠是一種複雜的機制,如JNDI。不一樣的容器有着不一樣的查找機制。spa
下面是一個簡單的服務定位器:
public class ServiceLocator { static{ //該類加載的時候執行一次 Container.init(); } public static Object getDao(){ return Container.getComponent("dao4Mysql"); // return Container.getComponent("dao4Oracle"); } }修改ServiceImpl的查找邏輯:
import com.tgb.container.ServiceLocator; import com.tgb.container.dao.Dao; import com.tgb.container.service.Service; public class ServiceImpl implements Service { // 從服務器定位器查找所需的接口 private Dao dao = (Dao) ServiceLocator.getDao();; @Override public void serviceMethod() { dao.daoMethod(); } }UML類圖:
原先由ServiceImpl到Container的依賴線上添加了ServiceLocator,組件再也不直接依賴於容器,實現了「非侵入
式」管理。
解決方案之二就是使用控制反轉,咱們將控制權交給容器,在運行期才由容器決定將具體的實現動態的「注入」到
調用類的對象中。
Ioc是一種通用的設計原則,DI(依賴注入)則是具體的設計模式。依賴注入有三種方式,咱們使用的是Setter注入。
修改Service接口:
import com.tgb.container.dao.Dao; public interface Service { //增長注入接口的方法 public void setDao(Dao dao); public void serviceMethod(); }修改ServiceImpl:
import com.tgb.container.dao.Dao; import com.tgb.container.service.Service; public class ServiceImpl implements Service { private Dao dao; //依賴注入 public void setDao(Dao dao) { this.dao= dao; } @Override public void serviceMethod() { dao.daoMethod(); } }修改Container類的初始化方法:
import java.util.HashMap; import java.util.Map; import com.tgb.container.dao.Dao; import com.tgb.container.dao.impl.Dao4MySqlImpl; import com.tgb.container.service.Service; import com.tgb.container.service.impl.ServiceImpl; public class Container { private static Map<String, Object> components; private Container() { } /** * 初始化容器 */ public static synchronized void init() { if (components == null) { components = new HashMap<String, Object>(); //寫一個讀配置文件的類,根據讀取的配置文件,反射對應的類 //反射好類後進行 依賴管理,往對應的屬性上注入相應的類 Dao dao4Mysql = new Dao4MySqlImpl(); components.put("dao4Mysql", dao4Mysql); Service service = new ServiceImpl(); components.put("service", service); //容器維護依賴關係 service.setDao(dao4Mysql); } } /** * 查找組件 * * @param id * @return */ public static Object getComponent(String id) { return components.get(id); } }UML類圖:
由ServiceImpl到Container的依賴線能夠直接抹掉了!
Setter注入易於使用,可是會有安全問題。第一次注入以後,有可能再一次調用setter方法,改變了原有的依賴。
這種對依賴的無心修改會帶來沒法預料的後果。因此須要有安全檢查機制。
解決組件再也不依賴容器,咱們使用了兩種方案:服務定位器和控制反轉。
一、使用服務定位器查找組件,這是一種主動查找的行爲。這種查找有一個缺點:組件須要知道如何查找資源。組件和容器依賴變成了組件和服務定位器的依賴。
二、而後,咱們使用了控制反轉,這是一種被動查找的行爲。容器主動將資源推送給組件,組件則以一種合適的方式來接受資源。反轉資源獲取方向,這就是大名鼎鼎的Ioc(控制反轉)。
從類圖中咱們能夠發現,容器須要知道各個組件,容器和組件的耦合度仍是很高的,下篇博文【SSH進階之路】
一步步重構容器實現Spring框架——配置文件+反射實現IoC容器(十),咱們利用配置文件+反射進一步下降耦合度。