如何讓組件再也不依賴容器?這篇java
博文主要是經過兩種解決方案來解決這個問題,最後對比各自的優缺點。sql
服務定位器
解決方案之一就是使用服務定位器(Service Locator),咱們也能夠叫主動查找。服務定位器用來封裝複雜的查設計模式
找邏輯,同時對外開放簡單的查找方法,全部組件均可以將查找請求委派給服務定位器。安全
服務定位器但是一個簡單的類,也能夠是一種複雜的機制,如JNDI。不一樣的容器有着不一樣的查找機制。服務器
下面是一個簡單的服務定位器:ide
[java] view plain copythis
- public class ServiceLocator {
-
- static{
- //該類加載的時候執行一次
- Container.init();
- }
- public static Object getDao(){
- return Container.getComponent("dao4Mysql");
- // return Container.getComponent("dao4Oracle");
- }
- }
修改ServiceImpl的查找邏輯:spa
[java] view plain copy.net
- 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)
解決方案之二就是使用控制反轉,咱們將控制權交給容器,在運行期才由容器決定將具體的實現動態的「注入」到
調用類的對象中。
Ioc是一種通用的設計原則,DI(依賴注入)則是具體的設計模式。依賴注入有三種方式,咱們使用的是Setter注入。
修改Service接口:
[java] view plain copy
- import com.tgb.container.dao.Dao;
-
-
- public interface Service {
- //增長注入接口的方法
- public void setDao(Dao dao);
- public void serviceMethod();
- }
修改ServiceImpl:
[java] view plain copy
- 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類的初始化方法:
[java] view plain copy
- 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(控制反轉)。