Spring Boot中基于配置的SOLID IoC:多连接客户端设计与实现问询
方案合理性判断
这个配置方案合理且具备良好的扩展性:
- 它将不同通信协议(REST/gRPC/RabbitMQ)的连接参数集中管理,与业务逻辑完全解耦,后续调整连接地址、端口等配置无需修改业务代码;
- 通过
endpointType将通信端点与业务模块(EMPLOYEE/DEPARTMENT)绑定,能清晰映射业务服务所需的底层通信能力; - 业务服务仅依赖抽象客户端,无需关注底层协议细节,符合依赖反转原则,后续切换通信协议(比如把某业务的REST换成gRPC)只需调整配置和实现类,不影响业务逻辑。
Spring Boot 实现步骤
1. 绑定配置文件到Java类
通过@ConfigurationProperties将YAML配置映射为Java对象,方便代码中读取配置:
// 顶级配置类 @ConfigurationProperties(prefix = "connections") @Component public class ConnectionsProperties { private List<HttpConnection> http; private List<GrpcConnection> grpc; // 后续可添加rabbitmq配置列表 // getter/setter } // HTTP连接配置 public class HttpConnection { private String name; private String host; private Integer port; private List<Endpoint> endpoints; // getter/setter } // gRPC连接配置 public class GrpcConnection { private String name; private String host; private Integer port; private Boolean disableTls; private List<Endpoint> endpoints; // getter/setter } // 端点配置 public class Endpoint { private String name; private String httpMethod; // 仅HTTP协议使用 private String endpointType; // EMPLOYEE/DEPARTMENT // getter/setter }
记得在启动类添加@EnableConfigurationProperties(ConnectionsProperties.class)开启配置绑定。
2. 定义抽象客户端接口
为业务模块定义抽象客户端,业务服务仅依赖这些接口,实现逻辑与底层协议解耦:
// 员工业务客户端抽象 public interface EmployeeClient { List<Employee> getEmployees(); } // 部门业务客户端抽象 public interface DepartmentClient { Department createDepartment(Department dept); }
3. 实现具体协议客户端
根据配置实现不同协议的客户端实现类:
HTTP客户端实现
public class EmployeeHttpClient implements EmployeeClient { private final RestTemplate restTemplate; private final String baseUrl; private final String httpMethod; // 构造器注入配置参数 public EmployeeHttpClient(RestTemplate restTemplate, String host, Integer port, String httpMethod) { this.restTemplate = restTemplate; this.baseUrl = String.format("http://%s:%s/employees", host, port); this.httpMethod = httpMethod; } @Override public List<Employee> getEmployees() { if ("GET".equalsIgnoreCase(httpMethod)) { return restTemplate.exchange(baseUrl, HttpMethod.GET, null, new ParameterizedTypeReference<List<Employee>>() {}).getBody(); } throw new UnsupportedOperationException("不支持当前HTTP方法"); } } public class DepartmentHttpClient implements DepartmentClient { private final RestTemplate restTemplate; private final String baseUrl; private final String httpMethod; public DepartmentHttpClient(RestTemplate restTemplate, String host, Integer port, String httpMethod) { this.restTemplate = restTemplate; this.baseUrl = String.format("http://%s:%s/departments", host, port); this.httpMethod = httpMethod; } @Override public Department createDepartment(Department dept) { if ("POST".equalsIgnoreCase(httpMethod)) { return restTemplate.postForObject(baseUrl, dept, Department.class); } throw new UnsupportedOperationException("不支持当前HTTP方法"); } }
gRPC客户端实现
public class EmployeeGrpcClient implements EmployeeClient { private final EmployeeServiceGrpc.EmployeeServiceBlockingStub blockingStub; public EmployeeGrpcClient(String host, Integer port, Boolean disableTls) { ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder.forAddress(host, port); if (disableTls) { channelBuilder.usePlaintext(); } ManagedChannel channel = channelBuilder.build(); this.blockingStub = EmployeeServiceGrpc.newBlockingStub(channel); } @Override public List<Employee> getEmployees() { GetEmployeesResponse response = blockingStub.getEmployees(GetEmployeesRequest.newBuilder().build()); return response.getEmployeesList().stream() .map(grpcEmp -> new Employee(grpcEmp.getId(), grpcEmp.getName())) .collect(Collectors.toList()); } }
4. 动态注册客户端Bean到Spring容器
编写配置类,遍历配置文件中的连接和端点,根据endpointType和协议类型创建对应的客户端实例并注册为Spring Bean:
@Configuration public class ClientAutoConfiguration { private final ConnectionsProperties connectionsProperties; private final RestTemplate restTemplate; public ClientAutoConfiguration(ConnectionsProperties connectionsProperties, RestTemplate restTemplate) { this.connectionsProperties = connectionsProperties; this.restTemplate = restTemplate; } // 注册HTTP客户端Bean @Bean public void registerHttpClients() { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) ApplicationContextProvider.getApplicationContext().getAutowireCapableBeanFactory(); for (HttpConnection conn : connectionsProperties.getHttp()) { for (Endpoint endpoint : conn.getEndpoints()) { switch (endpoint.getEndpointType()) { case "EMPLOYEE": BeanDefinitionBuilder empHttpBuilder = BeanDefinitionBuilder.genericBeanDefinition(EmployeeHttpClient.class) .addConstructorArgValue(restTemplate) .addConstructorArgValue(conn.getHost()) .addConstructorArgValue(conn.getPort()) .addConstructorArgValue(endpoint.getHttpMethod()); registry.registerBeanDefinition("employeeHttpClient", empHttpBuilder.getBeanDefinition()); break; case "DEPARTMENT": BeanDefinitionBuilder deptHttpBuilder = BeanDefinitionBuilder.genericBeanDefinition(DepartmentHttpClient.class) .addConstructorArgValue(restTemplate) .addConstructorArgValue(conn.getHost()) .addConstructorArgValue(conn.getPort()) .addConstructorArgValue(endpoint.getHttpMethod()); registry.registerBeanDefinition("departmentHttpClient", deptHttpBuilder.getBeanDefinition()); break; } } } } // 注册gRPC客户端Bean @Bean public void registerGrpcClients() { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) ApplicationContextProvider.getApplicationContext().getAutowireCapableBeanFactory(); for (GrpcConnection conn : connectionsProperties.getGrpc()) { for (Endpoint endpoint : conn.getEndpoints()) { if ("EMPLOYEE".equals(endpoint.getEndpointType())) { BeanDefinitionBuilder empGrpcBuilder = BeanDefinitionBuilder.genericBeanDefinition(EmployeeGrpcClient.class) .addConstructorArgValue(conn.getHost()) .addConstructorArgValue(conn.getPort()) .addConstructorArgValue(conn.getDisableTls()); registry.registerBeanDefinition("employeeGrpcClient", empGrpcBuilder.getBeanDefinition()); } } } } // 提供RestTemplate Bean @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
注:ApplicationContextProvider是实现ApplicationContextAware的工具类,用于获取Spring上下文,方便动态注册Bean。
5. 业务服务使用客户端
业务服务通过@Qualifier指定具体客户端实现,按需调用:
@Service public class EmployeeService { private final EmployeeHttpClient employeeHttpClient; private final EmployeeGrpcClient employeeGrpcClient; public EmployeeService(@Qualifier("employeeHttpClient") EmployeeHttpClient employeeHttpClient, @Qualifier("employeeGrpcClient") EmployeeGrpcClient employeeGrpcClient) { this.employeeHttpClient = employeeHttpClient; this.employeeGrpcClient = employeeGrpcClient; } public List<Employee> getEmployeesByHttp() { return employeeHttpClient.getEmployees(); } public List<Employee> getEmployeesByGrpc() { return employeeGrpcClient.getEmployees(); } } @Service public class DepartmentService { private final DepartmentHttpClient departmentHttpClient; public DepartmentService(@Qualifier("departmentHttpClient") DepartmentHttpClient departmentHttpClient) { this.departmentHttpClient = departmentHttpClient; } public Department createDepartment(Department dept) { return departmentHttpClient.createDepartment(dept); } }
6. RabbitMQ 扩展实现(可选)
如果需要支持RabbitMQ,只需在YAML中添加rabbitmq配置段,定义连接、队列/交换机信息,然后实现EmployeeRabbitClient等类,在ClientAutoConfiguration中添加对应的注册逻辑即可,思路与HTTP/gRPC一致。
内容的提问来源于stack exchange,提问作者Spirotrack
相关产品推荐
相关产品推荐

