动态定义在 Spring 中自动装配哪个 bean(使用限定符)

2023-11-26

我有一个 Java EE + Spring 应用程序,它更喜欢注释而不是 XML 配置。 bean 始终具有原型范围。

现在,我的应用程序中的业务规则取决于用户请求所在的国家/地区。所以我会有这样的东西(请记住这个例子被大大简化了):

@Component
public class TransactionService {
    @Autowired
    private TransactionRules rules;
    //..
}


@Component
@Qualifier("US")
public class TransactionRulesForUS implements TransactionRules {
     //..
}

@Component
@Qualifier("CANADA")
public class TransactionRulesForCanada implements TransactionRules {
     //..
}

我正在寻找一种方法,使自动装配机制根据当前请求的国家/地区自动注入正确的 bean(在本例中是美国或加拿大)。该国家/地区将存储在 ThreadLocal 变量中,并且会在每个请求中发生变化。对于所有没有自己特定规则的国家来说,还会有一个全球类别。

我想我必须自定义 Spring 决定如何创建它将注入的对象的方式。我发现做到这一点的唯一方法是使用 FactoryBean,但这并不完全是我所希望的(不够通用)。我希望做这样的事情:

  1. 在 Spring 实例化对象之前,必须调用我自己的自定义代码。
  2. 如果我检测到所请求的接口有多个实现,我会在 ThreadLocal 变量中查找正确的国家/地区,并动态地将适当的限定符添加到自动连接请求中。
  3. 之后,Spring 将发挥其所有通常的魔力。如果添加限定符,则必须考虑这一点;如果没有,流程将照常进行。

我走的路正确吗?我对此有什么想法吗?

Thanks.


创建您自己的注释,用于装饰实例变量或 setter 方法,然后是处理注释并注入通用代理的后处理器,该代理在运行时解析正确的实现并将调用委托给它。

@Component
public class TransactionService {
  @LocalizedResource
  private TransactionRules rules;
  //..
}

@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}

这是算法postProcessBeforeInitialization(bean, beanName)bean 后处理器中的方法:

  1. 内省 bean 类以查找使用 @LocalizedResource 注释的实例变量或 setter 方法。将结果存储在按类名索引的缓存(只是一个映射)中。你可以使用 Spring 的InjectionMetadata以此目的。您可以通过在 Spring 代码中搜索此类的引用来查找有关其工作原理的示例。
  2. 如果 bean 存在这样的字段或方法,请使用下面描述的 InvocableHandler 创建一个代理,并将当前的 BeanFactory 传递给它(bean 后处理器必须是 ApplicationContextAware)。将该代理注入实例变量,或使用代理实例调用 setter 方法。

这是将用于创建本地化资源的代理的 InitationHandler。

public class LocalizedResourceResolver implements InvocationHandler {
  private final BeanFactory bf;
  public LocalizedResourceResolver(BeanFactory bf) {
    this.bf = bf;
  }
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String locale = lookupCurrentLocale();
    Object target = lookupTarget(locale);
    return method.invoke(target, args);
  }

  private String lookupCurrentLocale() {
    // here comes your stuff to look up the current locale
    // probably set in a thread-local variable
  }

  private Object lookupTarget(String locale) {
    // use the locale to match a qualifier attached to a bean that you lookup using the BeanFactory.
    // That bean is the target
  }
}

您可能需要对 bean 类型进行更多控制,或者在 InvocableHandler 中添加请求的 bean 类型。

接下来的事情是自动检测给定接口的实现(这些接口是本地相关的),并使用与区域设置相对应的限定符来注册它们。您可以实施一个BeanDefinitionRegistryPostProcessor or BeanFactoryPostProcessor为此目的,为了添加新的BeanDefinitions 到注册表,并带有适当的限定符,每个区域设置感知接口的实现都有一个限定符。您可以通过以下命名约定来猜测实现的区域设置:如果区域设置感知接口称为 TransactionRules,则实现可能在同一包中命名为 TransactionRules_ISOCODE。

如果您无法承担这样的命名约定,则需要某种类路径扫描+一种猜测给定实现的语言环境的方法(可能是实现类上的注释)。类路径扫描是可能的,但相当复杂且缓慢,因此请尽量避免它。

以下是所发生情况的摘要:

  1. 当应用程序启动时,将发现 TransactionRules 的实现,并为每个实现创建 bean 定义,并使用与每个实现的区域设置相对应的限定符。这些 bean 的 bean 名称不相关,因为查找是根据类型和限定符执行的。
  2. 在执行期间,在线程局部变量中设置当前区域设置
  3. 查找您需要的 bean(例如 TransactionService)。后处理器将为每个 @LocalizedResource 实例字段或 setter 方法注入一个代理。
  4. 当调用 TransactionService 上的方法并最终进入某些 TransactionRules 的方法时,绑定到代理的调用处理程序会根据存储在线程局部变量中的值切换到正确的实现,然后将调用委托给该实现。

