再战 Spring IOC!

点击上方 Java后端,选择 设为星标

优质文章,及时送达


前段时间发布了一个 Spring IOC 相关的文章,原来 IOC 这么简单,反响不错。今天再战 Spring IOC ,根据 Spring 源码写一个带有三级缓存的 IOC。

Spring 中的 IOC

Spring 的 IOC 其实很复杂,因为它支持的情况,种类,以及开放的接口,拓展性(如各种PostProcessor)太丰富了。这导致我们在看 Spring 源码的过程中非常吃力,经常点进去一个函数发现很深很深。这篇我主要针对 Spring 的 IOC 中的核心部分,例如 Spring 的 IOC 是如何实现的,Spring 是如何解决循环依赖的这类问题做一个介绍以及一份实现,因为原理是相通的,对于 Spring 对各种情况的逻辑上的处理不做细致的讨论,对原型模式,或是 FactoryBean 类型的 Bean 的不同处理方式不做具体实现。

本文将实现一个怎样的 IOC

  1. 仅支持 Singleton 单例模式的 Bean 管理。(这也是我们在平时项目中最常用的模式)

  2. 仅支持 无参构造器的 Bean 的管理。(这部分如果实现支持有参构造器的也很简单,后续可能会补充)

  3. 仅支持 按照 BeanName 的方式加载 Bean 的方式,如果遇到 Class 的情况,将获取Class 的 SimpleName 后继续按照 BeanName 的方式加载。(这里类似于在 Spring 当中使用 @AutoWaired 按类型匹配不到的情况依然会按照 Name 的方式去匹配)

  4. 支持 自动装配,并完美解决循环依赖问题。

流程设计

基础流程设计

如果不考虑循环依赖的问题,不考虑三级缓存的情况下,实现我们这样一个IOC的功能很简单:

  1. 加载所有的需要被我们管理的 Bean(被 @Component 修饰的类),转换成 Bean 的定义(BeanDefinition,后面会说),存放在 Map 中。

  2. 利用反射得到这些 Bean 的实例,将这些 Bean 的实例存储在我们的容器内。

  3. 填充我们需要自动装配的属性(被 @Resource 修饰的属性)。

完成以上三步,就可以在外层容器调用 getBean() 方法获取 Bean 的实例了。

如何解决循环依赖

网上关于 Spring 如何解决循环依赖的文章很多,简单来说就是利用缓存,先将没有填充属性的对象缓存起来,需要的时候先去用这个对象,不必等待一个对象完整的初始化好。而为什么是三级缓存不是二级缓存呢,这里笼统的来说还是方便 Spring 或者开发者们去拓展一些东西,例如在 Spring Bean 的生命周期中有很多的 Processor,这个我们后续再讲。关于这部分的细节上的逻辑,在后面介绍完三级缓存会有一个很详细的流程图。

三级缓存

三级缓存的实现在代码中的 SingletonBeanRegistry 中:

其中有以下几个核心属性:

  1. singletonObjects:一级缓存,用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用。

  2. earlySingletonObjects:二级缓存,用于存放提前曝光的单例对象的cache,原始的 bean 对象(尚未填充属性)。

  3. singletonFactories:三级缓存,用于存放 bean 工厂对象(ObjectFactory)。三级缓存中用到了 ObjectFactory,这里的 ObjectFactory 可以理解为三级缓存中 Bean 的代理对象,其 getObject() 方法描述了如何获取这个三级缓存的对象。设计成这样除了方便实现三级缓存解决循环依赖,另外也是方便 Spring 在 ObjectFactory 中做一些拓展。

  4. singletonsCurrentlyInCreation:用于存放正在被创建的 Bean 对象。

流程图(重要)

代码设计

BeanFactory(参考自 Spring 的 BeanFactory)

和 Spring 一样,这是 IOC 相关的顶级接口,里面包含了获取 Bean,判断 Bean 是否存在的定义。

public interface BeanFactory {
    Object getBean(String name);
    <T> T getBean(Class<T> requiredType);
    boolean containsBean(String name);
}

SingletonBeanRegistry(参考自 Spring 的 DefaultSingletonBeanRegistry)

单例 Bean 的注册中心,里面包含了所有 Bean 的实例以及所有 Bean 实例的缓存,以及获取单例 Bean 的逻辑,这个类的方法结合 DefaultBeanFactory 中的 getBean() 调用链就是上面流程图的全部内容。

