Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get spring bean via context using generic

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.

like image 640
Lodewijk Bogaards Avatar asked May 21 '15 12:05

Lodewijk Bogaards


People also ask

Which is the correct way to obtain a bean from Spring context?

getBean() method. Simply put, as the name of the method also suggests, this is responsible for retrieving a bean instance from the Spring container.

How do I get Spring application context?

To get a reference to the ApplicationContext in a Spring application, it can easily be achieved by implementing the ApplicationContextAware interface. Spring will automatically detect this interface and inject a reference to the ApplicationContext: view rawMyBeanImpl. java hosted by GitHub.

How Spring define bean in context?

Bean Definition In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.


2 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]);
like image 187
jhas Avatar answered Sep 20 '22 05:09

jhas


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.

like image 37
Serge Ballesta Avatar answered Sep 22 '22 05:09

Serge Ballesta