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.
source to share
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]);
source to share
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;
source to share
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.
source to share