public class SingletonBeanRegistry {
    private static final Object NULL_OBJECT = new Object();
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private final Map<String, Object> earlySingletonObjects = new HashMap<>();
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>());
    protected Object getSingleton(String beanName) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
    protected Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                this.singletonsCurrentlyInCreation.add(beanName);
                singletonObject = singletonFactory.getObject();
                this.singletonsCurrentlyInCreation.remove(beanName);
                addSingleton(beanName, singletonObject);
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
        }
    }
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
            }
        }
    }
    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
        }
    }
    protected boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }
    protected boolean containsSingleton(String name) {
        return this.singletonObjects.containsKey(name);
    }
}

DefaultBeanFactory(参考自 Spring 的 DefaultListableBeanFactory)

BeanFactory 的一个实现,继承了 SingletonBeanRegistry ,同时也作为一个成员变量存在于 ApplicationContext 当中。getBean() 是入口,其调用链为:getBean()->doGetBean()获取Bean如果不存在则创建->doCreateBean()->createBeanInstance()创建 Bean 的实例->populateBean()Bean属性的自动装配。(在 Spring 中多了一步 createBean() 用于实现 AOP,和一步 initalizeBean() 用于执行后置处理器和 init-method,这里我们都暂不实现)

public class DefaultBeanFactory extends SingletonBeanRegistry implements BeanFactory {
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    public void preInstantiateSingletons() {
        this.beanDefinitionMap.forEach((beanName, beanDef) -> {
            getBean(beanName);
        });
    }
    @Override
    public Object getBean(String name) {
        return doGetBean(name);
    }
    private <T> T doGetBean(final String beanName) {
        Object bean;
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null) {
            bean = sharedInstance;
        } else {
            BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
            if (beanDefinition == null) {
                throw new DumpException("can not find the definition of bean '" + beanName + "'");
            }
            bean = getSingleton(beanName, () -> {
                try {
                    return doCreateBean(beanName, beanDefinition);
                } catch (Exception ex) {
                    removeSingleton(beanName);
                    throw ex;
                }
            });
        }
        return (T) bean;
    }
    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
        Object bean = createBeanInstance(beanName, beanDefinition);
        boolean earlySingletonExposure = isSingletonCurrentlyInCreation(beanName);
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, () -> bean);
        }
        Object exposedObject = bean;
        populateBean(beanName, beanDefinition, bean);
        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName);
            if (earlySingletonReference != null) {
                exposedObject = earlySingletonReference;
            }
        }
        return exposedObject;
    }
    private Object createBeanInstance(String beanName, BeanDefinition beanDefinition) {
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?> constructorToUse;
        if (beanClass.isInterface()) {
            throw new DumpException("Specified class '" + beanName + "' is an interface");
        }
        try {
            constructorToUse = beanClass.getDeclaredConstructor((Class<?>[]) null);
            return BeanUtils.instantiateClass(constructorToUse);
        } catch (Exception e) {
            throw new DumpException("'" + beanName + "',No default constructor found", e);
        }
    }
    private void populateBean(String beanName, BeanDefinition beanDefinition, Object beanInstance) {
        Field[] beanFields = beanDefinition.getBeanClass().getDeclaredFields();
        try {
            for (Field field : beanFields) {
                if (field.getAnnotation(Resource.class) == null) {
                    continue;
                }
                if (!containsBean(field.getName())) {
                    throw new DumpException("'@Resource' for field '" + field.getClass().getName() + "' can not find");
                }
                field.setAccessible(true);
                field.set(beanInstance, getBean(field.getName()));
            }
        } catch (Exception e) {
            throw new DumpException("populateBean '" + beanName + "' error", e);
        }
    }
    private boolean containsBeanDefinition(String name) {
        return beanDefinitionMap.containsKey(name);
    }
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<T> requiredType) {
        return (T) getBean(StringUtils.lowerFirst(requiredType.getSimpleName()));
    }
    @Override
    public boolean containsBean(String name) {
        return this.containsSingleton(name) || containsBeanDefinition(name);
    }
}

ApplicationContext

应用的最外层容器,利用内部的 DefaultBeanFactory 对象实现了 BeanFactory。在new ApplicationContext()时,会执行读取所有的 Bean 转化成 BeanDefinition,并对所有的 BeanDefinition 执行 getBean() 获取所有 Bean 的实例,存放在 SingletonBeanRegistry 当中。在 ApplicationContext 中调用 getBean() 其实就是调用 DefaultBeanFactory 中的 getBean()。

