动手编写一个IOC容器

动手编写一个IOC容器

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


什么是IOC

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

//Player自己负责实例化Card
public 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,并保存到BeanMap IOC容器遍历BeanMap 当前Bean是否有被注解标注的成员变量Field 从BeanMap中取Field类的对像,注入Bean 遍历结束 yes no

编写代码

根据上面的流程我们需要定义这几个工具类:
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

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

再定义一个Controller,依赖于Service

@Controller
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值