动手编写一个IOC容器

来源:互联网 发布:c语言中变量的定义 编辑:程序博客网 时间:2024/05/21 10:41

动手编写一个IOC容器

标签(空格分隔): 未分类


  • 动手编写一个IOC容器
      • 什么是IOC
      • IOC容器工作过程
      • 编写代码
      • 总结

什么是IOC

IOC(Inversion of control)控制反转,表示将原本由调用者自己实例化被调用类改成有容器来统一实例化调用类并且注入到调用者内部的过程。
以打牌为例,之前玩家需要自己摸牌,现在改成由发牌人为所有玩家发牌,这个过程就叫做“控制反转”,发牌人就是IOC容器,发牌人给玩家发牌的过程就被称为“依赖注入(Dependency Injection)”。

//Player自己负责实例化Cardpublic Class Player{    private Card myCard=new Card();    ...}//实例化在IOC容器(Dealer)中进行public Class Dealer{    private Card card=new Card();    ...}public Class Player{    @Inject    private Card myCard;}

IOC容器工作过程

其实依赖注入的含义还是比较好理解的,乍一看觉得高深吧~

那么IOC容器怎么来实现依赖注入呢,这里提供一个比较简单的实现方案:
我们将所有被IOC容器管理的类称为Bean,在项目启动时需要扫描当前所有的java Bean并放入到一个BeanMap中,并且将他们实例化,BeanMap保存BeanClass和Bean对象的对应关系。然后遍历BeanMap中的Bean,逐个判断Bean的成员变量中是否有@Inject注解,如果有,就从BeanMap中取出这个成员变量类对应的对象,注入给这个Bean。

Created with Raphaël 2.1.0加载package下的所有Class过滤IOC容器需要Bean Class实例化所有Bean,并保存到BeanMapIOC容器遍历BeanMap当前Bean是否有被注解标注的成员变量Field从BeanMap中取Field类的对像,注入Bean 遍历结束yesno

编写代码

根据上面的流程我们需要定义这几个工具类:
ClassUtil:扫描项目package下的所有Class
ClassHelper: 调用ClassUtil,并返回需要的Bean
ReflectionUtil: 返回对象实例,调用Settter方法实现注入
BeanHelper: 初始化BeanMap

代码参考《架构探险》一书

*ClassUtil:
需要通过ClassLoader获得项目的所有资源,分别查找文件和jar包中的类*

public static Set<Class<?>> getClassSet(String packageName) {        Set<Class<?>> classSet = new HashSet<Class<?>>();        try {            Enumeration<URL> urls = getClassLoader().getResources(                    packageName.replace(".", "/"));            while (urls.hasMoreElements()) {                URL url = urls.nextElement();                if (url != null) {                    String protocol = url.getProtocol();                    if (protocol.equals("file")) {                        String packagePath = url.getPath().replaceAll("%20", " ");                        addClassFromFile(classSet, packagePath, packageName);                    } else if (protocol.equals("jar")) {                        addClassFromJar(classSet, url);                    }                }            }        } catch (Exception e) {            log.error("get class set failed", e);            throw new RuntimeException(e);        }        return classSet;    }

*BeanHelper:
在初始化时立即将所有bean实例化,放入BeanMap中*

private static final Map<Class<?>,Object> BEAN_MAP=new HashMap<Class<?>, Object>();    static {        Set<Class<?>> beanClass=ClassHelper.getBeanClassSet();        for(Class<?> cls:beanClass){            Object obj=ReflectionUtil.getInstance(cls);            BEAN_MAP.put(cls,obj);        }    }

之后就可以在IOCHelper中实现依赖注入了。

public class IOCHelper {    static{        Map<Class<?>,Object> beanMap=BeanHelper.getBeanMap();        if(!CollectionsUtil.isEmpty(beanMap)){            for(Map.Entry<Class<?>,Object> beanEntry:beanMap.entrySet()){                //实例化对象由容器统一管理                Class<?> beanClass=beanEntry.getKey();                Object beanInstance=beanEntry.getValue();                Field[] fields=beanClass.getDeclaredFields();                if(fields.length!=0){                    for(Field beanField:fields){                    //判断是否需要注入                        if(beanField.isAnnotationPresent(Inject.class)){                            Class<?> beanFieldClass=beanField.getType();                            Object beanFieldInstance=beanMap.get(beanFieldClass);                            if(beanFieldInstance!=null){                                ReflectionUtil.setField(beanInstance,beanField,beanFieldInstance);                            }                        }                    }                }            }        }    }}

测试:
定义一个Service

@Servicepublic class LoginService {    public LoginService(){}    public void login(){        System.out.println("login...");    }}

再定义一个Controller,依赖于Service

@Controllerpublic class IndexController {    @Inject    private LoginService loginService;    public void login(){        System.out.println("call login method");        loginService.login();    }}

测试IOCHelper,是否注入了Service(@service,@Controller标记的类会被装配成Bean)

public class Start {    public static void main(String[] arg){        ClassUtil.loadClass(IOCHelper.class.getName(), true);        IndexController controller= BeanHelper.getBean(IndexController.class);        controller.login();    }}

总结

到这里相信你已经了解了IOC容器的大概工作方式(,这里的IOCHelper还存在一些不足:
1. 只支持Bean的属性注入,没有支持方法或者构造函数。
2. BeanMap中所有的Class都是单例的,每次getBean得到的都是同一实例。
完整代码地址

0 0