初稿地址:https://www.xilidou.com/2018/01/08/spring-ioc/

Spring 作为 J2ee
开发事实上的正儿八经,是每个Java开发人员都亟待了解之框架。但是Spring 的 IoC
和 Aop
的性状,对于初级的Java开发人员来说要于为难给明的。所以自己就是想写一系列之稿子给大家讲解这些特色。从而能够更深入摸底
Spring 框架。

读了这首文章,你以见面了解:

  • 什么是依注入和操纵反转
  • Ioc有啊用
  • Spring的 Ioc 是怎落实之
  • 照Spring的思路开发一个概括的Ioc框架

IoC 是什么?

wiki百科的解说是:

支配反转(Inversion of
Control,缩写为IoC),是面向对象编程中的相同栽设计条件,可以就此来削弱低计算机代码之间的耦合度。其中最为广泛的法门叫做依赖注入(Dependency
Injection,简称DI)。通过控制反转,对象在受创造的下,由一个调控体系外所有目标的外界实体,将该所因之靶子的援传递让它。也得以说,依赖让注入到对象中。

Ioc 有啊用?

关押了上面的解说你势必没明白什么是
Ioc,因为是率先次等看见上面的讲话也觉得云里雾里。

唯独经过地方的描述我们好约的询问及,使用IoC的目的是为解耦。也就是说IoC
是解耦的平栽艺术。

咱俩知晓Java 是相同派别面向对象的语言,在 Java 中 Everything is
Object,我们的程序即使是出于多靶成的。当我们的门类更大,合作的开发者更是多之时光,我们的切近即见面更加多,类与类似里的援就会化为指数级的提高。如下图所示:

混乱.png

如此这般的工简直就是是难,如果我们引入 Ioc
框架。由框架来维护类的生命周期和类之间的使用。我们的系即见面成为这样:

IoC.png

以此时节咱们发现,我们好像中的关系还是因为 IoC
框架负责维护类的生命周期,同时以类似注入到需要的好像中。也尽管是近乎的使用者只负责利用,而不负责掩护。把正规化的事体交给专业的框架来就。大大的压缩支出的复杂度。

故一个像样比较来喻这个题目。Ioc
框架就是咱活遭之房屋中介,首先中介会收集市场及的房源,分别与一一房源的二房东建立关系。当我们需要租房的时段,并不需要我们四处寻找各类租房信息。我们直接搜索房屋中介,中介就会依据你的急需提供相应的房屋信息。大大提升了租房的效率,减少了而及各项房东之间的关系次数。

Spring 的 IoC 是怎落实的

问询Spring框架最直接的法门就是看Spring的源码。但是Spring的代码抽象的层次很高,且处理的底细深高。对于多数人数的话不是无与伦比好理解。我念了Spirng的源码以后以自之知做一个总,Spirng
IoC 主要是以下几单步骤。

1. 初始化 IoC 容器。
2. 读取配置文件。
3. 将配置文件转换为容器识别对的数据结构(这个数据结构在Spring中叫做 BeanDefinition) 
4. 利用数据结构依次实例化相应的对象
5. 注入对象之间的依赖关系

祥和实现一个IoC框架

为好,我们参考 Spirng 的 IoC
实现,去除所有和基本原理无关的逻辑。极简的落实 IoC 的框架。 项目应用
json 作为配置文件。使用 maven 管理 jar 包的倚重。

每当是框架中我们的目标还是单例的,并无支持Spirng的多作用域。框架的贯彻采用了cglib
和 Java 的照。项目中自我还动用了 lombok 用来简化代码。

下面我们就是来修 IoC 框架吧。

首先我们看这个框架的中坚构造:

structure.jpg

自从本上考察一下夫框架,包含了3只package、在包 bean
中定义了咱们框架的数据结构。core 是咱框架的主干逻辑所在。utils
是局部通用工具类。接下来我们就相继讲解一下:

1. bean 定义了框架的数据结构

BeanDefinition 是我们种之着力数据结构。用于描述我们用 IoC
框架管理的靶子。

@Data
@ToString
public class BeanDefinition {

    private String name;

    private String className;

    private String interfaceName;

    private List<ConstructorArg> constructorArgs;

    private List<PropertyArg> propertyArgs;

}

