自己動手開發IOC容器
前兩天寫簡歷,寫了一句:精通Spring IoC容器。怎麼個精通法?還是自己動手寫個IOC容器吧。
什麼是IoC(Inversion of Control)?什麼是DI(Dependency Injection)?不多解釋,自己Google!希望你先明確幾個概念,該文章不做這方面的闡述,重點關注如何實現。我將用簡要的語言從需求,設計,程式碼三方面來描述一個簡單的IoC容器,來說明IoC容器是如何幫我們建立類的例項、如何實現依賴注入,最後會奉上一個完整的IoC容器例項。
一、需求,實現一個IoC容器
1、需求描述:
2、圖中一些概念:
(1)服務元資料:一種是我們的編譯好的類檔案,即.class檔案;另一種是xml檔案。你可以理解為spring的註解和xml兩種配置bean的方式。
(2)服務定義資訊:由元資料轉換來的資訊,即讀取bean中的註解或xml檔案,獲取到的物件描述資訊。
(3)服務提供者:物件的例項。你可以理解為Spring中Bean的例項;
(4)服務定義資訊儲存區:儲存服務定義資訊的地方;
(5)服務提供者例項快取區:儲存服務提供者的地方。
3、流程描述:
(1)建立容器:初始化容器,實質是初始化容器內部的工廠,然後讀取元資料,轉化為服務定義資訊,儲存到服務定義資訊儲存區;
(2)呼叫服務提供者A的方法:獲取該服務提供者A的服務定義資訊,判斷是否有快取,如果有,直接呼叫快取中的A;如果沒有,據此例項化A,放入快取,若發現A依賴其他服務提供者B,則查詢快取,如果有,將快取中的B注入A;否則例項化B,注入到A中,同時放入快取。以此類推。
(3)上面兩個是核心流程,在此基礎上,擴充套件了幾個流程:
1)獲取所有服務提供者定義資訊;
2)獲取所有服務提供者例項;
3)獲取某個服務提供者例項;
4)熱載入新的服務提供者。
二、設計,根據需求,以面向物件為基礎,融合幾種設計模式來勾勒IOC容器。
注:由於原始碼較多,會佔很大篇幅,所以只給出了部分核心程式碼實現,所有程式碼見我的CSDN CODE,見文章末尾的說明。
1、UML,類圖
2、設計說明
從類圖可以看出,嚴格按照面向介面的方式程式設計,鬆耦合的設計保證了可擴充套件性和靈活性。
我定義了一套實現IOC容器的規範,這套規範的直接表現就是一組介面及其關係,有沒有一點J2EE的感覺。然後,我給出了各個介面的一個預設實現,也就是給出了一個容器的預設實現。其他開發者既可以全部我的預設實現或全部使用自定義實現,也可以部分元件使用預設實現,部分元件實現自定義實現。
3、介紹介面與預設實現,即擴充套件點。
(1)Container介面及預設實現
核心程式碼:
import com.tgb.Entity.BeanEntity;
import com.tgb.data.IDataContext;
/**
* @Author : JiaLin
* @Email : [email protected]
* @Date : 2014/6/22
* @Description :容器的核心介面
*/
public interface IContainer{
//獲取容器的某個服務提供者例項
public Object getBean(String name);
//根據服務提供者名稱,服務名稱,引數來獲取容器提供的服務
//由三者確定一個唯一的服務。
public Object getService(String beanName,String serviceName,Object... args);
//獲取容器所有服務描述資訊
public IDataContext getBeanDefinitionContext();
//獲取容器中某個服務提供者的描述資訊
public BeanEntity getBeanDefinition(String name);
//獲取容器中所有服務提供者例項的快取
public IDataContext getBeanCacheContext();
//熱載入新的服務提供者
public void initContainerService(String resource);
}
說明:Container採用外觀模式,充當Factory的外觀層。Container可以自定義實現,這是擴充套件點一。而且使用了工廠模式和策略模式實現容器內部具體工廠的動態載入。
(2)Factory介面與預設實現
核心程式碼:
import com.tgb.Entity.BeanEntity;
import com.tgb.data.IDataContext;
import com.tgb.handler.HandlerDecorator;
import java.io.Serializable;
/**
* @Author : JiaLin
* @Email : [email protected]
* @Date : 2014/6/22
* @Description : 抽象的服務工廠,定義處理服務的演算法骨架,具體由其子類實現。
*/
//使用模板方法模式定義演算法的骨架,具體實現有相應的子類去做。
public abstract class AbstractBeanFactory implements IBeanFactory, Serializable {
//----------元件初始化----------begin-----
protected IDataContext beanDefinitionContext;//服務描述資訊的儲存區
protected IDataContext beanCacheContext; //服務提供者例項的快取區
protected HandlerDecorator handlerDecorator;//轉換器(元資料到服務描述資訊)
//設定服務描述資訊的儲存區
public abstract void setBeanDefinitionContext(String beanDefinitionContextName, String resource);
//設定服務提供者例項的快取區
public abstract void setBeanCacheContext(String beanCacheContextName);
//設定轉換器(元資料到服務描述資訊)
public abstract void setHandler(String defaultHandler, String handlerName);
@Override
//模板方法
//註冊服務元件,初始化服務描述資訊
public final void registerBeanDefinition(String resource, String cacheContext, String definitionContext, String defaultHandler, String customHandler) {
this.setHandler(defaultHandler, customHandler);
this.setBeanCacheContext(cacheContext);
this.setBeanDefinitionContext(definitionContext, resource);
}
//----------元件初始化----------end-----
//----------服務提供者的生命週期-----begin--------
//獲取某個服務提供者的服務描述資訊
public abstract BeanEntity getBeanDefinition(String name);
//檢查該服務提供者的例項是否有快取
public abstract boolean containsBeanCache(String name);
//從快取中獲取服務提供者的例項
public abstract Object getBeanCache(String name);
//建立服務提供者
public abstract Object creatBean(BeanEntity beanEntity);
//將服務提供者例項註冊到快取
public abstract Object regiterBeanCache(String name, Object obj);
//----------服務提供者的生命週期-----end--------
@Override
//模板方法
//獲取服務提供者例項
public final Object getBean(String name) {
//獲取某個Bean的定義
BeanEntity beanEntity = getBeanDefinition(name);
//判斷這個Bean是否已經載入到快取,如果有,直接返回
if (containsBeanCache(name)) {
return getBeanCache(name);
}
//建立bean的例項
Object beanObject = this.creatBean(beanEntity);
//註冊到快取
regiterBeanCache(name, beanObject);
//返回bean的例項
return beanObject;
}
//獲取所有的服務描述資訊
public abstract IDataContext getBeanDefinitionContext();
//獲取所有的服務例項快取資訊
public abstract IDataContext getBeanCacheContext();
}
說明:AbstractBeanFactory採用模板方法模式,AbstractBeanFactory中定義了初始化服務定義資訊和獲取服務提供者例項兩個模板方法,定義了演算法的骨架。模板方法依賴的方法的實現交給子類去完成,這裡交給DefaultBeanFactory完成。可以實現自己的BeanFactory,這是擴充套件點二。
(3)DataContext的介面及實現
核心程式碼:
import java.util.Map;
/**
* @Author : JiaLin
* @Email : [email protected]
* @Date : 2014/6/22
* @Description :抽象的資料儲存介面
*/
public interface IDataContext {
public void initData(Map<String,Object> map);
public void set(String name,Object obj);
public Object get(String name);
public Map<String,Object> getAll();
}
說明:IDataContext定義了儲存區域應該有的方法,兩個實現,DefaultBeanDefinitionContext是服務提供者描述資訊儲存的預設實現;DefaultBeanCacheContext是服務提供者例項儲存的預設實現。可以自定義儲存區實現,這是擴充套件點三。
(4)Handler介面及實現
核心程式碼:
import java.util.Map;
/**
* @Author : JiaLin
* @Email : [email protected]
* @Date : 2014/6/23
* @Description : 抽象的處理器裝飾者
*/
//使用裝飾者模式,可以動態新增功能(這裡是動態新增元資料處理器)
//例如要擴充套件Annotation就需要自己擴充套件處理器,或者擴充套件xml處理器等等
public abstract class HandlerDecorator implements IHandler{
protected IHandler handler;
public void setHandler(IHandler handler){
this.handler=handler;
}
@Override
public Map<String, Object> convert(String string) {
if(handler!=null)
{
return handler.convert(string);
}
return null;
}
}
說明:這裡採用了一個裝飾者模式,預設提供了一個註解裝飾者DefaultAnnotationHandler,來把元資料中註解資訊轉換為服務提供者定義資訊。其他開發者可繼承HandlerDecorator,來實現自己的裝飾者,例如把xml資訊轉換為服務提供者定義資訊的Xmlhandler。工廠會依次實現這些裝飾者解析元資料。這是擴充套件點四。
(5)Annotation
核心程式碼:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author : JiaLin
* @Email : [email protected]
* @Date : 2014/6/22
* @Description :用來標註服務的提供者
*/
@Retention(RetentionPolicy.RUNTIME)
@Target( {
ElementType.FIELD,
ElementType.TYPE
})
public @interface Bean {
String name();
}
說明:這裡提供了三個註解來描述服務提供者的元資料,handler據此將元資料轉換為服務提供者定義資訊。其他開發者可擴充套件該Annotation,實現自己的Annotation,然後自定義解析的handler,重寫factory的createBean方法即可。這是擴充套件點五。
(5)定義資料結構,儲存服務定義資訊
核心程式碼:
import java.util.List;
/**
* @Author : JiaLin
* @Email : [email protected]
* @Date : 2014/6/22
* @Description :服務提供者描述資訊載體,其資料結構如下:
*
* BeanEntity
*
* name type List
* @bean註解名 Bean型別 ServicEntity的arrayList
* ServiceEntity……
*
* name value List
* @Service註解名 方法名 ParamEntity的ArrayList
* ParamEntity……
* name value
* @param註解名 引數型別
*/
public class BeanEntity {
private String name; //服務提供者名
private String type; //服務提供者例項的型別
private Object value; //服務提供者例項
private List<ServiceEntity> serviceEntityList; //服務提供者提供的所有服務
public BeanEntity(){}
public BeanEntity(String name,String type)
{
this.name=name;
this.type=type;
}
//省略get,set方法
}
(6)反射Util
說明:這是java反射的工具類,封裝了一些反射常用方法。
(7)熱載入監聽器
說明:這個監聽器,將監聽某個位置,如果加入新的服務提供者元資料,將被容器熱載入。
三、所有程式碼
1、上面介紹了需求,設計,以及核心程式碼,如果依然不過癮,那來看看全部程式碼吧。
我已將這個小專案開源,託管到CSDN CODE 公有專案,專案首頁:
2、大家可以幫著改進,也可以下載檢視,還有很多改進的地方,最近工作繁忙,有空會持續更新,歡迎提出寶貴意見。