在上一篇的末尾,我们提到了 dubbo
的 spi
中增加了 IoC
和 AOP
的功能.那么本篇就讲一下这个增加的 IoC
, spi
部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之前都讲了什么.
从Dubbo内核聊聊双亲委派机制 主要讲了spi的基本概念,简单的入门,并以 spi
为线索讲解了 双亲委托模式
的弊端以及解决方案
Dubbo和JDK的SPI究竟有何区别? 主要以 dubbo改进了jdk的spi
为线索,重点讲分析问题的思路,从实际案例实战 从哪里着手分析问题
这个大家最喜欢问的问题.
提到 IoC
,大家第一个想到的就是 Spring
,所以Spring的 IoC
也是本篇的一大重点内容.当然毕竟主题是 dubbo
,所以对于 Spring
内容,将与dubbo结合,以短小精干,但是又不缺乏深度的介绍.(后期如果大家有需要,也可以开展Spring源码专题)
看到这里可能有同学就会问,肥朝你不是写dubbo源码解析的吗,为什么还要讲Spring呢?dubbo中涉及到很多的边缘知识,其中包括 Spring
、 Netty
、 Zookeeper
等等,我希望的是,大家能通过学习dubbo为主线,全面综合的提高自己,而不是为了看源码而看源码,也不是为了面试而看源码.
spi
也增加了 IoC
,那你先讲讲Spring的 IoC
,然后再讲讲dubbo里面又是怎么做的 Spirng的IoC容器主要有两种,即 BeanFactory
和 ApplicationContext
BeanFactory
是Spring中最底层的接口,只提供了最简单的 IoC
功能,负责配置,创建和管理bean. ApplicationContext
继承了 BeanFactory
,拥有了基本的 IoC
功能外,还支持
另外 ApplicationContext
在加载的时候就会创建所有的bean(Web应用推荐), BeanFactory
需要等到拿bean的时候才会创建bean(桌面应用推荐).所以,我们一般使用 ApplicationContext
实现IoC的过程,总体可以分为两步,如下图
该阶段相当于"根据图纸装配成生产线",也就是对象管理信息的收集.
该阶段相当于"根据生产线来生产具体产品"
Spring提供了 BeanFactoryPostProcessor
的容器拓展机制,该机制允许我们在容器实例化相应对象 之前 ,对注册到容器的 BeanDefinition
所保存的信息做相应的修改.
那我们有哪些实际场景有运用到这个 拓展
呢?
比如我们配置数据库信息,经常用到占位符
${jdbc.url} 复制代码
当 BeanFactory
在第一阶段加载完成所有配置信息时,保存的对象的属性信息还只是以占位符的形式存在.这个解析的工作是在 PropertySourcesPlaceholderConfigurer
中做的,我们来看看继承体系图就明白了.
PropertySourcesPlaceholderConfigurer
实现了该接口,在进入第二阶段时,已经把占位符信息替换完成.
温馨提示:
细心的同学可能发现 PropertySourcesPlaceholderConfigurer
和 PropertyPlaceholderConfigurer
名字好像,这两个有什么区别?我们来看源码
为何我反复强调 基础
、 原理
,难道是为了骗你关注一下我,多一个粉丝?因为基础扎实,明白原理之后很多东西真的是一通百通的,尤其了 Spring Boot
简化了配置,很多问题就更考验基础了.我演示一个简单的问题
首先我们发现这个占位符没有被解析,如果不知道原理你可能一脸懵逼,但是看过肥朝公众号的你,知道是缺少 PropertySourcesPlaceholderConfigurer
于是你高高兴兴补上了 PropertySourcesPlaceholderConfigurer
,发现有坑,占位符是解析出来了,但是却是 null
然后你通过私信联系到了肥朝,肥朝告诉你,加上个 static
就可以了.于是你一运行,果然是棒棒哒!
但是你却百思不得其解,为啥加上了一个 static
就可以了呢?
原因很简单,前面都说了,这个拓展机制是在实例化对象 之前
,你用 static
修饰方法,是属于类级别的,优先级高,自然在DataSource实例化之前就完成了这个占位符的解析工作.
既然是源码解析类文章,我就尽量避免贴大段代码,否则还不如你直接去看.我用一个图来粗略描述这个大致的过程
除了上一篇中对 objectFactory
的介绍外,从这里我们知道, objectFactory
就是dubbo的IoC提供对象.
public <T> T getExtension(Class<T> type, String name) { //factories=[SpiExtensionFactory,SpringExtensionFactory] //遍历获取spi,先从SpiExtensionFactory获取,如果没有,再从SpringExtensionFactory获取 for (ExtensionFactory factory : factories) { T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; } 复制代码
public <T> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (loader.getSupportedExtensions().size() > 0) { return loader.getAdaptiveExtension(); } } return null; } 复制代码
public <T> T getExtension(Class<T> type, String name) { for (ApplicationContext context : contexts) { if (context.containsBean(name)) { //从容器中获取注入的对象 Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } return null; } 复制代码
dubbo在设计的时候设计了这两种方式,但是截止 2.5.4
版本, SpringExtensionFactory
的方式尚未发现使用,可能像Java的保留字一样,给以后埋下伏笔.据说 3.0
版本准备出来,到时候可以关注一下 SpringExtensionFactory
的使用情况.