我正在 Spring boot 中编写服务,这些服务从 Spring cloud 获取配置。这些服务是 Multi-Tenancy 的,租户基于主机名。
我现在拥有的是
public class MyController {
@Autowired
public MyController(MyServiceFactory factory) {
...
}
@RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(@RequestHeader header, @PathVariable id) {
return factory.getMyService(header).handle(id);
}
}
MyServiceFactory 看起来像......
public class MyServiceFactory {
private final HashMap<String, MyService> serviceRegistry = new HashMap<>();
public MyService getMyService(String key) {
return serviceRegistry.get(key);
}
MyServiceFactory withService(String key, MyService service) {
this.serviceRegistry.put(key, service);
return this;
}
}
然后在配置文件中
@Configuration
public ServiceFactoryConfiguration {
@Bean
public MyServiceFactory getMyServiceFactory() {
return new MyServiceFactory()
.withService("client1", new MyService1())
.withService("client2", new MyService2());
}
}
虽然我现在所拥有的可以工作,但我不喜欢我需要为 Controller 可能拥有的每个依赖项创建一个工厂。我想让我的代码看起来像这样......
public class MyController {
@Autowired
public MyController(MyService service) {
...
}
@RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(@PathVariable id) {
return service.handle(id);
}
}
使用类似的配置文件
@Configuration
public class MyServiceConfiguration() {
@Bean
@Qualifier("Client1")
public MyService getMyService1() {
return new MyService1();
}
@Bean
@Qualifier("Client2")
public MyService getMyService2() {
return new MyService2();
}
}
如果我在应用程序启动时使用配置文件,我可以获得我想要编写的代码。但我希望有许多不同的 DNS 记录指向相同的实例(池),并且让一个实例能够处理不同客户端的请求。我希望能够根据每个请求交换配置文件。
这可以做到吗?
最佳答案
Spring 配置文件在这里没有帮助,每个客户端都需要一个应用程序上下文,而这似乎不是您想要的。
相反,您可以使用作用域 bean。 创建范围为“client”的依赖于客户端的 bean:
@Bean
@Scope(value="client",proxyMode = ScopedProxyMode.INTERFACES)
@Primary
MyService myService(){
//does not really matter, which instance you create here
//the scope will create the real instance
//may be you can even return null, did not try that.
return new MyServiceDummy();
}
至少有 3 个 MyService 类型的 bean:范围内的 bean,每个客户端一个。 @Primary 注释告诉 spring 始终使用作用域 bean 进行注入(inject)。
创建范围:
public class ClientScope implements Scope {
@Autowired
BeanFactory beanFactory;
Object get(String name, ObjectFactory<?> objectFactory){
//we do not use the objectFactory here, instead the beanFactory
//you somehow have to know which client is the current
//from the config, current request, session, or ThreadLocal..
String client=findCurrentClient(..);
//client now is something like 'Client1'
//check if your cache (HashMap) contains an instance with
//BeanName = name for the client, if true, return that
..
//if not, create a new instance of the bean with the given name
//for the current client. Easiest way using a naming convention
String clientBeanName=client+'.'+name;
Object clientBean=BeanFactory.getBean(clientBeanName);
//put in cache ...
return clientBean;
};
}
您的客户端特定 bean 的配置如下:
@Bean('Client1.myService')
public MyService getMyService1() {
return new MyService1();
}
@Bean('Client2.myService')
public MyService getMyService2() {
return new MyService2();
}
没有测试它,但在我的项目中使用了它。应该可以工作。
关于java - 根据主机名加载 Bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35939991/