public class ApplicationContext implements BeanFactory {
    private DefaultBeanFactory beanFactory = new DefaultBeanFactory();
    public ApplicationContext() {
        loadBeanDefinitions(beanFactory);
        finishBeanFactoryInitialization(beanFactory);
    }
    private void loadBeanDefinitions(DefaultBeanFactory beanFactory) {
        ComponentBeanReader beanReader = new ComponentBeanReader();
        beanReader.readBeanDefinition(beanFactory);
    }
    public void finishBeanFactoryInitialization(DefaultBeanFactory beanFactory) {
        beanFactory.preInstantiateSingletons();
    }
    @Override
    public Object getBean(String name) {
        return getBeanFactory().getBean(name);
    }
    @Override
    public <T> T getBean(Class<T> requiredType) {
        return getBeanFactory().getBean(requiredType);
    }
    @Override
    public boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }
    public DefaultBeanFactory getBeanFactory() {
        return beanFactory;
    }
}

BeanDefinition(参考自 Spring 的 BeanDefinition)

Bean 的描述,理论上应包含很多 Bean 的信息,但目前的实现只存了一个该 Bean 的 Class。

public class BeanDefinition {
    private volatile Class<?> beanClass;
    public Class<?> getBeanClass() {
        return beanClass;
    }
    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }
}

ComponentBeanReader(参考自 Spring 的 XmlBeanDefinitionReader)

用于初始化 ApplicationContext 时,读取所有的 Bean,转化为 BeanDefinition。

public class ComponentBeanReader {
    public void readBeanDefinition(DefaultBeanFactory beanFactory) {
        Set<Class<?>> componentSet = ReflectionUtils.getAllClass(Component.class);
        componentSet.forEach((componentClass) -> {
            BeanDefinition beanDefinition = new BeanDefinition();
            String beanName = componentClass.getAnnotation(Component.class).value();
            if ("".equals(beanName)) {
                beanName = StringUtils.lowerFirst(componentClass.getSimpleName());
            }
            beanDefinition.setBeanClass(componentClass);
            beanFactory.registerBeanDefinition(beanName, beanDefinition);
        });
    }
}

测试

@Component
class A{
    @Resource
    private B b;
@Component
class A{
    @Resource
    private B b;
    public void setB(B b) {
        this.b = b;
    }
    public B getB() {
        return b;
    }
}
@Component
class B{
    @Resource
    private A a;
    public void setA(A a) {
        this.a = a;
    }
    public A getA() {
        return a;
    }
}
@Component
class C{
    
    @Resource
    private A a;
    @Resource
    B b;
    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }
}
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ApplicationContext();
        A a = context.getBean(A.class);
        B b = context.getBean(B.class);
        C c = (C)context.getBean("c");
        System.out.println(a.getB());
        System.out.println(b.getA());
        System.out.println(c.getA());
        System.out.println(c.getB());
    }
}

最后

以上就是对 Spring 中单例 Bean 管理的一个简单实现,代码中比较难懂的部分是三级缓存的部分,对于三级缓存的详细流程和介绍其实全部都在上面的流程图里,如果看懂了流程图再看代码就会觉得很简单了。

同时这部分代码也会作为我实现的一个 web 框架 Dump 的一部分:Dump - A lightweight web framework:https://github.com/yuanguangxin/Dump

最后附上关于这部分实现的完整代码:

https://github.com/yuanguangxin/Dump/tree/master/src/main/java/group/dump/beans

作者

本文作者「袁广鑫」,欢迎关注作者的知乎:https://www.zhihu.com/people/yuan-yan-xin-53 专注于 Java 技术分享,点击阅读原文即可关注。


-END-

如果看到这里,说明你喜欢这篇文章,请 转发、点赞。同时 标星(置顶)本公众号可以第一时间接受到博文推送。

1. 老板!用 float 存储金额为什么要扣我工资?

2. 小白也能读懂的 Docker 入门教程

3. 再见,Eclipse!

4. 卧槽!原来 IOC 这么简单

最近整理一份面试资料《Java技术栈学习手册》,覆盖了Java技术、面试题精选、Spring全家桶、Nginx、SSM、微服务、数据库、数据结构、架构等等。

获取方式:点“ 在看,关注公众号 Java后端 并回复 777 领取,更多内容陆续奉上。

喜欢文章,点个在看 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值