概述
之前我们分析过dubbo执行rpc调用的时候如何同步和异步之间来回转换的,我们实际使用中发现dubbo直接在service接口上增加@DubboReference注解或者在xml配置文件中配置reference就可以直接在spring中引用到,就可以执行到InvokerInvocationHandler进而执行rpc。
使用起来非常的方便,那这又是怎么做到的呢?咱们今天就一起来看下。
猜想
咱们先猜想一下,在通过debug逐步分析我们的猜想是否正确。
我们在spring管理的类中可以直接注入一个接口,我们可以通过注解的方式在类上加入@Service @Component 或者在xml中通过bean标签的方式等等,但实际我们都没有做,只是在service注入的时候加了@DubboReference。
猜想是dubbo给我们生成了默认的实现类,并且将这个类注入到了spring ioc中,只有这样才可以在任意spring管理的类中注入一个bean。
那谁来@DubboReference解析注解呢?spring提供了一系列的扩展入口可以通过扩展spring来解析我们自己的注解,比如mybatis中的mapper等,那dubbo是怎么扩展的呢?
答案应该在@EnableDubbo 注解中可以找到,带着我们的猜想来看下究竟是怎么做的。
源码佐证
dubbo模块众多,我们见明知意来到dubbo-config-spring模块,具体结构如下:
熟悉spring的朋友看到这应该可以想通了,我们的猜想是正确的。
我们的项目demo结构:
DemoService是个rpc接口,我们在上面增加了@DubboReference注解,我们通过观察dubbo源码的结构可以大致猜想到是ReferenceAnnotationBeanPostProcessor类来做的事情,这是对spring扩展的一个实现。
咱们首先来到@EnableDubbo注解看下源码,这又一个spring扩展点知识。:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
这里可以看到是的@EnableDubboConfig @DubboComponentScan整合体,再分别进去到2个注解中:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
/**
* It indicates whether binding to multiple Spring Beans.
*
* @return the default value is <code>true</code>
* @revised 2.5.9
*/
boolean multiple() default true;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
* {@code @DubboComponentScan(basePackages="org.my.pkg")}.
*
* @return the base packages to scan
*/
String[] value() default {};
这里有个@Import注解这个是spring的扩展,可以动态的注册bean,我们猜想这里可能就是dubbo和spring结合的开始,我们在DubboConfigConfigurationRegistrar中打上断点然后运行项目跟下代码。
来到DubboSpringInitializer类中的initContext方法:部分源码如下:
private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
ConfigurableListableBeanFactory beanFactory) {
context.setRegistry(registry);
context.setBeanFactory(beanFactory);
// customize context, you can change the bind module model via DubboSpringInitCustomizer SPI
customize(context);
// init ApplicationModel
ApplicationModel applicationModel = context.getApplicationModel();
if (applicationModel == null) {
if (findContextForApplication(ApplicationModel.defaultModel()) == null) {
// first spring context use default application instance
applicationModel = ApplicationModel.defaultModel();
} else {
// create an new application instance for later spring context
applicationModel = FrameworkModel.defaultModel().newApplication();
}
// init ModuleModel
ModuleModel moduleModel = applicationModel.getDefaultModule();
context.setModuleModel(moduleModel);
}
// set module attributes
if (context.getModuleAttributes().size() > 0) {
context.getModuleModel().getAttributes().putAll(context.getModuleAttributes());
}
// 最最关键的地方之一
registerContextBeans(beanFactory, context);
// mark context as bound
context.markAsBound();
// 最最关键的地方之一
DubboBeanUtils.registerCommonBeans(registry);
}
这个方法大致做了这几件事,初始化dubbo的上下文,将dubbo的一些公共类和上下文注册到spring,这里最主要的就是registerCommonBeans方法这个方法注册一大堆dubbo对spring扩展的类,源码如下:
static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);
registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
// Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// TODO Whether DubboConfigAliasPostProcessor can be removed ?
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);
// Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
// registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
// DubboBootstrapApplicationListener.class);
// register ApplicationListeners
registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);
// Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);
// Dubbo config initializer
registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
// register infra bean if not exists later
registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}
注册的这些类呢又是我们刚刚在dubbo-config-spring项目中看到的那些,这更加证实我们的猜想是对的。
不太明白spring的朋友可能就会有疑惑了,这里简单解释下(这里说的注册不是直接注册bean,而是注册到beanDefinitaion中也就是所谓的bean源信息)spring源码解析不在本次探讨内,默认大家对spring源码已经熟悉了。
这部分直接跳过,来到了ReferenceAnnotationBeanPostProcessor类的postProcessBeanFactory方法这个方法循环处理beanFactory最后来到:
protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException {
try {
//find and register bean definition for @DubboReference/@Reference
for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
if (fieldElement.injectedObject != null) {
continue;
}
Class<?> injectedType = fieldElement.field.getType();
AnnotationAttributes attributes = fieldElement.attributes;
//重点在这里 registerReferenceBean 他在这里注册了这个玩意
String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);
//associate fieldElement and reference bean
fieldElement.injectedObject = referenceBeanName;
injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);
}
for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
if (methodElement.injectedObject != null) {
continue;
}
Class<?> injectedType = methodElement.getInjectedType();
AnnotationAttributes attributes = methodElement.attributes;
String referenceBeanName = registerReferenceBean(methodElement.getPropertyName(), injectedType, attributes, methodElement.method);
//associate fieldElement and reference bean
methodElement.injectedObject = referenceBeanName;
injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName);
}
} catch (ClassNotFoundException e) {
throw new BeanCreationException("prepare reference annotation failed", e);
}
}
我们在这里发现了猫腻了,继续跟进registerReferenceBean方法,这个方法是将ReferenceBean写入到beandefinition中核心代码:
// Register the reference bean definition to the beanFactory
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(ReferenceBean.class.getName());
beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);
// set attribute instead of property values
beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
// create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
// see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
targetDefinition.setBeanClass(interfaceClass);
String id = getPropertyValue(beanDefinition.getPropertyValues(), ReferenceAttributes.ID);
beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));
// signal object type since Spring 5.2
beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
logger.info("Register dubbo reference bean: "+referenceBeanName+" = "+referenceKey+" at "+member);
return referenceBeanName;
这个类的居然是demoService的描述信息
说明我们到核心位置了,我们看下ReferenceBean这个类发现这个是一个FactoryBean这又是一个spring的扩展点,我们看下这个类的关键代码:
public class ReferenceBean<T> implements FactoryBean<T>,
ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean {
@Override
public T getObject() {
if (lazyProxy == null) {
createLazyProxy();
}
return (T) lazyProxy;
}
@Override
public Class<?> getObjectType() {
return getInterfaceClass();
}
@Override
@Parameter(excluded = true)
public boolean isSingleton() {
return true;
}
private void createLazyProxy() {
//set proxy interfaces
//see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
ProxyFactory proxyFactory = new ProxyFactory();
//此处是重点中的重点
proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
proxyFactory.addInterface(interfaceClass);
Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
for (Class<?> anInterface : internalInterfaces) {
proxyFactory.addInterface(anInterface);
}
if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
//add service interface
try {
Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
proxyFactory.addInterface(serviceInterface);
} catch (ClassNotFoundException e) {
// generic call maybe without service interface class locally
}
}
this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
}
通过createLazyProxy方法将demoService的实例返回,但是我们实际看到的确实dubbo的Proxy类:
我们发现ioc实例化demoService的时候也是实例化的ReferenceBean类:
这时候就有人疑问了,明明是ReferenceBean类,怎么最后又成dubbo的代理类了呢?其实ReferenceBean类是factorybean,它是个特殊bean,它有个方法getObject会返回实际的bean类。
而我们明明看到是调用的spring的动态代理啊怎么回事呢、关键代码在proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());这行,源码如下:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
//如果不为空直接生成targetClass 的代理对象
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}
这也是spring的一个扩展点,targetSource属性可以灵活的扩展,所以最后springaop帮忙我们代理的并非demoService也非ReferenceBean而是dubbo的org.apache.dubbo.common.bytecode.Proxy。
总结
至此我们整个流程算是理清楚了,dubbo对接spring时采用了大量的spring扩展实现(后置处理器、factoryBean、import、Proxy-->tragetSource),关于spring的后置处理器的可以看下之前写的《2019年最后一天,呕心沥血之作,解读spring5大后置处理器》。
最后咱们简单画一下流程图:
我们不光要知其然,还要知其所以然,欢迎大家和我一起阅读源码、欣赏源码、借鉴源码,好了这期就到这了,咱们下期再见!
MYSQL系列经典文章