微服务 SDK 二次开发
7.x 之后,产品微服务采用 backend-lib + server 分层。backend-lib 包含产品完整 MVC 逻辑并发布到 Nexus,server 只保留 Spring Boot 启动类、容器适配和交付配置。二开项目依赖对应的 backend-lib,在自己的模块中增加扩展 Controller、Service、Component、Entity 和 Repository。
这条路径适合需要运行在 DAM 服务进程内、复用产品事务和权限上下文的开发。只做跨系统查询或审批编排时,优先使用本章的 REST API 和独立适配服务。
1. 前置条件
- 获得 SDK 授权、目标版本的
backend-lib和对应源码/接口说明。 - 安装 JDK 17、Maven 3.8 或更高版本,并配置企业 Nexus 镜像。
- 准备可访问的配置中心和服务注册中心。启动时通过环境变量或 JVM 参数注入地址、命名空间和凭据。
- 为扩展建立独立 Git 分支,记录产品版本、SDK 版本和数据库迁移版本。
不要把配置中心密码、数据库密码或个人 Cookie 放进 pom.xml、启动脚本和日志。
2. 工程拆分
datablau-<service>-backend-lib/ # 产品逻辑库,发布为 Maven artifact
datablau-<service>-server/ # 二开启动模块
├─ src/main/java/.../Application.java
├─ src/main/resources/
└─ pom.xml将原服务工程拆为两个模块:
- 原 backend 改成
datablau-<service>-backend,产物名改为datablau-<service>-backend-lib,版本与产品版本保持一致。 - 新建
datablau-<service>-server,复制资源目录,把Application启动类从 lib 移入 server。 - 在
SpringApplication和JpaConfiguration中加入扩展包扫描路径,预留ext包。 - server 只依赖 backend-lib 和运行容器 starter,不重新复制产品业务依赖。
backend-lib 的最小 Maven 结构:
<artifactId>datablau-metadata-backend-lib</artifactId>
<packaging>jar</packaging>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.csv</include>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes><include>**/*.csv</include></includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>server 的核心依赖和打包插件:
<dependencies>
<dependency>
<groupId>com.datablau</groupId>
<artifactId>datablau-metadata-backend-lib</artifactId>
<version>${datablau.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration><includeSystemScope>true</includeSystemScope></configuration>
<executions><execution><goals><goal>repackage</goal></goals></execution></executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
</plugin>
</plugins>
</build>容器差异放在 server profile:默认使用 Spring Boot Tomcat,需要 BES 或 TongWeb 时只切换对应 starter profile,不改业务代码。
3. Bean 覆盖机制
产品服务实现类和可扩展方法必须允许继承。产品 Bean 需要显式命名,并使用扩展 Bean 名称作为缺省条件:
@Service("userService")
@ConditionalOnMissingBean(name = "userServiceExt")
public class UserServiceImpl implements UserService {
// 可被扩展的方法使用 protected 或 public
}扩展类使用 XXXExt 命名:
@Service("userServiceExt")
public class UserServiceExtImpl extends UserServiceImpl {
@Override
public UserDto findById(Long id) {
UserDto user = super.findById(id);
// 增加企业自己的组合或校验
return user;
}
}规则如下:
- private 方法、内部类和无法重载的实现细节不能作为扩展点,应由产品版本提供 protected/public 的受支持入口。
- 产品 Bean 的名称和扩展 Bean 的名称必须稳定,否则 Spring 可能同时创建两个实现。
- 需要改变返回 DTO 时优先新增
xxxExt方法和 DTO。直接把原接口返回类型改成子类,可能破坏其他微服务的远程反序列化。 - 先调用
super再补充属性适合轻量增强;需要改变 SQL、分页或事务语义时,应完整覆盖方法并补充契约测试。
4. 扩展属性的持久化
产品表和产品 Repository 不直接修改。以给用户增加 hireDate 为例,创建独立扩展表,并用产品用户 ID 关联:
@Entity
@Table(name = "db_users_ext")
public class UserExt {
@Id
private Long userId;
@Column(name = "hire_date")
@Temporal(TemporalType.TIMESTAMP)
private Date hireDate;
}
@Repository("userExtRepository")
public interface UserExtRepository extends CrudRepository<UserExt, Long> {
@Query("select ue from UserExt ue where ue.userId in :userIds")
List<UserExt> findByUserIds(@Param("userIds") Collection<Long> userIds);
}扩展 Entity 必须位于 JpaConfiguration 扫描路径,例如 ...ext.jpa.entity 和 ...ext.jpa.repository。如果启动时出现 Entity 未 managed 或 Repository 找不到,先检查扫描包、实体主键和 profile,再检查数据库迁移是否已执行。
通过扩展 DTO 暴露属性:
public class UserExtDto extends UserDto {
private Date hireDate;
public UserExtDto(UserDto source) {
BeanUtils.copyProperties(source, this);
}
// getter / setter
}对列表接口,先调用产品服务拿到分页结果,再以业务主键批量查询扩展表并合并,禁止在循环中逐条查询。若远程调用方依赖原 DTO,提供新接口返回 UserExtDto,保留原接口兼容性。
5. 新增跨微服务接口
以 Service A 提供、Service B 调用为例:
- 新建
datablau-servicea-<project>-api,只放共享 interface 和 DTO。 - 发布 API artifact 到 Nexus;Service A 与 Service B 分别声明同版本依赖。
- Service A 扩展
HttpExporterConfiguration,注册HttpInvokerServiceExporter。 - Service B 在扩展配置中创建
RemoteServiceProxyFactoryBean,配置目标服务名、服务接口、服务 URL 和本地空实现。 - 在业务 Service 中注入接口,增加超时、重试、熔断和权限日志。
提供方示例:
@Configuration("httpExporterConfigurationExt")
public class HttpExporterConfigurationExt extends HttpExporterConfiguration {
@Bean(name = "/ExampleInterface")
public HttpInvokerServiceExporter exampleServiceExporter(ExampleInterface service) {
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(service);
exporter.setServiceInterface(ExampleInterface.class);
return exporter;
}
}调用方的代理配置必须使用目标服务的实际 context、端口和服务发现名称,不能复制开发环境 URL。共享 DTO 只放稳定字段,避免把产品内部 Entity 暴露为远程协议。
6. 构建、启动和调试
# 在父工程安装依赖和版本管理
mvn -f backend/datablau-parent/pom.xml install
# 编译全部后台模块
mvn -f backend/pom.xml install
# 只构建一个二开服务
mvn -pl datablau-metadata-server -am package启动时配置 JDK 17、服务注册中心地址、命名空间、应用 home 和日志目录。配置中心账号通过环境变量注入,例如:
export JAVA_HOME=/path/to/jdk-17
export METADATA_HOME=/opt/datablau_metadata
java -Xms2g -Xmx8g \
-Dspring.cloud.nacos.username="$NACOS_USERNAME" \
-Dspring.cloud.nacos.password="$NACOS_PASSWORD" \
-Dspring.cloud.nacos.discovery.server-addr="$NACOS_ADDR" \
-Dspring.cloud.nacos.discovery.namespace="$NACOS_NAMESPACE" \
-Dlogging.file.path="$METADATA_HOME/logs" \
-jar "$METADATA_HOME/app/datablau-metadata-server.jar"本地调试流程:启动扩展服务并连接测试配置中心 → 将同一微服务的其他实例下线,只保留调试实例 → 登录 DAM → 用浏览器开发工具获取当前会话进行测试 → 用 Postman 或自动化测试调用扩展接口 → 检查 Bean、数据库、审计日志和返回结构。测试结束后恢复其他实例。
7. 发布和升级
二开仓库建议按 backend/、frontend/ 和各微服务模块组织。发布时记录 backend-lib 版本、扩展版本、配置差异和数据库迁移。升级到 9.2 或后续版本时:先替换 lib 并编译 → 检查编译和 Bean 创建 → 回归原 API、扩展 API、远程调用和权限 → 执行数据库迁移 → 灰度启动 → 保留旧包和回滚配置。
升级检查重点包括方法签名、DTO 字段、Bean 名称、JPA 扫描路径、远程服务 URL、Parser SPI 和统计模型字段。每个版本必须有一组最小可重复的验收样本。
