问题描述
在使用 HTTP 远程处理的 Spring 应用程序中,我有一个如下配置的服务外观模块(我使代码通用以提高清晰度):
@Configuration
public class MyFacadeConfig {
private httpinvokerServiceExporter facade(Class<?> cls) {
httpinvokerServiceExporter bean = new httpinvokerServiceExporter();
// The service referred to by this exporter is already instantiated as another Spring bean with all its dependencies.
bean.setService(appContext.getBean(cls));
bean.setServiceInterface(cls);
return bean;
}
@Bean("/first.service")
public httpinvokerServiceExporter firstServiceFacade() {
return facade(FirstService.class);
}
@Bean("/second.service")
public httpinvokerServiceExporter secondServiceFacade() {
return facade(SecondService.class);
}
// ... and so on for the 37 other services
}
其中 FirstService
和 SecondService
是与现有实现的接口,此处不需要其详细信息。
我有另一个模块,它定义了 39 个代理(httpinvokerProxyfactorybean
的实例),这些代理对应于通过我的外观公开的每个服务。
到目前为止,一切正常。
但我想让代码更加通用、优雅和健壮,同时降低出错的风险(例如,将来服务与其代理之间的错误映射)。我想这样做的方式如下:
首先,我将外观/代理元数据移动到枚举中:
public enum ConfigBeansFacade {
FirsT("/first",FirstService.class),SECOND("/second",SecondService.class)
// ... and so on for the 37 other services
;
private String beanName;
private Class<?> serviceInterface;
// Constructor and getters
public String getCompleteBeanName() {
return beanName + ".service";
}
}
那么外观的配置将被简化为类似于以下的样式:
@Configuration
public class MyFacadeConfig {
@Autowired
private Configurablebeanfactory beanfactory;
@Autowired
public void configExporters() {
for (ConfigBeansFacade bean : ConfigBeansFacade.values()) {
httpinvokerServiceExporter exp = new httpinvokerServiceExporter();
exp.setService(beanfactory.getBean(bean.getServiceInterface()));
exp.setServiceInterface(bean.getServiceInterface());
beanfactory.registerSingleton(bean.getCompleteBeanName(),exp);
}
}
}
我尝试了我在在线论坛上找到的每一个食谱,包括 StackOverflow,但有两个其他地方没有遇到的限制:
- 在定义导出器时,底层服务是其他 Spring bean,它们通过标准 Spring 机制使用自己的配置和依赖项进行实例化、初始化和注册。除了导出器本身之外,没有直接的类实例化。
- 我考虑过按照某些人的建议将出口商分组到一个集合中。唯一的问题是 Spring MVC 在将导出器注册到自己的配置中时使用
httpinvokerServiceExporter
Spring bean 名称作为端点 URI。因此,我必须将每个导出器注册为“一等公民”bean,并将其自己的 bean 名称注册到应用程序上下文中。
鉴于这些限制,当我尝试检索要封装到导出器中的底层服务时,我在 (1) 中遇到的问题:它们不一定准备好,这导致 UnsatisfiedDependencyException
s。
我尝试使用带 @PostContruct
注释的方法、使用 BeanPostProcessor
和使用 @Autowired
方法(如上所示)的解决方案,但没有按要求工作。
有谁知道在上述约束下在单个方法中初始化和注册多个 bean 的方法或技术?这样的方法不需要用 @Bean
、@Autowired
或任何其他特定的注释进行注释,这只是我尝试过的一个示例。
幸运的是,在客户端模块中,httpinvokerProxyfactorybean
实例只需要接口和 bean 名称,因此上面的约束 (1) 不适用。
预先感谢您提供的任何帮助...
解决方法
我不是 100% 理解您想要做什么,但我想知道您是否可以尝试自动装配实现接口的 List
bean?
例如
public interface MyService {
String getKey();
void doStuff();
}
然后根据需要实现尽可能多的这些
例如
@Component
public class FirstService implements MyService {
public String getKey() {
return "/first";
}
public void doStuff() {
...
}
}
然后有一个带有自动装配列表的工厂 bean
@Component
public class MyServiceFactory {
private final List<MyService> services;
@Autowired
public MyServiceFactory(List<MyService> services) {
this.services = services;
}
}
要添加更多 MyService 的实现,只需将它们添加为 @Component,Spring 就会神奇地将它们添加到列表中。
有时我发现通过 Map 访问我的实现很有用
@Component
public class MyServiceFactory {
private final Map<String,MyService> services;
@Autowired
public MyServiceFactory(List<MyService> services) {
this.services = services
.stream()
.collect(toMap(MyService::getKey,Function.identity()));
}
public MyService getServiceByKey(String key) {
return services.get(key);
}
}
我发现这使每个实现都保持良好且自包含(并且易于测试)。 Spring 会自动选取实现我的接口的所有组件,而无需工厂大量导入。我可以通过模拟实现列表轻松测试工厂。