IOC容器初始化阶段中的Dubbo
BeanDefinitionRegistryPostProcessor
MapperScannerConfigurer
这个BeanDefinitionRegistryPostProcessor的意义在于将Mybatis定义的一些对象集成到SpringIOC容器中。在执行postProcessBeanDefinitionRegistry,这个方法做的事情就是将接口转换成BeanDefinition【实际是MapperFactoryBean】。
Spring在跟BeanDefinition创建对象时,是根据BeanDefinition的beanClass。 因此,会发现,虽然spring-mybatis提供的ClassPathMapperScanner调用父类的doScan将接口转换成对应BeanDefinition之外,还会对这些BeanDefinition做一些processBeanDefinitions处理,即将为这些BeanDefinition指定beanClass=MapperFactoryBean.class。
当Spring在创建含有Mapper接口注入的对象时,即populateBean时,会创建Mapper接口对象,这是创建的时MapperFactoryBean,然后再通过mapperFactoryBean.getObject()通过对应的MapperProxyFactory会真正创建mapper接口的代理对象。
从这里也可以看出BeanFactory和FactoryBean的不同区别,通俗点来说BeanFactory可以被认为是创建bean对象的工厂,而FactoryBean是一种可以被Bean Factory创建的特殊Bean,特殊在于这个bean也可以通过getObject创建对象,这个创建对象可以不通过Spring的机制,比如MapperFactoryBean的getObject。因此,这个FactoryBean的意义在于集成第三方组件内部的一些定义的对象。类似的还有ShiroFilterFactoryBean。
说一下mybatis在Spring环境下的工作机制的原因在于,Dubbo也是有自己的一些对象需要通过SpringIOC容器来创建,比如ServiceBean。而mybatis-spring提供了使用范例。我也是在读到Dubbo的源码时,有种直觉,就是Dubbo团队参考了mybatis-spring。因为Dubbo跟mybatis-spring之间的处理有很多相像的地方。我觉得,即便你深入了解过Spring源码,当你提供第三方的服务组件,需要集成到Spring环境中,如果有一种使用范例,那么在开发起来,会十分的便利。因为我们在学习一件事时,经常问的就是是什么,为什么,跟怎样做。而深入了解Spring源码也只是停留在是什么跟为什么的阶段,怎样做?mybatis-spring。
DubboConfigAliasPostProcessor
ServiceAnnotationBeanPostProcessor
由于在上一篇中,该BeanDefinitionRegistryPostProcessor已经在ConfigurationClassPostProcessor这一阶段被加载到Spring IOC容器中了。因此,会进入invokeBeanFactoryPostProcessors的第二阶段,从IOC容器中再次寻找BeanDefinitionRegistryPostProcessor,且是实现了Ordered的继续创建并执行。
因为,IOC容器中可能存在通过配置类而新加入的该BeanDefinitionRegistry类型的BeanDefinition。
跳过进入第三阶段,没有实现Ordered的BeanDefinitionRegistryPostProcessor的创建跟执行,即ServiceAnnotationBeanPostProcessor。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// @since 2.7.5
// 这个是统一监听器,而不再是每一个ServiceBean都是ApplicationListener
// 相当于一个窗口。我司的RSF就是这样,有一个统一的窗口
registerBeans(registry, DubboBootstrapApplicationListener.class);
// 解析Dubbo服务的包名
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 将该包名下的Dubbo服务都解析成BeanDefinition并注册到IOC容器中,
// 以便被IOC容器创建对象
// 用DubboClassPathBeanDefinitionScanner将Dubbo服务解析成BeanDefinition
// 思路跟mybatis-spring一样。就不在赘述了。
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
总结
提供自定义的BeanDefinitionRegistry服务,需要实现:
- 实现BeanDefinitionRegistryPostProcessor接口的PostProcessor
- 继承ClassPathBeanDefinitionScanner并重写doScan,这个重写需要注意的一点就是,不用取代原有的doScan逻辑,继续利用原来的,我们自己有特殊需求的,可以对生成的BeanDefinition做处理,比如mybatis-spring,dubbo。
BeanPostProcessor
当IOC容器初始化,执行完invokeBeanFactoryPostProcessors这一有关BeanDefinition生成的阶段后,紧接着,需要创建能对bean对象的初始化(生命周期)有影响的BeanPostProcessor。Dubbo的@Reference注解所对应的的ReferenceAnnotationBeanPostProcessor就是在这一阶段创建,而它的BeanDefinition则是在上一阶段就已经注册到IOC容器中了,这部分十分简单,就不详述。
MessageSource(没有涉及)
ApplicationEventMulticaster
创建默认的广播器
registerListeners
将ApplicationContext中所有的监听器都注册到上一阶段创建的广播器中,包括Dubbo实现的ApplicationListener。
finishBeanFactoryInitialization
根据IOC容器中已经存在的BeanDefinitionName完成Dubbo相关的对象创建。比如DubboBootstrapApplicationListener,以及Dubbo的@Service服务的ServiceBean等。
finishRefresh
当IOC容器初始化完成后,产生一事件ContextRefreshedEvent,该事件经ApplicationEventMulticaster广播器广播到所有的ApplicationListener,包括DubboBootstrapApplicationListener。
将Dubbo提供的服务发布到注册中心去。这里主要详述DubboBootstrapApplicationListener的工作机制。
/**
* Start the bootstrap
*/
public DubboBootstrap start() {
// 根据状态started只启动一次
if (started.compareAndSet(false, true)) {
// 暴露Dubbo服务之前的初始化,主要是设计复杂的类加载机制
// 启动Dubbo的配置管理ConfigManager,
// 主要是涉及Application Protocol Monitor Registry
// Module Provider Consumer收集管理
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. export Dubbo Services
// 从配置管理中获取所有的Dubbo服务进行暴露服务
exportServices();
// Not only provider register
if (!isOnlyRegisterProvider() || hasExportedServices()) {
// 2. export MetadataService
exportMetadataService();
//3. Register the local ServiceInstance if required
registerServiceInstance();
}
referServices();
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
在追踪exportServices方法时,会发现,Dubbo服务其实就是Dubbo的URL服务。通俗点说,就是将一个类服务转换成URL服务。这个方法前期做了URL地址的拼装,并将URL转换成Invoker对象,供服务消费方使用。
// 创建Invoker对象
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 根据协议暴露invoker
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
debug追踪到DubboProtocol的export的openServer方法,默认使用Netty进行数据传输。
当涉及Dubbo的@Reference引用的对象时,会在消费对象创建的populateBean阶段经过ReferenceAnnotationBeanPostProcessor处理,生成该@Reference注入的接口的代理对象。
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 利用Spring的InjectionMetadata将属性pvs注入到bean中去
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
}
return pvs;
}
继续追踪inject方法,是其ReferenceAnnotationBeanPostProcessor父类的内部类中的。
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Class<?> injectedType = field.getType();
// 根据注入类型和属性,获取注入对象
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
field.set(bean, injectedObject);
}
继续追踪,ReferenceAnnotationBeanPostProcessor实现其父类的doGetInjectedBean。
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
/**
* The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
*/
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
/**
* The name of bean that is declared by {@link Reference @Reference} annotation injection
*/
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
// 创建Dubbo服务提供的接口在Spring IOC容器中的对应对象
// 可以将ReferenceBean理解为Dubbo的服务接口的基础数据结构
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
// 向容器中注册ReferenceBean
registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 获取注入的代理对象,这个代理对象的创建过程涉及Netty客户端的创建,
// 以及与Netty服务端的连接建立。两种方式,连接客户端,一种是直连【不要单步debug,因为很容易debug迷失。去除所有的断点,直接在客户端初始化那打断点,然后看线程名】;另一种是
// 通过定时器DubboRegistryRetryTimer链接【单步debug时候,就会出现这种场景】。
return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
}
单步debug,以致触发定时任务重试链接服务端,此时的main线程执行消费方的时候,获取的服务提供方列表为空。因此,main线程在执行消费方对目录的providers直接return。而定时任务,可以走下一步toInvokers()。进而是客户端的创建。