理解 Spring:IOC 的原理及手动实现

沙海 2021年5月23日09:43:49Java评论16字数 9773阅读32分34秒阅读模式
摘要

速读摘要

速读摘要文章源自JAVA秀-https://www.javaxiu.com/25005.html

也是几乎所有Java工作者必须要掌握的框架之一,其优秀的设计思想以及其代码实现上的艺术也是我们需要掌握的。有鉴于此,我这里先对Spring中的几个重要的模块进行一个手动的简易实现,一是熟悉这些模块的原理,同时也是仿造Spring中的结构来对后面阅读源码打下基础。我们定义一个Bean工厂来存放bean,在需要的时候从bean工厂中取即可,bean工厂对外提供的也仅仅是一个获取bean的方法即可,由于bean的类型不定,所以返回值定位Object。文章源自JAVA秀-https://www.javaxiu.com/25005.html

原文约 6064 | 图片 10 | 建议阅读 13 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现

点击关注 ? Java基基 文章源自JAVA秀-https://www.javaxiu.com/25005.html

收录于话题文章源自JAVA秀-https://www.javaxiu.com/25005.html

#Java基基文章源自JAVA秀-https://www.javaxiu.com/25005.html

98个文章源自JAVA秀-https://www.javaxiu.com/25005.html

点击上方“Java基基”,选择“设为星标”文章源自JAVA秀-https://www.javaxiu.com/25005.html

做积极的人,而不是积极废人!文章源自JAVA秀-https://www.javaxiu.com/25005.html

文章源自JAVA秀-https://www.javaxiu.com/25005.html

源码精品专栏文章源自JAVA秀-https://www.javaxiu.com/25005.html

 文章源自JAVA秀-https://www.javaxiu.com/25005.html

文章源自JAVA秀-https://www.javaxiu.com/25005.html

来源:juejin.im/post/5c11b1e06fb9a04a0d56b787文章源自JAVA秀-https://www.javaxiu.com/25005.html

大家好,我是基基!文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

导语

Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。也是几乎所有Java工作者必须要掌握的框架之一,其优秀的设计思想以及其代码实现上的艺术也是我们需要掌握的。文章源自JAVA秀-https://www.javaxiu.com/25005.html

要学习Spring,除了在我们的项目中使用之外,也需要对它的源码进行研读,但是Spring的实现涵盖的知识很多,在加上其中的类的数量也是非常的多,在我们阅读源码时可能会在几十个类之间穿插阅读,很有可能一不小心就导致思维混乱。文章源自JAVA秀-https://www.javaxiu.com/25005.html

有鉴于此,我这里先对Spring中的几个重要的模块进行一个手动的简易实现,一是熟悉这些模块的原理,同时也是仿造Spring中的结构来对后面阅读源码打下基础。文章源自JAVA秀-https://www.javaxiu.com/25005.html

IOC(Inversion of Control)

Inversion of Control即控制反转,其意思是将我们之前由客户端代码来创建的对象交由IOC容器来进行控制,对象的创建,初始化以及后面的管理都由IOC完成。文章源自JAVA秀-https://www.javaxiu.com/25005.html

IOC的好处

解耦: IOC的出现解决了类与类之间的耦合,我们在Web开发的Servlet时代,如果一个Servlet需要依赖另一个类的某些实现,那么我们需要在当前类对依赖的类进行创建和初始化,如果其他类也依赖了这个类,那也需要进行创建和初始化,而交给了IOC来管理的话,那么在需要的时候只需向IOC进行申请,而不需要重复的创建和初始化。当然,IOC也允许每次都重新创建一个新的对象。文章源自JAVA秀-https://www.javaxiu.com/25005.html

方便与AOP进行配合: AOP也是一个使用十分频繁的功能,通过IOC可以十分方便的与AOP进行配合。文章源自JAVA秀-https://www.javaxiu.com/25005.html

IOC中设计的设计模式

工厂模式。IOC容器来负责创建管理类实例对象,在需要时向IOC进行申请,从IOC中获取。所以IOC容器也称为bean工厂。文章源自JAVA秀-https://www.javaxiu.com/25005.html

工厂模式是一种比较简单易懂的设计模式,这里就不在介绍了,如果有需要的可以看看工厂模式。文章源自JAVA秀-https://www.javaxiu.com/25005.html

IOC的手动实现

Bean定义

IOC的主要的功能便是对Bean进行管理,包括创建、初始化、管理以及销毁的工作。首先我们面对的问题就是我们怎么让IOC能够创建一个Bean?为了创建Bean我们需要提供一些什么?文章源自JAVA秀-https://www.javaxiu.com/25005.html

如何创建Bean

