【问题标题】:Get spring bean via context using generic使用泛型通过上下文获取spring bean
【发布时间】:2015-05-21 12:49:18
【问题描述】:

我有一堆实现Repository<T ? extends Node> 类型的存储库bean。现在我可以从用户那里获得一个随机节点列表,并且我想为每个节点获取适当的存储库。由于Spring 4.0RC1,我们可以像这样自动装配存储库:

@Autowired Repository<SomeNode> someNodeRepository;

如文档所述here

这很好用,但我的问题是如何根据泛型类型动态地做到这一点。

我想做的是这样的:

public <T extends Node> T saveNode(T node) {
    Repository<T> repository = ctx.getBean(Repository.class, node.getClass());
    return repository.save(node);
}

其中第二个参数是泛型类型。 这当然行不通,尽管它可以编译。

我找不到任何/文档。

【问题讨论】:

  • 应该为每个 bean 分配默认名称(不确定它是如何构建的)。如果您可以调试并了解如何为这种情况构建默认 bean 名称,您可以尝试按名称获取 bean
  • 什么是 ctx?看不懂

标签: java spring generics


【解决方案1】:

你可以这样做:

String[] beanNamesForType = ctx.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, node.getClass()));

// If you expect several beans of the same generic type then extract them as you wish. Otherwise, just take the first
Repository<T> repository = (Repository<T>) ctx.getBean(beanNamesForType[0]);

【讨论】:

  • 我怀疑你的代码是否会编译。据我所见,getBeanNamesForType 的参数类型与forClassWithGenerics 的返回类型不一致。
  • 我从不粘贴我没有检查过的代码。它可以编译并工作...请注意,getBeanNamesForType 有 3 个版本,其中一个接受 ResolvableType
  • 你说得对,我在看一个旧版本的 Spring。这似乎是一个合理的答案!你测试过这个吗?
  • 是的,我有,效果很好。我还搜索了如何做到这一点并最终得到了这个解决方案
  • 如果getBean()getBeansOfType() 方法也有接受ResolvableType 的重载,那就太好了。
【解决方案2】:

如果您可以确定对于Node 的每个具体子类(比如SomeNode),每个SomeNode 类型的对象都将是一个实际的SomeNode,而不是一个子类或代理,那就很容易了.只需使用存储库名称的约定(例如SomeNodeRepository),这将是微不足道的:

Repository<T> repository = ctx.getBean(node.getClass().getSimpleName()
        + "Repository", Repository.class);

但是您知道获得子类或代理的风险很高......

所以你可以尝试让每个 Node 子类实现一个nameForRepo 方法:

class Node {
    ...
    abstract String getNameForRepo();
}

然后在子类中

class SomeNode {
    static private final nameForRepo = "SomeNode";
    ...
    String getNameForRepo() {
        return nameForRepo;
    }
}

这样,即使你得到一个代理或子类,你也可以这样做:

public <T extends Node> T saveNode(T node) {
    Repository<T> repository = ctx.getBean(node.getNameForRepository()
            + "Repository", Repository.class);
    return repository.save(node);
}

或者,该方法可以直接返回存储库名称。

【讨论】:

  • 谢谢,确实不错,但由于@Autowire 在运行时完成了这项工作,我想要的实际上是可能的,所以不能令人满意。与此同时,我有自己的工作,这只是一个简单的Map&lt;Class&lt;? extends Node&gt;, Class&lt;Repository&lt;? extends Node&gt;&gt;&gt;
【解决方案3】:

如果我理解得很好,您想获取具有 Repository 类和不同泛型类型的 bean 实例吗?

恐怕你没有使用 spring 的动态方式,但我有一个解决方案:

  1. 你的泛型类型应该是你类中的一个字段,你的 Repository 类中必须有一个构造函数来设置你的泛型类型,你的 Repository 类应该是这样的:

    public class Repository<T>{ Class<T> nodeClass; public Repository(Class<?> clazz){ this.nodeClass = clazz; } // your codes... }

  2. 为每个Node声明一个Repository bean,假设你有Repository和Repository,如果你使用xml配置,你需要添加:

    <bean id="someNodeRep" class="your.package.Repository"> <constructor-arg> <value>your.package.SomeNode</value> </constructor-arg> </bean> <bean id="otherNodeRep" class="your.package.Repository"> <constructor-arg> <value>your.package.OtherNode</value> </constructor-arg> </bean>

  3. 以这种方式“自动装配”您的存储库:

    @Autowired @Qualifier("someNodeRep") Repository<SomeNode> someNodeRepository;

    @Autowired @Qualifier("otherNodeRep") Repository<OtherNode> otherNodeRepository;

【讨论】:

  • 感谢您的解决方案,但我目前没有寻找解决方法。我也有一个。
【解决方案4】:

从 Spring 5.1 开始,您可以像这样获得 Repository&lt;T&gt; 类型的 bean:

public static <T> Repository<T> getRepositoryFor(Class<T> clazz)
{
    ResolvableType type = ResolvableType.forClassWithGenerics(Repository.class, clazz);
    return (Repository<T>) context.getBeanProvider(type).getObject();
}

【讨论】:

    猜你喜欢
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 2020-06-28
    相关资源
    最近更新 更多