基于主机名加载 Bean

Loading Beans based on hostname

我正在 Spring 引导中编写服务,这些服务从 Spring 云获取配置。这些服务是多租户的,租户基于主机名。

我现在拥有的是

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());
    }
}

虽然我现在的工作正常,但我不喜欢我需要为我的控制器可能具有的每个依赖项创建一个工厂。我想让我的代码看起来像这样...

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 进行注入。

创建范围:

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();
}

没有测试但在我的项目中使用了它。应该可以。

tutorial spring custom scope