如何选择Spring bean在运行时调用

时间:2019-02-09 13:49:37

标签: spring-boot spring-security

原始标题:如何为同一接口声明配置多个bean并在运行时选择正确的bean

我最近有一个项目,该项目对实施Web应用程序有以下要求:

当用户登录系统时,她将指定范围(B2C,B2B)。在使用该应用程序时,必须根据当前登录的范围将对后端系统的某些调用路由到后端系统的不同实例。

该Web应用程序由两部分组成(前面是SPA,后面是REST端点的Spring Boot应用程序)和后端系统实例的集合,例如B2C的存档和B2B的存档。

当前实现处理REST请求,并确定要调用的归档的登录范围。这可行,但是我想使它声明式而不是显式编码。因此,为不同的作用域添加新的后端实例应该很容易。

对于初学者来说,我想定义两个bean,一个用于访问B2C存档,另一个用于访问B2B存档。两者将实现相同的接口;实际上,它们是具有不同配置值的同一类的实例。

处理REST调用后,我希望spring根据当前登录名(B2C或B2B)的范围选择要调用的正确bean。

我对如何解决此问题有不同的想法(作用域代理,AOP,自定义AutowireCandidateResolver,对象池),但目前我陷入困境,无法说出自己是否在正确的轨道上。

有人做过类似的事情吗?

更新

感谢Fritz DuchardtKen Bekov使用工厂+范围代理并将它们返回给调用代码的想法。

更新2

我刚刚实现了一个能够运行以下测试的spring扩展(此处的完整代码:https://gitlab.com/thuri/service-provider-proxy)。

内部类只是为了将测试保持在一起。当它们在分离文件中时,它也应该起作用。

主要要点是,该接口由两个不同的bean实现,并且spring在运行时通过评估注释中的spEL表达式来确定哪个bean应该实际处理调用


  @Test
  public void testProxiedResourceInjectionWithField() {

    caseSwitch.switchValue = "B2B";
    assertEquals("Well you know ... ", client.proxiedService.doCoolStuff());

    caseSwitch.switchValue = "B2C";
    assertEquals("This will do ...", client.proxiedService.doCoolStuff());
  }


  public static class ProxiedServiceClient {
    @Autowired 
    public ProxiedServiceInterface proxiedService;
  }

  @ServiceProviderProxy
  public static interface ProxiedServiceInterface {
    public String doCoolStuff();
  }

  @ProxiedService(expression = "#{switch.switchValue == 'B2B'}")
  public static class B2BServiceImpl implements ProxiedServiceInterface {
    @Override
    public String doCoolStuff() {
      return "Well you know ... ";
    }
  }

  @ProxiedService(expression = "#{switch.switchValue == 'B2C'}")
  public static class B2CServiceImpl implements ProxiedServiceInterface {
    @Override
    public String doCoolStuff() {
      return "This will do ...";
    }
  }

  public static class CaseSwitch {
    public String switchValue = "";
  }

1 个答案:

答案 0 :(得分:1)

这可以与设置为React: Update Child Component Without Rerendering ParentSpring Factory Bean一起使用,例如:

@Bean(name = "archive")
@Scope(value= "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public ArchiveFactory archiveFactory() {
    ArchiveFactory factory = new ArchiveFactory();
    return factory;
}

Spring将在运行时使用AOP代理创建新bean,例如应您的要求,使用您的工厂bean实现。您可以使用本地线程来传递当前上下文。