原稿地址: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. 容器

俺们所谓的器皿,就是本着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地图