背景,2022.4.13,当前dubbo-spring-boot-starter版本是3.0.7, dubbo-api-docs-core版本在2.7.8.3
2023.4.8 由于springboot2.7 ,springboot3调整了自动配置规则 ,dubbo-spring-boot-starter需要升级到3.2.0+版本
由于dubbo默认采用dubbo协议非http方式调用,项目中不利于把接口元数据信息暴露并呈现在类swagger的文档管理界面上,当前springboot项目中有效的方式是在服务端maven上引用dubbo-api-docs-core和dubbo-api-docs-annotations,将现在dubbo服务API元数据暴露给大在dubbo-admin中进行查看.
dubbo-admin下载地址
基于目前dubbo3.x与dubbo-api-docs-core2.7.8.3版本有冲突,dubbo-api-docs-core内部依赖dubbo2.7.8首先要解决依赖冲突.
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-api-docs-annotations</artifactId>
<version>2.7.8.3</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-api-docs-core</artifactId>
<version>2.7.8.3</version>
<!-- 排除冲突 -->
<exclusions>
<exclusion>
<artifactId>dubbo</artifactId>
<groupId>org.apache.dubbo</groupId>
</exclusion>
</exclusions>
</dependency>
同样如果注册中心采用nacos,也要解决依赖冲突
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
服务端添加注解
@ApiModule(value = "测试接口服务", apiInterface = UserService.class) //必要 api-doc会查找所有带有@ApiModule的bean对象
@DubboService(version = "1.0") //需要@DubboService或@Service 注解有效
public class UserServiceImpl implements UserService {
@ApiDoc(value = "测试查询接口", description = "通过id查用户", responseClassDescription = "A quick star response bean") // 必要 扫描@ApiDoc注解的方法作为API
@Override
public SysUserDto getUserById(String userId) {
SysUserEntity entity = sysUserDataService.getById(userId);
SysUserDto result = BeanHelper.copyProperties(entity, SysUserDto::new);
return result;
}
}
服务端application.yml或bootstrap.yml中添加配置
dubbo:
registry:
address: nacos://localhost:8848
protocol:
name: dubbo
port: 20881
consumer:
check: false
retries: 1 #超时后不管前面是否完成,重试一次,可能会造成重复调用
timeout: 10000 #10秒内超时
metadata-report: #添加汇报元数据信息
address: nacos://localhost:8848
在启动时报错提示,如下
Description:
Field application in org.apache.dubbo.apidocs.core.DubboApiDocsAnnotationScanner required a bean of type 'org.apache.dubbo.config.ApplicationConfig' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.apache.dubbo.config.ApplicationConfig' in your configuration.
分析原因是DubboApiDocsAnnotationScanner这个类需要依赖ApplicationConfig ,RegistryConfig,ProtocolConfig 注入的bean,但是dubbo3在前面未在beanFactory中创建这3个bean对象。
@Import({DubboDocProviderImpl.class})
public class DubboApiDocsAnnotationScanner implements ApplicationListener<ApplicationReadyEvent> {
...
@Autowired
private ApplicationConfig application;
@Autowired
private RegistryConfig registry;
@Autowired
private ProtocolConfig protocol;
private ParamBean processHtmlType(Class<?> classType, RequestParam annotation, ParamBean param) {
....
/**
* export dubbo service for dubbo doc
*/
private <I, T> void exportDubboService(Class<I> serviceClass, T serviceImplInstance, boolean async) {
ServiceConfig<T> service = new ServiceConfig<>();
service.setApplication(application); #这里有用
service.setRegistry(registry); #这里有用
service.setProtocol(protocol); #这里有用
service.setInterface(serviceClass);
service.setRef(serviceImplInstance);
service.setAsync(async);
// service.setVersion("1.0.0");
service.export(); #内部
}
...省略
//以下为扫描具体实现方法
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
// Register dubbo doc provider
IDubboDocProvider dubboDocProvider = applicationContext.getBean(IDubboDocProvider.class);
exportDubboService(IDubboDocProvider.class, dubboDocProvider, false);
LOG.info("================= Dubbo API Docs--Start scanning and processing doc annotations ================");
Map<String, Object> apiModules = applicationContext.getBeansWithAnnotation(ApiModule.class); //从带@ApiModule的BEAN中查找
apiModules.forEach((key, apiModuleTemp) -> {
Class<?> apiModuleClass;
if (AopUtils.isAopProxy(apiModuleTemp)) {
apiModuleClass = AopUtils.getTargetClass(apiModuleTemp);
} else {
apiModuleClass = apiModuleTemp.getClass();
}
ApiModule moduleAnn = apiModuleClass.getAnnotation(ApiModule.class);
//必须是@Service或@DubboService
if (!apiModuleClass.isAnnotationPresent(Service.class) && !apiModuleClass.isAnnotationPresent(DubboService.class)) {
LOG.warn("【Warning】" + apiModuleClass.getName() + " @ApiModule annotation is used, " +
"but it is not a dubbo provider (without " + Service.class.getName() + " or " +
DubboService.class.getName() + " annotation)");
return;
}
boolean async;
String apiVersion;
String apiGroup;
if (apiModuleClass.isAnnotationPresent(Service.class)) {
Service dubboService = apiModuleClass.getAnnotation(Service.class);
async = dubboService.async();
apiVersion = dubboService.version();
apiGroup = dubboService.group();
} else {
DubboService dubboService = apiModuleClass.getAnnotation(DubboService.class);
async = dubboService.async();
apiVersion = dubboService.version();
apiGroup = dubboService.group();
}
// API version&group safe guard!
String version = getSupplierValueIfAbsent(apiVersion, () -> ofNullable(providerConfig).map(ProviderConfig::getVersion).orElse(""));
String group = getSupplierValueIfAbsent(apiGroup, () -> ofNullable(providerConfig).map(ProviderConfig::getGroup).orElse(""));
apiVersion = applicationContext.getEnvironment().resolvePlaceholders(version);
apiGroup = applicationContext.getEnvironment().resolvePlaceholders(group);
ModuleCacheItem moduleCacheItem = new ModuleCacheItem();
DubboApiDocsCache.addApiModule(moduleAnn.apiInterface().getCanonicalName(), moduleCacheItem);
//module name
moduleCacheItem.setModuleDocName(moduleAnn.value());
//interface name containing package path
moduleCacheItem.setModuleClassName(moduleAnn.apiInterface().getCanonicalName());
//module version
moduleCacheItem.setModuleVersion(apiVersion);
//module group
moduleCacheItem.setModuleGroup(apiGroup);
Method[] apiModuleMethods = apiModuleClass.getMethods();
// API basic information list in module cache
List<ApiCacheItem> moduleApiList = new ArrayList<>(apiModuleMethods.length);
moduleCacheItem.setModuleApiList(moduleApiList);
for (Method method : apiModuleMethods) {
//从带@ApiDoc的方法分析API
if (method.isAnnotationPresent(ApiDoc.class)) {
processApiDocAnnotation(method, moduleApiList, moduleAnn, async, moduleCacheItem, apiVersion, apiGroup);
}
}
});
LOG.info("================= Dubbo API Docs-- doc annotations scanning and processing completed ================");
}
因此只需要添加配置类启用文档并注入bean. 如果引入 dubbo-spring-boot-autoconfigure,会从配置文件中读取配置创建DubboConfigurationProperties对象。这里就可以利用。 可以新建一个配置类如下
@Profile("dev")
@EnableDubboApiDocs
@Configuration
public class DubboDocConfig {
@Autowired
DubboConfigurationProperties properties;
@ConditionalOnMissingBean
@Bean
ApplicationConfig application() {
if (properties != null && properties.getApplication() != null) {
return properties.getApplication();
}
return new ApplicationConfig(); //当前应用配置信息 ,如服务名
}
@Bean
@ConditionalOnMissingBean
RegistryConfig registry() {
if (properties != null && properties.getRegistry() != null) {
return properties.getRegistry();
}
RegistryConfig registryConfig = new RegistryConfig();//注册中心配置
// registryConfig.setAddress("nacos://localhost:8848");
// registryConfig.setUsername("nacos");
// registryConfig.setPassword("nacos");
return registryConfig;
}
@Bean
@ConditionalOnMissingBean
ProtocolConfig protocol() {
if (properties != null && properties.getProtocol() != null) {
return properties.getProtocol();
}
return new ProtocolConfig();// 服务传输协议的配置
}
}
再启动服务,不报错,服务启动完成。
接下来配置一下dubbo-admin 的配置,在config下配置application.properties 注册中心地址,访问端口
admin.registry.address=nacos://127.0.0.1:8848
admin.config-center=nacos://127.0.0.1:8848?group=dubbo
admin.metadata-report.address=nacos://127.0.0.1:8848
server.port=8028 //访问端口
启动dubbo-admin.bin目录中startup.cmd,访问localhost:8028(自己在配置中修改), 默认登录 root root 就能查看了
点此参考完整源码