这并不是微不足道的,但它确实有效。这实际上是 Spring 处理 @PersistenceContext 的方式,除了实现查找之外,这是用例的附加功能。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态定义在 Spring 中自动装配哪个 bean(使用限定符) 的相关文章

  • SAML 服务提供商 Spring Security

    当使用预先配置的服务提供者元数据时 在 Spring Security 中 是否应该有 2 个用于扩展元数据委托的 bean 定义 一份用于 IDP 元数据 一份用于 SP 元数据
  • 如何使用 Java 处理 Selenium WebDriver 中的新窗口?

    这是我的代码 driver findElement By id ImageButton5 click Thread sleep 3000 String winHandleBefore driver getWindowHandle drive
  • 按第一列排序二维数组,然后按第二列排序

    int arrs 1 100 11 22 1 11 2 12 Arrays sort arrs a b gt a 0 b 0 上面的数组已排序为 1 100 1 11 2 12 11 22 我希望它们按以下方式排序a 0 b 0 首先 如果
  • 如何在不超过最大值的情况下增加变量?

    我正在为学校开发一个简单的视频游戏程序 我创建了一个方法 如果调用该方法 玩家将获得 15 点生命值 我必须将生命值保持在最大值 100 并且由于我目前的编程能力有限 我正在做这样的事情 public void getHealed if h
  • 如何安全地解决这个 Java 上下文类加载器问题?

    我的数百名用户中只有一位在启动我的 Java 桌面应用程序时遇到问题 他只有大约三分之一的时间开始 另外三分之二的时间在启动时抛出 NullPointerException Exception in thread AWT EventQueu
  • Java 文件上传速度非常慢

    我构建了一个小型服务 它从 Android 设备接收图像并将其保存到 Amazon S3 存储桶中 代码非常简单 但是速度非常慢 事情是这样的 public synchronized static Response postCommentP
  • Hazelcast 分布式锁与 iMap

    我们目前使用 Hazelcast 3 1 5 我有一个简单的分布式锁定机制 应该可以跨多个 JVM 节点提供线程安全性 代码非常简单 private static HazelcastInstance hInst getHazelcastIn
  • 使用过滤器@ComponentScan所有包的危险

    我现在正在开发一个概念应用程序 我想使用组件扫描来使用特定的自定义 Spring 元注释来获取类路径上任何位置的所有类 我的注释如下所示 Target value ElementType TYPE Retention value Reten
  • 如何在 Java 中测试一个类是否正确实现了 Serialized(不仅仅是 Serialized 的实例)

    我正在实现一个可序列化的类 因此它是一个与 RMI 一起使用的值对象 但我需要测试一下 有没有办法轻松做到这一点 澄清 我正在实现该类 因此在类定义中添加 Serialized 很简单 我需要手动序列化 反序列化它以查看它是否有效 我找到了
  • 有没有一种快速方法可以从 Jar/war 中删除文件,而无需提取 jar 并重新创建它?

    所以我需要从 jar war 文件中删除一个文件 我希望有类似 jar d myjar jar file I donot need txt 的内容 但现在我能看到从 Linux 命令行执行此操作的唯一方法 不使用 WinRAR Winzip
  • Java整数双除法混淆[重复]

    这个问题在这里已经有答案了 方案1 int sum 30 double avg sum 4 result is 7 0 not 7 5 VS 方案2 int sum 30 double avg sum 4 0 Prints lns 7 5
  • Jersey 客户端请求中未设置 Content-Length-Header

    我正在使用 Jersey Client 访问网络服务 如下所示 response r accept MediaType TEXT PLAIN TYPE header content length 0 post String class 其中
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • 替换后增量

    我自己已经有一个问题了 但我想扩展它后增量示例 https stackoverflow com questions 51308967 post increment with example char a D int b 5 System o
  • HQL Hibernate 内连接

    我怎样才能在 Hibernate 中编写这个 SQL 查询 我想使用 Hibernate 来创建查询 而不是创建数据库 SELECT FROM Employee e INNER JOIN Team t ON e Id team t Id t
  • javafx android 中的文本字段和组合框问题

    我在简单的 javafx android 应用程序中遇到问题 问题是我使用 gradle javafxmobile plugin 在 netbeans ide 中构建了非常简单的应用程序 其中包含一些文本字段和组合框 我在 android
  • Eclipse 中 Spring MVC 模型对象的 (jsp /jstl) 视图中的代码辅助

    在 Spring MVC 中 当将对象放置在视图模型中时 如下所示 public String getUser Model model fetch user model addAttribute user user return viewN
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • 在 RESTful Web 服务中实现注销

    我正在开发一个需要注销服务的移动应用程序 登录服务是通过数据库验证来完成的 现在我陷入了注销状态 退一步 您没有提供有关如何在应用程序中执行身份验证的详细信息 并且很难猜测您在做什么 但是 需要注意的是 在 REST 应用程序中 不能有会话
  • GUI Java 程序 - 绘图程序

    我一直试图找出我的代码有什么问题 这个想法是创建一个小的 Paint 程序并具有红色 绿色 蓝色和透明按钮 我拥有我能想到的让它工作的一切 但无法弄清楚代码有什么问题 该程序打开 然后立即关闭 import java awt import

随机推荐