问题
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:
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... }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>'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