Get spring bean via context using generic - java

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.

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

Since Spring 5.1 you can get a bean of type Repository<T> like that:
public static <T> Repository<T> getRepositoryFor(Class<T> clazz)
{
ResolvableType type = ResolvableType.forClassWithGenerics(Repository.class, clazz);
return (Repository<T>) context.getBeanProvider(type).getObject();
}

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.

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;

Related

Java extract generic type parameters with reflection from interface

I'm writing a custom Java annotation for processing CrudRepositories with Reflection in Java Spring. With the org.reflections.reflections library. I'm getting all interfaces annotated with my annotation as a class file like so:
Reflections reflections = new Reflections("basePackage");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(MyAnnotation.class);
Only interfaces, which at some point extend JpaRepository are annotated with my #MyAnnotation at the class level.
My repository structure is as follows:
There are two cases,
first case:
public interface SomeRepo extends JpaRepository<SomeEntity, Long> {...}
the second case is composed out of a inheritance hierarchy:
public interface SuperClassRepo <T extends SomeRandomEntity> extends JpaRepository<T, String> {...}
public interface SubClassRepo extends SuperClassRepo<SubEntityOfSomeRandomEntity> {...}
My goal is now to extract the generic type parameters of the underlying JpaRepository.
I achieved to do that if the annotated class is a Java class, not an interface. How can I achieve the same for an interface? I guess I'm also having trouble because of the inheritance. I guess I have to get the "super class" until I reach the JpaRepository and then somewhat extract the generic type arguments.
Help is very much appreciated, thanks in advance
I found a solution by looking at the GenericsUtils#getParameterType as suggested:
private static Class<?> extractKeyFromRepository(Class<?> repository) {
ResolvableType currentType = ResolvableType.forType(repository);
ResolvableType resolvableType = currentType.getInterfaces()[0];
if (JpaRepository.class.equals(resolvableType.getRawClass())) {
ResolvableType[] generics = resolvableType.getGenerics();
ResolvableType generic = generics[1];
return generic.resolve();
} else {
return extractKeyFromRepository(resolvableType.toClass());
}
}
This works only because I ensure beforehand, that what I'm putting in this method is valid. I do it like so, also I pay attention that only one interface is extended, by extending more than one interface one has to figure out which of these interfaces is the JpaRepository:
if (!repository.isInterface()) {throw new IllegalArgumentException();}
if (!JpaRepository.class.isAssignableFrom(repository)) {throw new IllegalArgumentException();}

How to force Spring Data to create query methods with entity runtime type?

