Get spring bean via context using generic

那年仲夏 提交于 2020-01-12 13:57:47

问题


I have a bunch of repository beans that implement type Repository<T ? extends Node>. Now I can get a list of random nodes from the user and I want to get the appropriate repository for each node. Since Spring 4.0RC1 we can autowire repositories like this:

@Autowired Repository<SomeNode> someNodeRepository;

As documented here.

This works fine, but my question is how I can do this dynamically based on the generic type.

What I want to do is something like:

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

Where the second parameter is the generic type. This of course does not work, although it compiles.

I can't find any/the documentation on this.


回答1:


You can do something like this:

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



回答2:


If you could be sure that for every concrete subclass of Node (say SomeNode), every object of type SomeNode will be an actual SomeNode and not a subclass or a proxy, it would be easy. Just use a convention for the repository name (say SomeNodeRepository) and it would be trivial :

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

But you know that there's a high risk of getting a subclass or proxy ...

So you can try to have each Node subclass to implement a nameForRepo method :

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

and then in the subclasses

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

That way, even if you get a proxy or subclass, you will be able to do :

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

Alternatively, the method could directly return the repository name.




回答3:


If I understand well, you want to get instance of bean with Repository class and different generic type?

I'm afraide you don't have the dynamic way with spring, but I have a work around solution:

  1. Your generic type should be a field in your class, you must have a constructor in your Repository class for setting your generic type, your Repository class should be like this:

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

  2. declare a Repository bean for each Node, let's say you have Repository and Repository, if you are using xml configuration, you need to add:

    <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. 'autowire' your Repository in this way:

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

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



来源:https://stackoverflow.com/questions/30374267/get-spring-bean-via-context-using-generic

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!