java - 根据主机名加载 Bean

标签 java spring spring-cloud

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

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

tutorial spring custom scope

关于java - 根据主机名加载 Bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35939991/

相关文章:

java - Google Compute Engine 上 Hadoop 中的标准输出

java - 使用http发送二进制数据

mysql - Spring 启动: MySQL Duplicate key error when trying to map customers to items

java - 自定义功能区运行状况检查 URL

java - 为什么Spring Cloud @EnableSidecar使用@EnableCircuitBreaker

java - 线程在终止之前不会输出流

java - Izpack,Java - 获取资源路径作为变量

java - 在非字符串注释字段 spring 中使用 yml 文件中的值

java - Jedis key 过期

spring-boot - 如何在微服务架构中实现基于角色的安全