自己動手寫一個ioc容器
阿新 • • 發佈:2019-02-06
控制反轉(Inversion of Control,縮寫為IoC),是面向物件程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查詢”(Dependency Lookup)。通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體,將其所依賴的物件的引用傳遞給它。也可以說,依賴被注入到物件中,我們常用的spring框架核心就是一個ioc容器。
1.首先定義一個ioc的介面
package pers.dwl.sardine.ioc; /** * ioc 容器介面 * @author dwl * */ public interface Ioc { /** * 獲取例項物件 * @param clazz * @return 例項物件 */ <T> T getEntity(Class<T> clazz); /** * ioc中該是否有例項物件 * @param clazz * @return boolean */ boolean hasEntity(Class<?> clazz); /** * 將容器登出 */ void depose(); }
2.傳統的bean都是配置在xml中,不過現在也流行通過註解來注入,所以我們定義兩個註解。
@Inject 表示屬性需要由ioc來注入
package pers.dwl.sardine.ioc.annotation; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * 標識需要注入的屬性 * @author dwl * */ @Documented @Retention(RUNTIME) @Target(FIELD) public @interface Inject { Class<?> value() default String.class; }
@IocBean表示該類交給ioc管理
package pers.dwl.sardine.ioc.annotation; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * 定義bean類 -標識該類交給IOC管理 * @author dwl * */ @Documented @Retention(RUNTIME) @Target(TYPE) public @interface IocBean { }
3.實現ioc介面,完成依賴注入的功能
package pers.dwl.sardine.ioc.impl;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import pers.dwl.sardine.ioc.Ioc;
import pers.dwl.sardine.ioc.annotation.Inject;
import pers.dwl.sardine.ioc.annotation.IocBean;
import pers.dwl.sardine.util.ArrayUtil;
import pers.dwl.sardine.util.ClassHelper;
/**
* ioc實現類
* @author dwl
*
*/
public class SineIoc implements Ioc {
private static final Object lock = new Object();
/**
* 例項物件集合-未完成注入
*/
private static Map<Class<?>, Object> beanMap = new HashMap<Class<?>, Object>();
/**
* 例項物件集合-已完成注入(第一次呼叫時注入)
*/
private static Map<Class<?>, Object> injectMap = new HashMap<Class<?>, Object>();
//初始化
static{
//獲取基礎包下所有class
List<Class<?>> classes = ClassHelper.getClassList();
classes.forEach(clazz->{
if(clazz.isAnnotationPresent(IocBean.class)){
try {
//加入例項物件集合
beanMap.put(clazz, clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
});
}
@SuppressWarnings("unchecked")
public <T> T getEntity(Class<T> clazz) {
if(!injectMap.containsKey(clazz)){
if(!beanMap.containsKey(clazz)){
throw new RuntimeException("沒有該示例物件 - "+clazz);
}else{
synchronized (lock) {
if(!injectMap.containsKey(clazz))
setFiled(clazz);//依賴注入,加入快取
}
}
}
return (T)injectMap.get(clazz);
}
public boolean hasEntity(Class<?> clazz) {
return injectMap.containsKey(clazz);
}
public void depose() {
beanMap.clear();
injectMap.clear();
}
/**
* 給屬性賦值-依賴注入
* @param clazz
*/
public void setFiled(Class<?> clazz){
//獲取所有屬性
Field[] fields = clazz.getDeclaredFields();
//獲取例項物件
Object instance = beanMap.get(clazz);
//有屬性
if(!ArrayUtil.isEmpty(fields)){
for(Field field:fields){
//有待注入的屬性
if(field.isAnnotationPresent(Inject.class)){
//獲取屬性上的inject資訊
Inject inject = field.getAnnotation(Inject.class);
Class<?> type = inject.value();
//預設String.class,表示該屬性型別是類不是介面
if(type.isAssignableFrom(String.class)){
Class<?> implClass = field.getType();
//為屬性物件例項進行依賴注入,不考慮迴圈依賴
setFiled(implClass);
//從injectMap獲取已經注入好的屬性物件例項
Object implBean =injectMap.get(implClass);
field.setAccessible(true);
try {
//為屬性賦值
field.set(instance, implBean);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}else{//為介面時value要宣告具體實現類
setFiled(type);//為屬性物件例項進行依賴注入,不考慮迴圈依賴
//從injectMap獲取已經注入好的屬性物件例項
Object implBean =injectMap.get(type);
field.setAccessible(true);
try {
//為屬性賦值
field.set(instance, implBean);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
injectMap.put(clazz, instance);//加入快取
}
}
在獲取物件例項的時有需要注入的屬性會先例項該屬性物件,使用遞迴從下至上依次例項化,但是沒有考慮迴圈依賴的情況,如果程式碼中不注意寫出了迴圈依賴的話會產生死迴圈。
4.測試 我們寫個兩層依賴試一試效果。 MyService依賴MyService2 ,MyAction依賴MyService。
MyService2只有一個方法
package pers.dwl.sardine.ioc.test;
import pers.dwl.sardine.ioc.annotation.IocBean;
@IocBean
public class Myservice2 {
public void doSome(){
System.out.println("----------注入啦------------");
}
}
MyService依賴MyService2
package pers.dwl.sardine.ioc.test;
import pers.dwl.sardine.ioc.annotation.Inject;
import pers.dwl.sardine.ioc.annotation.IocBean;
@IocBean
public class MyService {
@Inject
private Myservice2 myservice2;
public void doSome(){
myservice2.doSome();
}
}
MyAction依賴MyService
package pers.dwl.sardine.ioc.test;
import pers.dwl.sardine.ioc.annotation.Inject;
import pers.dwl.sardine.ioc.annotation.IocBean;
@IocBean
public class MyAction {
@Inject
private MyService myService;
public void doSome(){
myService.doSome();
}
}
寫個main方法測試效果
package pers.dwl.sardine.ioc.test;
import pers.dwl.sardine.ioc.Ioc;
import pers.dwl.sardine.ioc.impl.SineIoc;
public class test {
public static void main(String[] args) {
Ioc ioc= new SineIoc();
MyAction myBean = ioc.getEntity(MyAction.class);
myBean.doSome();
}
}
控制檯效果