使用泛型通过上下文获取spring bean

问题描述 投票:0回答:4

我有一堆实现类型

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

@Autowired Repository<SomeNode> someNodeRepository;

如记录此处

这工作得很好,但我的问题是如何根据泛型类型动态地执行此操作。

我想做的是:

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

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

我找不到任何相关的/文档。

java spring generics
4个回答
34
投票

你可以这样做:

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]);

7
投票

从 Spring 5.1 开始,你可以获得

Repository<T>
类型的 bean,如下所示:

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

0
投票

如果您可以确定对于

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

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


0
投票

如果我理解得很好,你想获得一个具有 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;
    
© www.soinside.com 2019 - 2024. All rights reserved.