How does spring autowire generic converters? - java

Occasionally I've noticed interesting thing:
I've implemented spring Converter interface:
#Component
public class MyConverter implements Converter<MyClass1, MyClass2> {
#Override
public MyClass2 convert(MyClass1 source) {
// doesn't matter
}
}
In controller I autowire it like this
#Autowire
Converter<MyClass1, MyClass2> myConverter;
Surprise but spring inject proper class.
According my information spring autowires beans in runtime. Also I know that in Runtime generic types erasure.
I am tried to understand Spring soure but it hard for me.
Can you explain idea how does spring resolve this situation?

Even though type erasure happens, some of the type-parameter information is not actually erased, but it's rather preserved elsewhere at Runtime.
For this class:
public class MyConverter implements Converter<MyClass1, MyClass2>
the parameterized types for the super-interface (Converter) are preserved, because the JVM should know that the compiled MyConverter implements an abstract method with a signature that actually contains these two types (MyClass1 and MyClass2).
In order to demonstrate this, you can try out the following snippet in a simple main method - here, the parameterized information for the super-interface is restored at Runtime with Reflection:
Type[] interfaces = MyConverter.class.getGenericInterfaces();
ParameterizedType interfaceType = (ParameterizedType) interfaces[0];
Type[] types = interfaceType.getActualTypeArguments();
for (Type argument : types) {
System.out.println(argument.getTypeName());
}
These Reflection-related classes (Type, ParameterizedType, etc.) are the ones which are actually being used by the Spring's ResovableType class, which is responsible for detecting the best candidate for wiring, based on the provided type-information.

It is because of what they describe in their blogpost here.
Quote,
Starting with Spring Framework 4.0, Spring will automatically consider generics as a form of #Qualifier. Behind the scenes, the new ResolvableType class provides the logic of actually working with generic types. You can use it yourself to easily navigate and resolve type information.
So the answer is in this class and in this class (and in this class).

Related

Why does JPMS allow annotation types as services

In introducing JPMS services, section 7.7.4 of the Java Language Specification notes that "The service type must be a class type, an interface type, or an annotation type."
I'm struggling to see the point of permitting an annotation. My understanding is that the JPMS notion of a service is something for which we expect to select an implementation at runtime. It also seems that, to be useful, the implementation needs at least the possibility of being something other than the original class that identifies the service being requested. But I believe an annotation cannot use "extends" so this could never happen? From that, I reach the belief that if I try to make a service out of an annotation type, I'd inevitably end up with a situation where the only thing that could ever be returned by a service lookup on, for example, SomeAnnotation.class would be exactly SomeAnnotation. That seems pointless, so I must assume I'm missing something.
Can anyone shed light on this, and perhaps offer examples of how an annotation might be a "service"?
It seems that you have missed another addition to the service providers. Within a named module, a service provider may return the implementation from a static method:
If the service provider declares a provider method, then the service loader invokes that method to obtain an instance of the service provider. A provider method is a public static method named "provider" with no formal parameters and a return type that is assignable to the service's interface or class.
In this case, the service provider itself need not be assignable to the service's interface or class.
from ServiceLoader
So the following would work:
module Example.Module {
uses example.Anno;
provides example.Anno with example.AnnoProvider;
}
package example;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Anno {
int value();
}
package example;
#Anno(42)
public class AnnoProvider {
public static Anno provider() {
return AnnoProvider.class.getAnnotation(Anno.class);
}
}
package example;
import java.util.ServiceLoader;
public class ServiceUser {
public static void main(String[] args) {
for(Anno a: ServiceLoader.load(Anno.class)) {
System.out.println(a.value());
}
}
}
While in Java an annotation interface cannot explicitly extend any interfaces (but implicitly it always extends java.lang.annotation.Annotation), it can be implemented. I.e. it is syntactically possible to write a concrete class implementing an annotation interface, though according to JLS 9.6. Annotation Types such a class does not represent an annotation type:
a subclass or subinterface of an annotation type is never itself an
annotation type
Thus I believe that the original question boils down to "why would anyone want to explicitly implement an annotation interface?". This question has already been asked and answered on SO: Use cases for implementing annotations. The accepted answer there proposes to do this in order to partially overcome the limitation that a value of an annotation element must be either a constant expression, or a class literal, or an enum constant (see JLS 9.7.1. Normal Annotations): one may implement an annotation interface to "annotate" the implementing class with an "annotation" that includes dynamic data taken e.g. from a config file, a database, etc. Obviously, such a technique also requires small changes in the code that reads annotations, as the class implementing an annotation interface is not actually annotated, but instead its instance can be used as an instance of an annotation as if it was retrieved e.g. via java.lang.Class.getAnnotationsByType.

