Get spring bean through context using generic

I have a pile of beans repository that implements type Repository<T ? extends Node>

. Now I can get a list of random nodes from the user, and I want to get the corresponding repository for each node. Since Spring 4.0RC1 , we can auto-layer repositories like this:

@Autowired Repository<SomeNode> someNodeRepository;

      

As described here .

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

I want to do something like:

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

      

If the second parameter is generic type. This of course doesn't work, although it does compile.

I cannot find / documentation on this.

+3


source to share


3 answers


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

      

+6


source


If I understand well, you want to get an instance of a bean with a repository class and some other generic type?

I'm sure you don't have a dynamic path with spring, but I have a solution to work:



  • Your generic type should be a field of your class, you should have a constructor in your Repository class to set your generic type, your repository class should look like this:

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

  • declare a bean repository for each Node, let's say you have a repository and a repository, if you are using xml config 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>

  • 'autowire' your repository this way:

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

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

+1


source


If you could be sure that for each specific subclass Node

(say SomeNode

) each object of the type SomeNode

will be actual SomeNode

and not a subclass or 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 there is a high risk of getting a subclass or proxy ...

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

:

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

      

and then in subclasses

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

      

This way, even if you get a proxy or subclass, you can:

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 can directly return the name of the repository.

0


source







All Articles