在不手动通过new关键字创建的情况下创建类实例的对象方法有两种:文章源自JAVA秀-https://www.javaxiu.com/25005.html

  • 反射:通过反射的方法可以创建类的实例:clazz.getClass().newInstance();。文章源自JAVA秀-https://www.javaxiu.com/25005.html

  • 工厂模式:工厂模式可以让我们在不接触实例类的情况下创建出实例。文章源自JAVA秀-https://www.javaxiu.com/25005.html

public class PersonFactory{    public Person getPerson(){        return new Person();    }}

为了创建Bean我们需要提供什么

通过分析上面的两种方法可以轻松得出答案。文章源自JAVA秀-https://www.javaxiu.com/25005.html

对于反射的方式我们仅需提供实例的Class对象。文章源自JAVA秀-https://www.javaxiu.com/25005.html

对于工厂方法我们需要提供的就是创建该类的工厂名(factoryName)和方法名(methodName);文章源自JAVA秀-https://www.javaxiu.com/25005.html

除了创建bean还需要做些什么

IOC容器是对bean的整个生命周期进行管理,除了创建之外还需要对bean进行初始化,以及不需要时对bean进行销毁的工作(如释放资源等)。所以我们还需要提供初始化和销毁等操作。文章源自JAVA秀-https://www.javaxiu.com/25005.html

到这里创建bean需要的基本分析完了,看类图:文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

Bean工厂

Bean的定义解决了,但是这个bean定义以及创建好的Bean实例放在哪里呢,我们需要一个统一的地方来存放这些东西以方便我们要用的时候方便取。文章源自JAVA秀-https://www.javaxiu.com/25005.html

我们定义一个Bean工厂来存放bean,在需要的时候从bean工厂中取即可,bean工厂对外提供的也仅仅是一个获取bean的方法即可,由于bean的类型不定,所以返回值定位Object。文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

注册Bean定义

到了现在我们有了创建bean的Bean定义,有了存放和管理bean的Bean工厂,现在需要考虑的是怎么来联系这两个类,我们还需要另外一个接口,接口的功能是让我们能注册和获取bean定义,这里我们通过beanName来区分不同的bean。文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

代码实现

到这里我们实现一个简易的IOC容器的需要的东西基本准备完成了,看下基本类图:文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

基本代码实现:文章源自JAVA秀-https://www.javaxiu.com/25005.html

DefaultBeanDefinition:

public class DefaultBeanDefinition implements BeanDefinition{    private Class<?> clazz;    private String beanFactoryName;    private String createBeanMethodName;    private String staticCreateBeanMethodName;    private String beanInitMethodName;    private String beanDestoryMethodName;    private boolean isSingleton;    // setter    public void setSingleton(boolean singleton) {        isSingleton = singleton;    }    @Override    public Class<?> getBeanClass() {        return this.clazz;    }    @Override    public String getBeanFactory() {        return this.beanFactoryName;    }    @Override    public String getCreateBeanMethod() {        return this.createBeanMethodName;    }    @Override    public String getStaticCreateBeanMethod() {        return this.staticCreateBeanMethodName;    }    @Override    public String getBeanInitMethodName() {        return this.beanInitMethodName;    }    @Override    public String getBeanDestoryMethodName() {        return this.beanDestoryMethodName;    }    @Override    public String getScope() {        return this.isSingleton?BeanDefinition.SINGLETION :BeanDefinition.PROTOTYPE;    }    @Override    public boolean isSingleton() {        return this.isSingleton;    }    @Override    public boolean isPrototype() {        return !this.isSingleton;    }}

DefaultBeanFactory:

public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {    private Log log = LogFactory.getLog(this.getClass());    //ConcurrentHashMap应对并发环境    private Map<String, BeanDefinition> bdMap = new ConcurrentHashMap<>();    private Map<String, Object> beanMap = new ConcurrentHashMap<>();    @Override    public void register(BeanDefinition bd, String beanName) {        Assert.assertNotNull("beanName不能为空 beanName", beanName);        Assert.assertNotNull("BeanDefinition不能为空", bd);        if(bdMap.containsKey(beanName)){            log.info("[" + beanName + "]已经存在");        }        if(!bd.validate()){            log.info("BeanDefinition不合法");        }        if(!bdMap.containsKey(beanName)){            bdMap.put(beanName, bd);        }    }    @Override    public boolean containsBeanDefinition(String beanName) {        return bdMap.containsKey(beanName);    }    @Override    public BeanDefinition getBeanDefinition(String beanName) {        if(!bdMap.containsKey(beanName)){            log.info("[" + beanName + "]不存在");        }        return bdMap.get(beanName);    }    public Object doGetBean(String beanName) throws InstantiationException, IllegalAccessException {        if(!beanMap.containsKey(beanName)){            log.info("[" + beanName + "]不存在");        }        Object instance = beanMap.get(beanName);        if(instance != null){            return instance;        }        //不存在则进行创建        if(!this.bdMap.containsKey(beanName)){            log.info("不存在名为:[" + beanName + "]的bean定义");        }        BeanDefinition bd = this.bdMap.get(beanName);        Class<?> beanClass = bd.getBeanClass();        if(beanClass != null){            instance = createBeanByConstruct(beanClass);            if(instance == null){                instance = createBeanByStaticFactoryMethod(bd);            }        }else if(instance == null && StringUtils.isNotBlank(bd.getStaticCreateBeanMethod())){            instance = createBeanByFactoryMethod(bd);        }        this.doInit(bd, instance);        if(instance != null && bd.isSingleton()){            beanMap.put(beanName, instance);        }        return instance;    }    private void doInit(BeanDefinition bd, Object instance) {        Class<?> beanClass = instance.getClass();        if(StringUtils.isNotBlank(bd.getBeanInitMethodName())){            try {                Method method = beanClass.getMethod(bd.getBeanInitMethodName(), null);                method.invoke(instance, null);            } catch (Exception e) {                e.printStackTrace();            }        }    }    /**     * 构造方法创建实例     * @param beanClass     * @return     */    private Object createBeanByConstruct(Class<?> beanClass) {        Object instance = null;        try {            instance = beanClass.newInstance();        } catch (Exception e) {            e.printStackTrace();        }        return instance;    }    /**     * 普通工厂方法创建实例     * @param bd     * @return     */    private Object createBeanByFactoryMethod(BeanDefinition bd) {        Object instance = null;        try {            //获取工厂类            Object factory = doGetBean(bd.getBeanFactory());            //获取创建实例的方法            Method method = factory.getClass().getMethod(bd.getCreateBeanMethod());            //执行方法            instance = method.invoke(factory, null);        } catch (Exception e) {            e.printStackTrace();        }        return instance;    }    /**     * 静态方法创建实例     * @param bd     * @return     */    private Object createBeanByStaticFactoryMethod(BeanDefinition bd) {        Object instance = null;        try {            Class<?> beanClass = bd.getBeanClass();            //获取创建实例的方法            Method method = beanClass.getMethod(bd.getStaticCreateBeanMethod());            instance = method.invoke(beanClass, null);        } catch (Exception e) {            e.printStackTrace();        }        return instance;    }    @Override    public Object getBean(String beanName) {        if(!beanMap.containsKey(beanName)){            log.info("[" + beanName + "]不存在");        }        return beanMap.get(beanName);    }    @Override    public void close() throws IOException {        Set<Map.Entry<String, BeanDefinition>> entries = bdMap.entrySet();        for(Map.Entry<String, BeanDefinition>  entry: entries){            BeanDefinition value = entry.getValue();            String destoryMethodName = value.getBeanDestoryMethodName();            try {                Method method = value.getBeanClass().getMethod(destoryMethodName, null);                method.invoke(value.getBeanClass(), null);            } catch (Exception e) {                e.printStackTrace();            }        }    }}

简单测试一下:实例bean:文章源自JAVA秀-https://www.javaxiu.com/25005.html

public class User {    private String name;    private int age;    //getter setter    public void init(){        System.out.println("init...");    }    public void destory(){        System.out.println("destory...");    }}

工厂类:

public class TestFactory {    public Object createMethod(){        return new User();    }    public static Object staticCreateMethod(){        return new User();    }}

测试类:

public class MySpringTest {    static DefaultBeanFactory factory = new DefaultBeanFactory();    @Test    public void test() throws IllegalAccessException, InstantiationException {        DefaultBeanDefinition bd = new DefaultBeanDefinition();        bd.setClazz(User.class);        bd.setSingleton(true);        bd.setBeanFactoryName("TestFactory");        bd.setCreateBeanMethodName("createMethod");        bd.setStaticCreateBeanMethodName("staticCreateMethod");        bd.setBeanInitMethodName("init");        factory.register(bd, "user");        System.out.println(factory.doGetBean("user"));    }}

小结

一个简易的容器就这样实现了,当然我们这里只是具备了基本的功能,实际上还差的远,比如带参数的bean的实例化等功能。但是IOC的基本原理已经表达出来了,后面我们只需在这个基础上添加新的功能即可。文章源自JAVA秀-https://www.javaxiu.com/25005.html

文章源自JAVA秀-https://www.javaxiu.com/25005.html

欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

已在知识星球更新源码解析如下:文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

理解 Spring:IOC 的原理及手动实现文章源自JAVA秀-https://www.javaxiu.com/25005.html

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。文章源自JAVA秀-https://www.javaxiu.com/25005.html

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。文章源自JAVA秀-https://www.javaxiu.com/25005.html

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。文章源自JAVA秀-https://www.javaxiu.com/25005.html

文章源自JAVA秀-https://www.javaxiu.com/25005.html

文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)
文章源自JAVA秀-https://www.javaxiu.com/25005.html

阅读原文文章源自JAVA秀-https://www.javaxiu.com/25005.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定