含蓄了目标的
name,class的名号。如果是接口的贯彻,还有该目标实现之接口。以及构造函数的传参的列表
constructorArgs 和需注入的参数列表 propertyArgs

2. 再次看我们的家伙类包里面的对象:

ClassUtils 负责处理 Java 类的加载,代码如下:

public class ClassUtils {
    public static ClassLoader getDefultClassLoader(){
        return Thread.currentThread().getContextClassLoader();
    }
    public static Class loadClass(String className){
        try {
            return getDefultClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

咱俩才写了一个方,就是经过 className 这个参数获取对象的 Class。

BeanUtils 负责处理对象的实例化,这里我们下了 cglib
这个家伙确保,代码如下:

public class BeanUtils {
    public static <T> T instanceByCglib(Class<T> clz,Constructor ctr,Object[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clz);
        enhancer.setCallback(NoOp.INSTANCE);
        if(ctr == null){
            return (T) enhancer.create();
        }else {
            return (T) enhancer.create(ctr.getParameterTypes(),args);
        }
    }
}

ReflectionUtils 主要透过 Java 的照原理来就目标的靠注入:

public class ReflectionUtils {

    public static void injectField(Field field,Object obj,Object value) throws IllegalAccessException {
        if(field != null) {
            field.setAccessible(true);
            field.set(obj, value);
        }
    }
}

injectField(Field field,Object obj,Object value)
这个点子的意向就是,设置 obj 的 field 为 value。

JsonUtils 的意就是是为着解析我们的json配置文件。代码比较丰富,与我们的
IoC 原理关系不大,感兴趣之同班可以自行从github上下载代码看看。

起了当时几乎独趁手的家伙,我们尽管可以开始成功 Ioc 框架的基本代码了。

3. 着力逻辑

自之 IoC 框架,目前单支持一种 ByName 的注入。所以我们的 BeanFactory
就不过来一个智:

public interface BeanFactory {
    Object getBean(String name) throws Exception;
}

接下来我们落实了是法:

public class BeanFactoryImpl implements BeanFactory{

    private static final ConcurrentHashMap<String,Object> beanMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String,BeanDefinition> beanDefineMap= new ConcurrentHashMap<>();

    private static final Set<String> beanNameSet = Collections.synchronizedSet(new HashSet<>());

    @Override
    public Object getBean(String name) throws Exception {
        //查找对象是否已经实例化过
        Object bean = beanMap.get(name);
        if(bean != null){
            return bean;
        }
        //如果没有实例化,那就需要调用createBean来创建对象
        bean =  createBean(beanDefineMap.get(name));

        if(bean != null) {

            //对象创建成功以后,注入对象需要的参数
            populatebean(bean);

            //再把对象存入Map中方便下次使用。
            beanMap.put(name,bean;
        }

        //结束返回
        return bean;
    }

    protected void registerBean(String name, BeanDefinition bd){
        beanDefineMap.put(name,bd);
        beanNameSet.add(name);
    }

    private Object createBean(BeanDefinition beanDefinition) throws Exception {
        String beanName = beanDefinition.getClassName();
        Class clz = ClassUtils.loadClass(beanName);
        if(clz == null) {
            throw new Exception("can not find bean by beanName");
        }
        List<ConstructorArg> constructorArgs = beanDefinition.getConstructorArgs();
        if(constructorArgs != null && !constructorArgs.isEmpty()){
            List<Object> objects = new ArrayList<>();
            for (ConstructorArg constructorArg : constructorArgs) {
                objects.add(getBean(constructorArg.getRef()));
            }
            return BeanUtils.instanceByCglib(clz,clz.getConstructor(),objects.toArray());
        }else {
            return BeanUtils.instanceByCglib(clz,null,null);
        }
    }

    private void populatebean(Object bean) throws Exception {
        Field[] fields = bean.getClass().getSuperclass().getDeclaredFields();
        if (fields != null && fields.length > 0) {
            for (Field field : fields) {
                String beanName = field.getName();
                beanName = StringUtils.uncapitalize(beanName);
                if (beanNameSet.contains(field.getName())) {
                    Object fieldBean = getBean(beanName);
                    if (fieldBean != null) {
                        ReflectionUtils.injectField(field,bean,fieldBean);
                    }
                }
            }
        }
    }
}

第一我们看来于 BeanFactory 的落实着。我们出少数 HashMa:,beanMap 和
beanDefineMap。 beanDefineMap
存储的是目标的名号及对象对应的数据结构的映照。beanMap 用于保存
beanName和实例化之后的目标。

容器初始化的早晚,会调用 BeanFactoryImpl.registerBean 方法。把 对象的
BeanDefination 数据结构,存储起来。

当我们调用 getBean() 的不二法门的时段。会优先到 beanMap
里面找,有没来实例化好之目标。如果无,就会去beanDefineMap查找这目标对应之
BeanDefination。再采取DeanDefination去实例化一个目标。

靶实例化成功后,我们还欲注入相应的参数,调用
populatebean()斯点子。在 populateBean
这个点子被,会扫描对象中的Field,如果目标中之 Field
是我们IoC容器管理的对象,那就会见调用 我们上文实现的
ReflectionUtils.injectField来注入对象。

全体准备妥当后,我们对象就形成了全套 IoC 流程。最后是目标放入
beanMap 中,方便下一样赖采用。

因此我们得以理解 BeanFactory 是管制暨转移对象的地方。

4. 容器

咱所谓的器皿XML,就是本着BeanFactory的扩充,负责管理
BeanFactory。我们的是IoC 框架下 Json
作为配置文件,所以我们容器就命名吧
JsonApplicationContext。当然之后您肯促成 XML
作为配置文件的器皿你就算得好写一个
XmlApplicationContext,如果根据注解的器皿就可叫AnnotationApplcationContext。这些实现留个大家去做到。

咱看 ApplicationContext 的代码:

public class JsonApplicationContext extends BeanFactoryImpl{
    private String fileName;
    public JsonApplicationContext(String fileName) {
        this.fileName = fileName;
    }
    public void init(){
        loadFile();
    }
    private void loadFile(){
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        List<BeanDefinition> beanDefinitions = JsonUtils.readValue(is,new TypeReference<List<BeanDefinition>>(){});
        if(beanDefinitions != null && !beanDefinitions.isEmpty()) {
            for (BeanDefinition beanDefinition : beanDefinitions) {
                registerBean(beanDefinition.getName(), beanDefinition);
            }
        }
    }
}

夫容器的用意就是 读取配置文件。将部署文件转换为容器能够掌握的
BeanDefination。然后使 registerBean 方法。注册之目标。

迄今为止,一个简练版的 IoC 框架就得。

5. 框架的使用

咱们形容一个测试类来探望我们这个框架怎么使:

率先我们发出三独对象

public class Hand {
    public void waveHand(){
        System.out.println("挥一挥手");
    }
}

public class Mouth {
    public void speak(){
        System.out.println("say hello world");
    }
}

public class Robot {
    //需要注入 hand 和 mouth 
    private Hand hand;
    private Mouth mouth;

    public void show(){
        hand.waveHand();
        mouth.speak();
    }
}

咱得也咱的 Robot 机器人注入 hand 和 mouth。

部署文件:

[
  {
    "name":"robot",
    "className":"com.xilidou.framework.ioc.entity.Robot"
  },
  {
    "name":"hand",
    "className":"com.xilidou.framework.ioc.entity.Hand"
  },
  {
    "name":"mouth",
    "className":"com.xilidou.framework.ioc.entity.Mouth"
  }
]

此时段写一个测试类:

public class Test {
    public static void main(String[] args) throws Exception {
        JsonApplicationContext applicationContext = new JsonApplicationContext("application.json");
        applicationContext.init();
        Robot aiRobot = (Robot) applicationContext.getBean("robot");
        aiRobot.show();
    }
}

运转之后输出:

挥一挥手
say hello world

Process finished with exit code 0

得看到我们成功的吃自身之 aiRobot 注入了 hand 和 mouth。

迄今为止我们 Ioc 框架开发形成。

总结

就首文章读了之后相信您肯定为落实了一个简短的 IoC 框架。

尽管如此说读源码是摸底框架的末段手段。但是 Spring
框架当作一个产框架,为了保险通用和安乐,源码必定是惊人抽象,且处理大量细节。所以
Spring 的源码阅读起来或相当困难。希望马上篇稿子会拉了解 Spring Ioc
的贯彻。

下同样篇稿子 应该会是 《徒手撸框架–实现AOP》。

github
地址:https://github.com/diaozxin007/xilidou-framework

相关文章

网站地图xml地图