手把手带你写一个 MiniSpring
郭屹
前 Sun Microsystems Java 研发工程师,开源软件 MiniSpring、MiniTomcat 开发者
6170 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 26 讲
手把手带你写一个 MiniSpring
15
15
1.0x
00:00/00:00
登录|注册

02|扩展Bean:如何配置constructor、property和init-method?

你好,我是郭屹。
上节课,我们初步实现了一个 MiniSpring 框架,它很原始也很简单。我们实现了一个 BeanFactory,作为一个容器对 Bean 进行管理,我们还定义了数据源接口 Resource,可以将多种数据源注入 Bean。
这节课,我们继续增强 IoC 容器,我们要做的主要有 3 点。
增加单例 Bean 的接口定义,然后把所有的 Bean 默认为单例模式。
预留事件监听的接口,方便后续进一步解耦代码逻辑。
扩展 BeanDefinition,添加一些属性,现在它只有 id 和 class 两个属性,我们要进一步地丰富它。

构建单例的 Bean

首先我们来看看如何构建单例的 Bean,并对该 Bean 进行管理。
单例(Singleton)是指某个类在整个系统内只有唯一的对象实例。只要能达到这个目的,采用什么技术手段都是可以的。常用的实现单例的方式有不下五种,因为我们构建单例的目的是深入理解 Spring 框架,所以我们会按照 Spring 的实现方式来做。
为了和 Spring 框架内的方法名保持一致,我们把 BeanFactory 接口中定义的 registryBeanDefinition 方法修改为 registryBean,参数修改为 beanName 与 obj。其中,obj 为 Object 类,指代与 beanName 对应的 Bean 的信息。你可以看下修改后的 BeanFactory。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入讲解了如何在IoC容器中配置constructor、property和init-method,以及如何增加事件监听。文章首先介绍了构建单例的Bean,并对该Bean进行管理,引入了SingletonBeanRegistry接口和DefaultSingletonBeanRegistry实现类。接着,详细讨论了属性注入的三种方式:Field注入、Setter注入和构造器注入,并给出了相应的XML配置和Java代码示例。此外,还对BeanDefinition的属性进行了扩展,新增了lazyInit、dependsOn、initMethodName等属性,以及构造器参数和property列表。同时,还提前为构造器注入、Setter注入提供了基本的实例类,为后续实现依赖注入方式提供了基础。通过对原始IoC容器的扩展和丰富,使其越来越像Spring框架。整体而言,本文内容涵盖了IoC容器的增强和属性注入的配置与实现方式,适合对IoC容器有一定了解的读者学习参考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 MiniSpring》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(39)

  • 最新
  • 精选
  • 姐姐
    这节课的类开始爆炸了,一开始看得很没头绪,但是俗话说“书读百遍,其义自见”,俗话很有道理,最后决定从simpleBeanFactory和ClassPathXmlApplicationContext两个角度,总结一下自己的理解和感悟,当做是自己的笔记:1. 从SimpleBeanFactory的角度看,首先理理三个接口BeanFactory、BeanDefinitionRegistry、SingletonBeanRegistry,对于这三个接口,其中BeanFactory、BeanDefinitionRegistry是由SimpleBeanFactory直接实现的,而对于SingletonBeanRegistry,SimpleBeanFactory继承了它的实现类DefaultSingletonBeanRegistry,起初我很疑惑,为什么SimpleBeanFactory不同时声明实现SingletonBeanRegistry并且继承它的默认实现类呢,但是后来想想也许SimpleBeanFactory对外只希望外界知道自己是一个beanFactory和beanDefinitionRegistry,至于singletonBeanRegistry,它只希望作为一种内部的能力来使用,所以继承一个已经实现的类来拥有能力,但是声明接口的时候不声明这个接口。理清了这三个以后,再来看看内部的细节逻辑,SimpleBeanFactory的registerBeanDefinition方法中每注册一个beanDefinition,如果不是懒加载的就立刻调用getBean,而getBean方法会从SimpleBeanFactory继承的DefaultSingletonBeanRegistry能力中判断bean是否存,不存在创建并注册进DefaultSingletonBeanRegistry。2.ClassPathXmlApplicationContext 它的组装逻辑和上一节一样,但是现在XmlBeanDefinitionReader在遍历resource向simplebeanfactory注册的时候,由于simplebeanfactory注册时候会创建非懒bean,所以现在applicationContext启动的时候就会创建所有非懒加载bean,上一节课的容器创建完并不会创建bean,要到获取bean的时候才创建bean,对getBean方法的调用提前到注册beanDefinition的时候了;ClassPathXmlApplicationContext实现了ApplicationEventPublisher,所以可以猜测以后容器不仅是容器,还兼具发布事件的功能。 这节课最大收获是加深了对实现接口和继承类的理解,如果一个类声明它实现了某个接口,那么它偏向于告诉外部它是那个接口,你可以把它当成那个接口来用,如果一个类继承了某个实现类,这时候也可以把它当成这个实现类来用,但是我想它更偏向于获得该实现类的能力,如果它既想获得能力又想对外提供能力,那么它可以同时声明实现接口和继承接口的某些实现类,再自己修改增强某些方法。

    作者回复: 赞!你的笔记好好整理,可以对大家分享了。

    2023-03-23归属地:浙江
    4
    30
  • 咕噜咕噜
    构造器注入:适合于强制依赖,适合在创建对象的同时必须要初始化的变量。但是要注入的依赖多了可能构造器会相对臃肿;循环依赖问题无法有效解决,会在启动的时候报错。 setter注入:适合于可选依赖,当没有提供它们时,类应该能够正常工作。相对更加灵活,可以多次调用,循环依赖问题spring可以通过三级缓存解决。

    作者回复: 赞一个

    2023-03-17归属地:上海
    10
  • 聪聪不匆匆
    老师可以将每一章的各个类之间UML关系提供一下吗 方便学习缕清楚创建过程 和 各个组件依赖关系 谢谢老师

    作者回复: 好建议。课程发布完之后,我们增强一下。

    2023-03-16归属地:上海
    2
    7
  • 爱学习的王呱呱
    老师好,有点没理解 synchronized + ConcurrentHashMap 同时使用的意义。 我理解HashMap在多线程下的问题有两个 1. 不同key但是相同hashcode会造成元素覆盖;2. 死循环。但是ConcurrentHashMap不存在这个问题了,为什么还需要synchronized呢。

    作者回复: 这个问题好也难,我试着说一下我的思考。 Java并发容器ConcurrentHashMap,它只是保证容器本身的并发控制,对同一个bucket,会保证存取时的原子性和并发控制。但是,它只是这个容器层面的,扩大到业务逻辑,可能由多个操作组成,业务操作上对同一个key值的业务数据在同时读写,这个业务逻辑层面的并发控制仍然还是要由我们自己来控制的。 最简单的控制就是直接在业务操作上加上synchronized,这样只有一个维护线程,SpringCache的sync属性也是这么处理的。但是这样并发性又降低了,相当于整个操作都排他了。用ReentrantReadWriteLock也不行,这只是读写分开了,整个锁的范围仍然是整个数据而不是某个key值。 所以实际工程中需要考虑颗粒度更加精细的加锁:只对同一个key加锁,key之间互不影响。可以这么考虑: 除了ConcurrentHashMap存储数据之外,再用一个ConcurrentHashMap单独存储锁,如<key, Object>,(注意:Java中的锁时所在对象上的,所以我们随便用一个Object就可以当锁)。进行业务操作的时候,从locl map中先拿锁,然后用synchronized(lock){}将业务操作包起来保证并发安全和原子性。 分布式事务Seata的一段代码可以借鉴: 在NettyClientChannelManager类中,定义了数据及锁的map: private final ConcurrentMap<String, Object> channelLocks = new ConcurrentHashMap<>(); private final ConcurrentMap<String, Channel> channels = new ConcurrentHashMap<>(); 然后有一个方法这么写的: void releaseChannel(Channel channel, String serverAddress) { if (channel == null || serverAddress == null) { return; } try { synchronized (channelLocks.get(serverAddress)) { Channel ch = channels.get(serverAddress); if (ch == null) { nettyClientKeyPool.returnObject(poolKeyMap.get(serverAddress), channel); return; } if (ch.compareTo(channel) == 0) { if (LOGGER.isInfoEnabled()) { LOGGER.info("return to pool, rm channel:{}", channel); } destroyChannel(serverAddress, channel); } else { nettyClientKeyPool.returnObject(poolKeyMap.get(serverAddress), channel); } } } catch (Exception exx) { LOGGER.error(exx.getMessage()); } } 关键是注意这两行: synchronized (channelLocks.get(serverAddress)) { Channel ch = channels.get(serverAddress); 先从锁map中获取锁,在这个细对象上synchronized,然后再获取数据。 这样的控制就精细了。

    2023-06-05归属地:北京
    7
    4
  • Geek_e298ce
    有一个问题不太明白, 为什么要有BeanFactory和SingletonBeanRegistry这两个接口呢

    作者回复: 角色分离。一个是工厂,另一个是仓库。

    2023-04-02归属地:北京
    3
    4
  • 陈小远
    这节课在内容上能看懂,但是在编码实操的时候有些难受——老师的源码都是完成品,而课件中并没有给出满足当前进度的可运行代码,一些迭代性的改动也并没有顾及会对其它部分实现的影响,比如BeanFactory中registerBean突然没有了,导致跟进课程实操方面有点打脑壳

    作者回复: Github上有十几个分支,差不多是跟每一节可以对上的。你不要只看master分支,那个是最终的样子。具体到你这一部分,可以看ioc分支。好多人不注意Github上有多个分支,我很意外。

    2023-03-18归属地:四川
    2
    3
  • 故作
    感觉代码真的贴的不是很用心,看专栏里的代码,会觉得有些东西莫名其妙,以至于一头雾水,然后去看git上的代码,发现是没有这一看不懂的内容的。蛮气愤的,明明开篇词里说每一小步都是可运行的,结果,从这一章开始,就不行了。能理解无法把所有代码都放里边,但是,因为开篇词里的这句话,导致白白浪费了很多时间。这一章,质量真的很低,很不用心

    作者回复: 感谢指教。github的docs目录下有一个readme文件,里面有说明29天里一天天的演变,说的每一小步都能运行是指这个。后来出这个专栏的时候,因为有篇幅的硬性要求,强行合并了章节,为了外在形式确实造成了文本,逻辑和代码有某些对不上的地方。建议仔细看github代码,坚持一下,看完的人大多数反馈还是觉得很有收获的,当年线下给合肥工大和中南大学本科生研究生讲课的时候,大家也是觉得收获蛮大的。“牢骚太盛防肠断,风物长宜放眼量。莫道昆明池水浅,观鱼胜过富春江。”

    2023-06-30归属地:浙江
    2
    2
  • __@Wong
    看了两节,这个课程对于缺少基础的人来说有一定的难度。本课程对学习者来说需要对Spring, 设计模式准则以及常用设计模式有一定的基础,并有一定代码设计功底。如果想要通过本课程学习企业Spring开发,不太建议,不如看Spring的书籍更来得有效。如果想要学习下Spring的设计,本课程非常适用。

    作者回复: 是这样的。就是这个定位。

    2023-05-13归属地:广东
    2
  • 未聞花名
    老师贴代码可以保留下包名,或者脑图里的类可以带上包名,这样可以按着思路写,不用去翻github的代码了

    作者回复: 后面会加上的。感谢建议。

    2023-03-20归属地:北京
    2
  • KernelStone
    我找到的一条主线是这样的。beans.xml -> AServiceImpl -> ArgumentValue & PropertyValue -> ArgumentValues & PropertyValues -> BeanDefinition -> BeanDefinitionRegistry -> XmlBeanDefinitionReader -> BeanFactory -> SingletonBeanRegistry -> DefaultSingletonBeanRegistry -> SimpleBeanFactory。主要还是围绕着扩充Bean定义,属性注入去完成的,这个顺序看下来比较好理解。其余的就是ApplicationEvent +ApplicationEventPublisher预留事件监听机制的扩展点、和重新组装ClassPathXmlApplicationContext。 当然同学们评论很精彩,学到了一些代码细节。再次感谢。

    作者回复: 用心了!

    2023-05-24归属地:广东
    1
收起评论
显示
设置
留言
39
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部