Spring 4 not automatically qualifying generic types on autowire

PROBLEM HAS BEEN IDENTIFIED, POST UPDATED (Scroll to bottom)
I am developing a desktop application currently using Spring (spring-context, 4.1.6.RELEASE) for IoC and dependency injection. I am using an annotation configuration, using #ComponentScan. The issue I am experiencing is supposed to be implemented as a feature in 4.X.X, as it states here and here, but I am getting the old 3.X.X exception.
I have a parameterised interface that represents a generic repository:
public interface DomainRepository<T> {
T add(T entity) throws ServiceException, IllegalArgumentException;
// ...etc
}
I then have two concrete implementations of this, ChunkRepositoryImpl and ProjectRepositoryImpl, which are parameterised accordingly. They share some common implementation from an abstract class, but are declared like so:
#Repository
public class ChunkRepositoryImpl extends AbstractRepositoryImpl<Chunk> implements DomainRepository<Chunk> {
// ...+ various method implementations
}
#Repository
public class ProjectRepositoryImpl extends AbstractRepositoryImpl<Project> implements DomainRepository<Project> {
// ...+ various method implementations
}
My understanding of the above links leads me to believe that I should be able to autowire these without needing to manually specify the beans via #Qualifier. However, when I do so:
#Autowired
private DomainRepository<Project> repository;
I get the following exception (preceded by a long stack trace of course):
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.foo.bar.repositories.DomainRepository] is defined: expected single matching bean but found 2: chunkRepositoryImpl,projectRepositoryImpl
Can anybody shine a light as to why this might be happening? I would expect this exception in 3.X.X, but it should not happen in 4.X.X. What is the difference between my situation, and the one described here?
UPDATE
I have discovered the source of the problem. One of the methods in my DomainRepository<T> interface is marked as #Async, and makes use of Spring's asynchronous capabilities. Removing this means that the beans are correctly qualified. I hypothesize that Spring transforms classes with #Async methods under the hood into some other class, and this process strips the type information, meaning that it can't tell the beans apart.
This means I now have two questions:
Is this intended behaviour?
Can anybody suggest a workaround?
Here is a project demonstrating the problem. Simply remove the #Async annotation from the DomainRepository<T> interface, and the problem dissappears.
I hypothesize that Spring transforms classes with #Async methods under
the hood into some other class, and this process strips the type
information, meaning that it can't tell the beans apart.
Yes. That's exactly what happens.
Spring 4 supports injecting beans by their full generic signature. Given the injection target
#Autowired
private DomainRepository<Project> repository;
and a bean of type ProjectRepositoryImpl, Spring will properly resolve and inject that bean into the field (or method argument, or constructor argument).
However, in your code, you don't actually have a bean of type ProjectRepositoryImpl, not even of type DomainRepository<Project>. You actually have a bean of type java.lang.Proxy (actually a dynamic subclass of it) that implements DomainRepository, org.springframework.aop.SpringProxy, and org.springframework.aop.framework.Advised.
With #Async, Spring needs to proxy your bean to add the asynchronous dispatching behavior. This proxy, by default, is a JDK proxy. JDK proxies can only inherit the interfaces of the target type. JDK proxies are produced with the factory method Proxy#newProxyInstance(...). Notice how it only accepts Class arguments, not Type. So it can only receive a type descriptor for DomainRepository, not for DomainRepository<Chunk>.
Therefore, you have no bean that implements your parameterized target type DocumentRepository<Project>. Spring will fall back to the raw type DocumentRepository and find two candidate beans. It's an ambiguous match so it fails.
The solution is to use CGLIB proxies with
#EnableAsync(proxyTargetClass = true)
CGLIB proxies allow Spring to get the full type information, not just interfaces. So your proxy will actually have a type that is a subtype of ProjectRepositoryImpl, for example, which carries with it the DocumentRepository<Project> type information.
A lot of the above are implementation details and defined in many separate places, official documentation, javadoc, comments, etc. Use them carefully.

Spring AOP - using joinpoint to get generic class type (JAVA)

