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

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 理解Spring——IOC的原理及手动实现

点击上方“Java知音”,选择“置顶公众号”

技术文章第一时间送达!

作者:宁愿 juejin.im/post/5c11b1e06fb9a04a0d56b787

juejin.im/post/5c11b1e06fb9a04a0d56b787

导语

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

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

有鉴于此,我这里先对Spring中的几个重要的模块进行一个手动的简易实现,一是熟悉这些模块的原理,同时也是仿造Spring中的结构来对后面阅读源码打下基础。

IOC(Inversion of Control)

Inversion of Control即控制反转,其意思是将我们之前由客户端代码来创建的对象交由IOC容器来进行控制,对象的创建,初始化以及后面的管理都由IOC完成。

IOC的好处

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

方便与AOP进行配合:AOP也是一个使用十分频繁的功能,通过IOC可以十分方便的与AOP进行配合。

IOC中设计的设计模式

工厂模式。IOC容器来负责创建管理类实例对象,在需要时向IOC进行申请,从IOC中获取。所以IOC容器也称为bean工厂。

工厂模式是一种比较简单易懂的,这里就不在介绍了,如果有需要的可以看看工厂模式。

IOC的手动实现

Bean定义

IOC的主要的功能便是对Bean进行管理,包括创建、初始化、管理以及销毁的工作。首先我们面对的问题就是我们怎么让IOC能够创建一个Bean?为了创建Bean我们需要提供一些什么?

如何创建Bean

在不手动通过new关键字创建的情况下创建类实例的对象方法有两种:

  • 反射:通过反射的方法可以创建类的实例:clazz.getClass().newInstance();。
  • 工厂模式:工厂模式可以让我们在不接触实例类的情况下创建出实例。
  • 
    public class PersonFactory{
        public Person getPerson(){
            return new Person();
        }
    }
    

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

    通过分析上面的两种方法可以轻松得出答案。

    对于反射的方式我们仅需提供实例的Class对象。

    对于工厂方法我们需要提供的就是创建该类的工厂名(factoryName)和方法名(methodName);

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

    IOC容器是对bean的整个生命周期进行管理,除了创建之外还需要对bean进行初始化,以及不需要时对bean进行销毁的工作(如释放资源等)。所以我们还需要提供初始化和销毁等操作。

    到这里创建bean需要的基本分析完了,看类图:

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

    Bean工厂

    Bean的定义解决了,但是这个bean定义以及创建好的Bean实例放在哪里呢,我们需要一个统一的地方来存放这些东西以方便我们要用的时候方便取。

    我们定义一个Bean工厂来存放bean,在需要的时候从bean工厂中取即可,bean工厂对外提供的也仅仅是一个获取bean的方法即可,由于bean的类型不定,所以返回值定位Object。

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

    注册Bean定义

    到了现在我们有了创建bean的Bean定义,有了存放和管理bean的Bean工厂,现在需要考虑的是怎么来联系这两个类,我们还需要另外一个接口,接口的功能是让我们能注册和获取bean定义,这里我们通过beanName来区分不同的bean。参考:

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

    代码实现

    到这里我们实现一个简易的IOC容器的需要的东西基本准备完成了,看下基本类图:

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

    基本代码实现:

    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 MapString, BeanDefinition bdMap = new ConcurrentHashMap();
    
        private MapString, 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 {
            SetMap.EntryString, BeanDefinition entries = bdMap.entrySet();
            for(Map.EntryString, 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:

    
    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的基本原理已经表达出来了,后面我们只需在这个基础上添加新的功能即可。

    END

    Java面试题专栏

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

    让我知道你 “在看理解Spring:IOC的原理及手动实现

    原文始发于微信公众号(Java知音):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    转载声明:转载请注明出处,本技术博客是本人原创文章

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 理解Spring——IOC的原理及手动实现


     上一篇
    Spring怎么在一个事务中开启另一个事务 Spring怎么在一个事务中开启另一个事务
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 作者:Mazin my.oschina.net/u/3441184/blog/893628 my.oschina.net/u/3441184/blog/8
    2021-04-05
    下一篇 
    Spring AOP 使用注解进行校验实例(valid) Spring AOP 使用注解进行校验实例(valid)
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 作者:CatalpaFlat juejin.im/post/5a6301e051882573315c769d juejin.im/post/5a6301e
    2021-04-05