• 手写一个简单的Spring容器(原理+源码)


    原理

    手写一个简单的Spring容器,用配置文件方式,在配置文件中写入我们想要的bean,bean实例被用到时注册(注册到仓库,或者说缓存),后续我们就可以直接根据bean的名字拿到这个bean的单例。

    在这里插入图片描述

    Spring容器,就是Spring帮忙管理类(JavaBean),我们先写一个简单的单例容器,Bean的配置写在配置文件(beans.properties)中,格式是:

    <beanName>:<fullClassName>
    
    • 1

    要根据名字获取一个bean,就需要用到工厂,当工厂被实例化时,就会用资源加载器,去反射创建bean(也就是beanDefinition),然后存入beanDefinitionMap。

    beanDefinition不是一个普通的类,而是一个有beanName和ClassName的类,有了这两项才好被用来创建(反射)和管理(配合注册机)bean的使用实例。

    通过资源加载创建的beanDefinition先放置到beanDefinitionMap(一个临时的bean表),每次getBean(通过beanName)的时候,如果注册机已经注册了这个bean,那么直接从注册机获取,否则从beanDefinitionMap中取出beanDefinition,根据beanDefinition的内容反射创建实例,并通过工厂内部的BeanRegister注册机(本质也是一个HashMap)进行注册。(甚至可以理解为缓存)

    代码

    bean配置

    beans.properties
    这里边只有一个bean的beanName:fullClassName

    userDao:UserDao
    
    • 1

    bean工厂

    BeanFactory.java

    import java.util.*;
    
    public class BeanFactory {
    
        private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    
        private BeanRegister beanRegister;
    
        public BeanFactory() {
            //创建bean注册器
            beanRegister = new BeanRegister();
            //加载资源
            this.beanDefinitionMap = new ResourceLoader().getResource();
        }
    
        /**
         * 获取bean
         *
         * @param beanName bean名称
         * @return
         */
        public Object getBean(String beanName) {
            //从bean缓存中取
            Object bean = beanRegister.getSingletonBean(beanName);
            if (bean != null) {
                return bean;
            }
            //根据bean定义,创建bean
            return createBean(beanDefinitionMap.get(beanName));
        }
    
        /**
         * 创建Bean
         *
         * @param beanDefinition bean定义
         * @return
         */
        private Object createBean(BeanDefinition beanDefinition) {
            try {
                Object bean = beanDefinition.getBeanClass().newInstance();
                //缓存bean
                beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
                return bean;
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    注册机

    BeanRegister.java
    一个HashMap实现单例注册

    import java.util.*;
    
    public class BeanRegister {
        //单例Bean缓存
        private Map<String, Object> singletonMap = new HashMap<>(32);
    
        /**
         * 获取单例Bean
         *
         * @param beanName bean名称
         * @return
         */
        public Object getSingletonBean(String beanName) {
            return singletonMap.get(beanName);
        }
    
        /**
         * 注册单例bean
         *
         * @param beanName
         * @param bean
         */
        public void registerSingletonBean(String beanName, Object bean) {
            if (singletonMap.containsKey(beanName)) {
                return;
            }
            singletonMap.put(beanName, bean);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    资源加载

    ResourceLoader.java

    import java.util.*;
    import java.io.*;
    
    public class ResourceLoader {
        public static Map<String, BeanDefinition> getResource() {
            Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
            Properties properties = new Properties();
            try {
                InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
                properties.load(inputStream);
                Iterator<String> it = properties.stringPropertyNames().iterator();
                while (it.hasNext()) {
                    String key = it.next();
                    String className = properties.getProperty(key);
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setBeanName(key);
                    Class clazz = Class.forName(className);
                    beanDefinition.setBeanClass(clazz);
                    beanDefinitionMap.put(key, beanDefinition);
                }
                inputStream.close();
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
            return beanDefinitionMap;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    Bean定义

    BeanDefinition.java

    public class BeanDefinition {
    
        private String beanName;
    
        private Class beanClass;
        //省略getter、setter
    
    
        public String getBeanName() {
            return beanName;
        }
    
        public void setBeanName(String beanName) {
            this.beanName = beanName;
        }
    
        public Class getBeanClass() {
            return beanClass;
        }
    
        public void setBeanClass(Class beanClass) {
            this.beanClass = beanClass;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Bean原型

    UserDao.java

    public class UserDao {
    
        public void queryUserInfo(){
            System.out.println("A good man.");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试

    ApiTest.java

    public class ApiTest {
    
        public void test_BeanFactory() {
            //1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)
            BeanFactory beanFactory = new BeanFactory();
    
            //2.第一次获取bean(通过反射创建bean,缓存bean)
            UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
            userDao1.queryUserInfo();
    
            //3.第二次获取bean(从缓存中获取bean)
            UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
            userDao2.queryUserInfo();
        }
    
        public static void main(String[] args) {
            new ApiTest().test_BeanFactory();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    参考:
    程序员鱼皮《五分钟,手撸一个Spring容器!》

  • 相关阅读:
    区间贪心-
    第十六章 源代码文件 REST API 教程(一)
    【Rust日报】2022-09-16 Cloudflare放弃了Nginx,转而使用Rust编写的Pingora
    JNI中调用Java函数
    第9章 Apache-Dbutils实现CRUD操作
    HSTS(HTTP 严格传输安全)
    MySQL远程连接
    卫龙辣条第三次冲刺上市:业绩增速下滑,刘卫平、刘福平提前套现
    数据结构与算法基础(王卓)(2)
    claims, ok := token.Claims.(*CustomClaims)
  • 原文地址:https://blog.csdn.net/GDUT_xin/article/details/125556055