I am trying to obtain the generic type of a class using the JoinPoint in Spring AOP. But there doesnt seem to be a way to do that.
This is the class whose methods I am intercepting.
public abstract class AbstractHibernateDaoBase<T, ID extends Serializable> implements BaseDao<T, ID>, PersistenceAware
{
//Some methods
}
I am able to intercept these methods and execute advices. But what I need is the type T (using AOP) which gets its value at runtime.
Is there a way to do this?
What you want would be possible in .NET languages like C#, but not in Java due to the way generics are implemented. Because at the time generics were introduced backward byte (and even source) code compatibility was one of the main design goals, generic type information is unavailable during runtime due to type erasure.
Bottom line: No, you cannot determine T during runtime by any other means than creating a T object as a workaround (or using an existing one you already have) and determining its type via myT.getClass() or myT instanceof Foo.

Java, Named Bean, wildcard?

We need to retrieve multiple class-instances during runtime, withoud manually maintaining a list of all available Types.
Possible Approaches:
Retrieve an instance of each type annotated with #xy
Retrieve an instance of each type implementing the interface iXY
Retrieve an instance of each type, named like xy%
Requirements:
Instance should be container managed. (CDI)
If possible, no reflection
What I tried:
Annotations, but it seems that the only way of reading annotations during runtime is reflection. Also this would return me the class-type of which I would need to create instances. (But unmanaged then)
Same for interface implementations.
The perfect way would be to evaluate an el-expression, containing a wildcard:
List<IMyInterface> instances = evaluateExpression("#{commonNameStart*}");
of couse, all the Names could be maintained somewhere in a properties file - but if possible I would like avoid that.
Basically I need something like the Hibernate-Entity-Scanner for custom Annotations.
Is there a nice way to solve this?
This can generally be achieved by injecting the interface javax.enterprise.inject.Instance, appropriately parameterized. At least the two first suggested approaches are possible. Some template pseudo-code is:
#Inject #Foo #Bar private Instance<Xxxx> myVariable;
Explanation of the code above:
Retrieve an instance of each type annotated with #xy: #xy should be a qualifier (see specs, but it is really as simple as follows):
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface XY {
}
In which case the template code would be:
#Inject #XY private Instance<Object> myVariable;
Meaning roughly "get all the CDI beans having the #XY qualifier, disregarding actual type (the <Object>)".
Retrieve an instance of each type implementing the interface iXY: Easier:
public interface XY { ... }
Injection:
#Inject private Instance<XY> myVariable;
Retrieve an instance of each type, named like xy%: This could be possible, I think the implementation would be quite awkward. I will not go into this.
Anyway, Instance is an Iterable of its type parameter (Object in the first case, XY in the second), so in your code you can enumerate the beans matching your criteria as simple as:
for( Object o : myVariable ) {
...
}

Is it possible to instantiate a Java Annotation given a Class<? extends Annotation>?

I have a Class<? extends Annotation> and tried calling newInstance() but Java yelled at me for the obvious reason that I can't instantiate an interface. But I know frameworks like EasyMock are perfectly capable of instantiating interfaces. What would it take to get a completely dumb Annotation instance out of my Class?
Mock frameworks do not instantiate interfaces, they build classes that implement them on the fly at runtime. You may find this javadoc enlightening for what you want to do!
Thanks to Affe for pointing me the right direction - I'd comment on his answer but then I wouldn't be able to format the solution:
Annotation annotation = (Annotation) Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class[] { Annotation.class },
new InvocationHandler() {
#Override public Object invoke(Object proxy, Method method, Object[] args) {
return clazz; // only getClass() or annotationType() should be called.
}
});
works like a charm.
You can't create an instance of a class that's not fully specified.
To illustrate, calling newInstance() on a class of type Class<Object> would definately create an Object, but calling newInstance() on a class of type Class<?> would leave the compiler wondering which class out of the entire universe of possibilities it should construct.
Just because you have narrowed the field down a bit by specifying that instead of just ?, you want a ? that extends Annotation doesn't mean you've actually named a particular class to construct. ? extends Annotation just means "some class that extends Annotation" Without naming the exact class, the ClassLoader cannot figure out which constructor to call, because there is no limit to the number of classes that might extend Annotation.
EasyMock doesn't instantiate Interfaces. I'm not familiar with the framework, but it likely instantiates java.lang.Object(s) which extend the desired interface, or it instantiates some sort of "behind the scenes" framework class which was generated with an "implements interface" clause in the class definition. Interfaces don't have a default constructor.
If you know the type of the annotation at compile time, you can just create a class that implements the annotation as if it was a normal annotation. See also this answer.

Categories