本文首先介绍了SpringBoot 整合Dubbo的示例代码;接着讲解SpringBoot启动过程中Service暴露、Reference引用注入的过程,包括代码跟踪与类图展示。
文章目录
使用示例
Service
1、pom.xml 配置
<properties>
<version.dubbo>2.7.7</version.dubbo>
</properties>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${version.dubbo}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${version.dubbo}</version>
</dependency>
2、服务实现类
import com.learn.springcloud.service.DemoDbService;
import org.apache.dubbo.config.annotation.DubboService;
/**
* @Author zhangyugu
* @Date 2020/8/13 8:06 上午
* @Version 1.0
*/
@DubboService
public class DemoDbServiceImpl implements DemoDbService {
@Override
public String hello(String name) {
return "haha " + name;
}
}
3、配置
spring:
main:
allow-bean-definition-overriding: true # 这个是需要的,否则会因为重复bean注册而报错
application:
name: spring_cloud_dubbo_server
dubbo:
application:
id: spring_cloud_dubbo_server
protocol:
id: dubbo
port: 20990
registry:
id: spring_cloud_dubbo_server_registry
address: nacos://127.0.0.1:8848
provider:
version: 1.0.0
group: dev
启动类增加@EnableDubbo
@EnableDubbo(scanBasePackages = "com.learn.springcloud.service.impl")
Client
pom.xml配置同Server端
1、使用类
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author zhangyugu
* @Date 2020/8/13 9:06 上午
* @Version 1.0
*/
@RestController
public class TestController {
@DubboReference(version = "1.0.0")
DemoService demoService;
@GetMapping("/hello")
public String hello(String name) {
return demoService.hello(name);
}
}
2、yaml配置
spring:
main:
allow-bean-definition-overriding: true
application:
name: spring_cloud_dubbo_server
dubbo:
application:
id: spring_cloud_dubbo_server
protocol:
id: dubbo
port: 20990
registry:
id: spring_cloud_dubbo_server_registry
address: nacos://127.0.0.1:8848
provider:
version: 1.0.0
group: dev
SpringBoot启动类增加注解 @EnableDubbo
Dubbo service 暴露 和 reference 引用注入
Service
service bean注册:ServiceClassPostProcessor
1、ServiceClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor,在BeanDefinitionRegistry之后会调用postProcessBeanDefinitionRegistry方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 注册dubbo启动监听器,后面会用于服务暴露与引用
// @since 2.7.5
registerBeans(registry, DubboBootstrapApplicationListener.class);
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 注册serviceBean
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
/** 注册用@Service注解的类
* Registers Beans whose classes was annotated {@link Service}
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// refactor @since 2.7.7
serviceAnnotationTypes.forEach(annotationType -> {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
for (String packageToScan : packagesToScan) {
// 注册 @Service bean
// Registers @Service Bean first
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// 找到所有 @Service 注解的BeanDefinitionHolder
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
// 对于所有beanDefinitionHolder,注册 ServiceBean
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
+ packageToScan + "]");
}
}
}
}
/**
* 注册 ServiceBean,关键
* Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
*
* @param beanDefinitionHolder
* @param registry
* @param scanner
* @see ServiceBean
* @see BeanDefinition
*/
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
Class<?> beanClass = resolveClass(beanDefinitionHolder);
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 解析出service的接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 构建serviceBean的definition,设置一些dubbo需要的属性
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
logger.info("The BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean has been registered with name : " + beanName);
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
"] was be found , Did @DubboComponentScan scan to same package in many times?");
}
}
}
dubbo启动器:DubboBootstrapApplicationListener
DubboBootstrapApplicationListener 实现了ApplicationListener,在
接收到ContextRefreshedEvent事件时dubboBootstrap.start(),暴露service服务。
@Override
public void onApplicationContextEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
dubboBootstrap start()方法中暴露服务:
/**
* Start the bootstrap
*/
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
ready.set(false);
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. export Dubbo Services
exportServices();
// Not only provider register
if (!isOnlyRegisterProvider() || hasExportedServices()) {
// 2. export MetadataService
exportMetadataService();
//3. Register the local ServiceInstance if required
registerServiceInstance();
}
referServices();
if (asyncExportingFutures.size() > 0) {
new Thread(() -> {
try {
this.awaitFinish();
} catch (Exception e) {
logger.warn(NAME + " exportAsync occurred an exception.");
}
ready.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
}).start();
} else {
ready.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
}
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
ServiceBean
ServiceBean 继承自 ServiceConfig,实现了InitializingBean, ApplicationContextAware 和 ApplicationEventPublisherAware 接口
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware
这几个接口的执行顺序是怎样的呢?
1、如果某个类实现了ApplicationContextAware接口,会在类初始化完成后调用setApplicationContext()方法进行操作。
首先会执行ApplicatioinContextAware中的setApplicationContext()方法。
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
}
这里主要是将Spring的上下文引用保存到SpringExtensionFactory中,里面有个set集合,保存所有的Spring上下文。这里实现了Dubbo与Spring容器的相连,在SPI机制中利用ExtensionLoader.getExtension生成扩展类时,会有一个依赖注入的过程,即调用injectExtension()方法,它会通过反射获取类的所有方法,然后遍历set开头的方法,得到set方法的参数类型,再通过ExtensionFactory寻找参数类型相同的扩展类实例。
2、如果某个类实现了InitializingBean接口,在类初始化并setApplicationContext后,调用afterPropertiesSet()方法设置服务的path:
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(getPath())) {
if (StringUtils.isNotEmpty(beanName)
&& StringUtils.isNotEmpty(getInterface())
&& beanName.startsWith(getInterface())) {
setPath(beanName);
}
}
}
3、ApplicationEventPublisherAware 会设置applicationEventPublisher属性,用于在dubbo export完成后发布exported事件。
类图
Reference
Reference注入: ReferenceAnnotationBeanPostProcessor
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor
AbstractAnnotationBeanPostProcessor 是对 BeanPostProcessor的一个实现,用于处理注解注入的对象的处理。
/**
* 从指定的 注解属性 和 bean类中,得到注入的对象
* Get injected-object from specified {@link AnnotationAttributes annotation attributes} and Bean Class
*
* @param attributes {@link AnnotationAttributes the annotation attributes} 注解属性
* @param bean Current bean that will be injected 需要被注入对象的主体
* @param beanName Current bean name that will be injected
* @param injectedType the type of injected-object 注入对象的类型
* @param injectedElement {@link InjectionMetadata.InjectedElement} 字段还是方法
* @return An injected object 注入的对象
* @throws Exception If getting is failed
*/
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
Object injectedObject = injectedObjectsCache.get(cacheKey);
if (injectedObject == null) {
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement); // 需要在子类中实现
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
@DubboReference 注解加在字段上,通过ReferenceAnnotationBeanPostProcessor 的doGetInjectedBean方法实现注入对应的dubbo reference。
@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);
// 构建referenceBean
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);
// 注册referenceBean
registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);
cacheInjectedReferenceBean(referenceBean, injectedElement);
return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
}
private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
Class<?> referencedType)
throws Exception {
ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);
if (referenceBean == null) {
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
// 创建ReferenceBean所需要的属性
.create(attributes, applicationContext)
.interfaceClass(referencedType);
// 实际创建
referenceBean = beanBuilder.build();
referenceBeanCache.put(referenceBeanName, referenceBean);
} else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
throw new IllegalArgumentException("reference bean name " + referenceBeanName + " has been duplicated, but interfaceClass " +
referenceBean.getInterfaceClass().getName() + " cannot be assigned to " + referencedType.getName());
}
return referenceBean;
}
ReferenceBeanBuilder
/**
* Build {@link C}
*
* @return non-null
* @throws Exception
*/
public final C build() throws Exception {
checkDependencies();
C configBean = doBuild();
configureBean(configBean);
if (logger.isInfoEnabled()) {
logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
}
return configBean;
}
注册dubbo相关的信息
protected void configureBean(C configBean) throws Exception {
preConfigureBean(attributes, configBean);
configureRegistryConfigs(configBean);
configureMonitorConfig(configBean);
configureApplicationConfig(configBean);
configureModuleConfig(configBean);
postConfigureBean(attributes, configBean);
}
ReferenceBean
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean,
ApplicationContextAware, InitializingBean, DisposableBean
ReferenceBean是@DubboReference 注解的对象的具体引用类,里面最主要的就是对生成的ReferenceBean设置一个代理对象,在执行该对象方法时,会转化为远程调用,详见ReferenceConfig实现。