I've got around 5 objects that I want to do similar things with.
I figured out that not to polute the code I will put a logic for those objects in one place.
public class MetaObjectController<T extends MetaObject> {
#Autowired
private final MetaObjectRepository<T> repository;
// generic logic
Here's how repository looks:
public interface MetaObjectRepository<T extends MetaObject> extends GraphRepository<T> {
T findByName(String name);
}
Now, I create concrete class which uses delegation:
public class ExperimentalController {
#Autowired
private final MetaObjectController<MetaCategory> metaController;
#RequestMapping(method = RequestMethod.POST)
public void add(#RequestBody MetaCategory toAdd) {
metaController.add(toAdd);
}
Now, when I look at the generated queries I see, that although instantiated correctly, repository puts MetaObject as an entity name instead of runtime type.
Is there a way to force the repository to use runtime type?
Please don't advise to put a #Query annnotation. That's not what I am looking for.
This is most probably due to type erasure: at runtime there is only the type constraint available which is MetaObject. If you want to use (via spring-data) the actually relevant subclass you will have to create explicit interfaces of the MetaObjectRepository like this:
public class Transmogrifier extends MetaObject
public interface MetaTransmogrifierRepository
extends MetaObjectRepository<Transmogrifier> {}

get annotation on method from a CDI managed bean

I want to retrieve an annotation (a custom written one) from a method. Usually I can ask the classloader by accessing
class.getMethod("methodName").getAnnotation("annotationName")
But if the bean is managed by a CDI container (I am using OpenWebBeans) the class is enhanced at runtime. Then I have to use the superclass to ask for annotations. Currently I try to detect if the class is managed by looking for "$$" in the classname. But that seems to be a very dirty solution to me.
Is there any good way to retrieve anntations from a CDI managed bean?
In detail my code is something like that:
I created an annotation "Coolnessfactor" to mark a method to be very cool :-)
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Coolnessfactor {
CoolnessValue value();
}
Via the enumeration CoolnessValue I want to specify how cool the method implementation is.
public enum CoolnessValue {
POOR, VERY_COOL, UNBELIEVABLE;
}
Then I mark different methods in my business classe with this annotation, fe:
#Override
#Coolnessfactor(CoolnessValue.POOR)
public void getSingleObjectWithDetails(final Integer techId) {
return this.dao.findCompleteDataByPrimaryKey(techId);
}
Now I want to analyse the value of the annotation which marks the different method. I have to do it in a CDI-Decorator, therefore I cannot do it with an interceptor binding.
At the moment my approach is to use the reflection API to retrieve the annotation value:
public static <A extends Annotation> Map<String, A> getAnnotatedMethodsOfClass(final Class<?> clazz,
final Class<A> annotationClazz) {
final Map<String, A> annotationMap = new HashMap<String, A>();
Method[] declaredMethods;
if (clazz.getName().contains("$$")) {
declaredMethods = clazz.getSuperclass().getDeclaredMethods();
} else {
declaredMethods = clazz.getDeclaredMethods();
}
for (final Method m : declaredMethods) {
if (m.isAnnotationPresent(annotationClazz)) {
annotationMap.put(m.getName(), m.getAnnotation(annotationClazz));
}
}
return annotationMap;
}
But this looks very awful to me. Especcially the detection of a class which is enhanced by the CDI implementation is very bad.
Maybe try it with BeanManager - you will want to use it to get hold of a Bean<?> instance of your bean. The approaches differ here, based on what kind of bean it is. Shuffle through the API and find your way.
Once you get Bean<?> you should be able to use getBeanClass() and with that you gain access to methods and their annotations.

Spring bean interface declaring

I have a work class :
public class WorkClass implements ApplicationContextAware {
... // has access to ApplicationContext
}
Have some useful interface :
public interface UsefulInterface {
void doUseful();
}
Have some impl class that can do much more:
public class CanDoAlmostEverything implements UsefulInterface {
...
}
I want to provide UsefulInterface implementation (via CanDoAlmostEverything) to WorkClass using Spring, but NOT to access any other CanDoAlmostEverything methods exept "doUseful"
In other words I want to declare my bean[s] like :
<bean id="workerA" interface="UsefulInterface" class="CanDoAlmostEverything"/>
<bean id="workerB" interface="UsefulInterface" class="AnotherUsefulImpl"/>
WorkClass will know about interface impl only during runtime and code must look like:
String todayWorker = getWorkerNameFromDataBase();
UsefulInterface worker = appCtx.getBean(todayWorker, UsefulInterface.class);
worker.doUseful();
Is it possible? And how it must look like?
I don't recommend you to use getBean this way. In the Spring documentation, it is written that it could be bad for performance.
http://docs.spring.io/spring/docs/1.0.2/api/org/springframework/beans/factory/BeanFactory.html#getBean%28java.lang.String%29
Return an instance (possibly shared or independent) of the given bean
name. Provides a measure of type safety by throwing an exception if
the bean is not of the required type.
Note that callers should retain references to returned objects. There
is no guarantee that this method will be implemented to be efficient.
For example, it may be synchronized, or may need to run an RDBMS
query.
Will ask the parent factory if the bean cannot be found in this
factory instance.
It really depends of what do you want to do. Did you tought that you Workclass could be a bean ?
public class WorkClass implements ApplicationContextAware {
private UsefulInterface workerA;
private UsefulInterface workerB;
public void setWorkerA(UsefulInterface workerA) {
this.workerA = workerA;
}
public void setWorkerB(UsefulInterface workerB) {
this.workerB = workerB;
}
public void work() {
UsefulInterface workerToUse;
if(condition) {
workerToUse = workerA;
} else {
workerToUse = workerB;
}
// treatment
}
}
Here the configuration file :
<bean id="workerA" interface="UsefulInterface" class="CanDoAlmostEverything"/>
<bean id="workerB" interface="UsefulInterface" class="AnotherUsefulImpl"/>
<bean id="mainWorker" class="package.of.WorkClass">
<property name="workerA" ref="workerA" />
<property name="workerB" ref="workerB" />
</bean>
Your main class will have to call getBean, but only one time to get the instance of WorkClass.

can guice create a factory for me?

I have the following factory class:
public class MessagePresenterCreator implements IPresenterFactory{
#Override
public MessagePresenter createPresenter(Message m) {
if (m instanceof Letter) {
return new LetterPresenter();
}
if (m instanceof Cable) {
return new CablePresenter();
}
if (m instanceof Postcard) {
return new PostcardPresenter();
}
throw new IllegalArgumentException();
}
}
Can I configure a guice to auto-generate and supply such factory by the following interface:
public interface IPresenterFactory {
public abstract MessagePresenter createPresenter(Message m);
}
I like to think of factories in two ways - factories that merely assemble objects from components, and factories that do conditional logic in that assembly.
Your factories have logic behind them. Guice can't automate that logic, because it simply handles the wiring up of dependencies. If you have a type that is created that needs some amount of injected dependencies and some things that are provided just at creation time, then the auto-wiring of factories can be done with guice's AssistedInject extension. This would let you give a factory interface, annotate any fields in the created type with #AssistedInject, and guice's extension would create a factory implementation class which would inject anything bound in the Injector, and also pass through those create() parameters. But in such a case, all Guice and AssistedInject are doing is pulling pieces together - wiring things up according to a recipe specified in advance. It's not making decisions about them at the last minute.
You're providing conditional creation of objects. That's not going to work.
An idea above mentions making a factory that depends on mapping of type to presenter - something like:
Map<Class<? extends Message>, Class<? extends MessagePresenter>>
This is a good approach, if you combine it with Multibinder's MapBindings. (I gotta beef up those docs... hmm)
With this approach you can create an extensible factory - defining the initial mappings of Message -> MessagePresenter subclasses, but leave the possibility open for additional mappings later without having to change your factory - just bind more mappings on the multibinder, like so:
MapBinder<String, Snack> mapbinder = MapBinder.newMapBinder(
binder(),
new TypeLiteral<Class<? extends Message>>(){},
new TypeLiteral<Class<? extends MessagePresenter>>(){});
mapbinder.addBinding(MyMessage.class).toInstance(MyMessagePresenter.class);
mapbinder.addBinding(YourMessage.class).toInstance(YourMessagePresenter.class);
And you can do this in as many modules as you like, adding more types between which to switch, using the mapping.
No guice does not have a factory like that built in. You could write a factory that took a Map<Class<? extends Message>, Class<? extends MessagePresenter> and have that use reflection. That way you can manage it in a guice module if that is your goal.
public class MessagePresenterCreator implements IPresenterFactory{
private final Map<Class<? extends Message>, Class<? extends MessagePresenter> mapping;
public MessagePresenterCreator(Map<Class<? extends Message>, Class<? extends MessagePresenter> mapping) {
this.mapping = mapping;
}
#Override
public MessagePresenter createPresenter(Message m) {
Class<? extends MessagePresenter> clazz = mapping.get(m);
if (clazz == null) {
throw new UnsupportedOperationException();
}
return clazz.newInstance();
}
}

Categories

Resources