自己动手开发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 公有项目,项目首页:

https://code.csdn.net/shan9liang/ioccontainer

2、大家可以帮着改进,也可以下载查看,还有很多改进的地方,最近工作繁忙,有空会持续更新,欢迎提出宝贵意见。

自己动手开发IOC容器

时间: 2024-10-19 13:22:26

自己动手开发IOC容器的相关文章

JAVAWEB开发之Spring详解之——Spring的入门以及IOC容器装配Bean(xml和注解的方式)、Spring整合web开发、整合Junit4测试

Spring框架学习路线 Spring的IOC Spring的AOP,AspectJ Spring的事务管理,三大框架的整合 Spring框架概述 什么是Spring? Spring是分层的JavaSE/EE full-stack(一站式)轻量级开源框架. 所谓分层: SUN提供的EE的三层结构:web层.业务层.数据访问层(也称持久层,集成层). Struts2是web层基于MVC设计模式框架. Hibernate是持久的一个ORM的框架. 所谓一站式:Spring框架有对三层的每层解决方案.

Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器

第2章.IoC容器 IoC容器概述 abstract: 介绍IoC和bean的用处和使用 IoC容器处于整个Spring框架中比较核心的位置:Core Container: Beans, Core, Context, SpEL 为上层AOP/Aspects/Instrumentation/Messaging提供支持 IoC容器的用途: 创建对象的依赖,最后组装成所需的业务对象 容器通过业务对象和配置(application-context.xml; xxxController.java)-->生

SpringMVC中IOC容器

1.Spring作用: 1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!] 2.将其他组件粘合在一起 3.IOC容器和AOP Spring的Ioc机制(控制反转和依赖注入)正是用在此处. Spring的Ioc(控制反转和依赖注入) 控制反转[Ioc]:就是由容器控制程序之间的(依赖)关系,而非传统实现中,由程序代码直接操控. 控制反转是一种思想,其具体实现就是依赖注入! 依赖注入[DI]:组件之间的依赖关系由容器在运行期决定 ,由容器动

对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解

.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合,并由此引申出IoC.DI以及Ioc容器等概念. 2.意图 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本. 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节

深入理解DIP、IoC、DI以及IoC容器

摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学习这些概念,并理清他们之间微妙的关系. 目录 前言 依赖倒置原则(DIP) 控制反转(IoC) 依赖注入(DI) IoC容器 总结 前言 对于大部分小菜来说,当听到大牛们高谈DIP.IoC.DI以及IoC容器等名词时,有没有瞬间石化的感觉?其实,这些"高大上"的名词,理解起来也并不是那么的

Spring 学习笔记(二)—— IOC 容器(BeanFactory)

使用Spring IoC容器后,容器会自动对被管理对象进行初始化并完成对象之间的依赖关系的维护,在被管理对象中无须调用Spring的API. 为了实现IoC功能,Spring提供了两个访问接口: org.springframework.beans.factory.BeanFactory org.springframework.context.ApplicationContext 前者为Bean工厂,借助于配置文件能够实现对JavaBean的配置和管理,用于向使用者提供Bean的实例: 后者为Ap

Ioc容器Autofac系列(1)-- 初窥

前言 第一次接触Autofac是因为CMS系统--Orchard,后来在一个开源爬虫系统--NCrawler中也碰到过,随着深入了解,我越发觉得Ioc容器是Web开发中必不可少的利器.那么,Ioc容器是用来做什么的?用了有什么好处?我相信如果不明白这两点就很难敞开心扉接受Ioc容器. 传统解耦设计的弊端 为方便描述,举个日志的栗子.我简化实现,一个Log类,一个SaveLog方法.如果其他类想拥有记日志功能,那么只需在内部包含一个Log类型的变量: public class Log  {    

Ioc容器Autofac系列(1)-- 初窥(转)

前言 第一次接触Autofac是因为CMS系统--Orchard,后来在一个开源爬虫系统--NCrawler中也碰到过,随着深入了解,我越发觉得Ioc容器是Web开发中必不可少的利器.那么,Ioc容器是用来做什么的?用了有什么好处?我相信如果不明白这两点就很难敞开心扉接受Ioc容器. 传统解耦设计的弊端 为方便描述,举个日志的栗子.我简化实现,一个Log类,一个SaveLog方法.如果其他类想拥有记日志功能,那么只需在内部包含一个Log类型的变量: 双击代码全选 1 2 3 4 5 6 7 8

Spring(一)--作用、IOC容器细节、搭配环境、Spring实验 (转)

1.Spring作用:      1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!]      2.将其他组件粘合在一起      比如将SpringMVC和Mybaits连在一起      3.包括:IOC容器和AOP[面向切面编程]           Spring的IOC机制(控制反转和依赖注入)正是用在此处.           Spring的IOC(控制反转和依赖注入)                控制反转[IOC]:就是由