May #Repository annotation be inherited?
May I create a
#Repository
public abstract class BaseRepository {
}
and then extend it without specifying #Repository annotation?
public class MyRepository extends BaseRepository {
}
How do I know if an annotation can be inherited?
See http://www.docjar.com/html/api/org/springframework/stereotype/Repository.java.html
There is no #Inherited annotation, so it is not inherited.
No. The documentation about stereotypes says nothing about it.
Also, imagine the case when you specify a name #Repository("foo") - then each subclass will be named foo, which will lead to an exception (duplicate names)
Annotations aren't inherited by subclasses by default, unless annotated themselves with #Inherited (see James Kingsbery's comment below), and even then only when they are used at class-level. #Repository-annotation itself is not annotated (I checked from the code) with #Inherited, so you'll have to annotate the subclass separately.
Related
In a spring boot project I'm working on, we have a lot of classes that extend Consumer, Processor, or Producer custom abstract classes. All these concrete extension classes are also annotated with #Component, #ConditionalOnProperty and usually #Profile as well.
I just started learning about Java annotations and was wondering if it's possible (at all) to simplify my above scenario by creating custom #Consumer, #Processor, and #Producer annotations, such that doing
#Consumer(profile = "some_profile", conditionalOnName = "some_name", conditionalOnValue = "some_value")
public class MyCustomConsumer {
// Abstract methods implementation
}
is the same as
#Component
#ConditionalOnProperty(name = "some_name", havingValue = "some_value")
#Profile("some_profile")
public class MyCustomConsumer extends Consumer {
// Abstract methods implementation
}
and the missing abstract methods inherited from Consumer are enforced on MyCustomConsumer (meaning the compiler will complain that those methods are not implemented).
Seems like a very long shot in getting something like this to work (as my research into Java annotations hasn't shown any viable option), but seeing how Lombok can add code to my code without modifying the file, I thought I'd ask.
Is this possible?
I have two interfaces in different packages:
package com.domain1
#Repository //expect that Spring will generate a bean
public interface PersonRepository extends org.springframework.data.repository.Repository<Person, UUID> {}
package com.domain2
//just an interface
public interface PersonRepository extends org.springframework.data.repository.Repository<Person, UUID> {}
//implementation
#Component
public PersonRepositoryImpl implements com.domain2.PersonRepository {}
And I try to inject the first repository:
public class MyClass {
#Autowired
com.domain1.PersonRepository personRepository;
}
I expect that spring-data-jpa injects a repository from domain1 package but, instead of it, com.domain2.PersonRepositoryImpl is injected (a component which doesn't implement com.domain1.PersonRepository interface).
I tried using qualifiers - doesn't help.
Is this a bug or feature? :)
P.S. Of course, if I change the interface name then everything works as expected.
It is a feature of course. Interfaces can not be instantiated, instead, their implementations are. Spring will look for all implementations of a particular interface, if it can identify a unique one, which is your case, it will use that implementation.
If you have more than one it will give you an exception saying that there are multiple candidates for the autowire, and it can't pick one independentely. In this case you have to use the #Qualifer to specify which implementation is that you want.
Often people ask AspectJ questions like this one, so I want to answer it in a place I can easily link to later.
I have this marker annotation:
package de.scrum_master.app;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface Marker {}
Now I annotate an interface and/or methods like this:
package de.scrum_master.app;
#Marker
public interface MyInterface {
void one();
#Marker void two();
}
Here is a little driver application which also implements the interface:
package de.scrum_master.app;
public class Application implements MyInterface {
#Override
public void one() {}
#Override
public void two() {}
public static void main(String[] args) {
Application application = new Application();
application.one();
application.two();
}
}
Now when I define this aspect, I expect that it gets triggered
for each constructor execution of an annotated class and
for each execution of an annotated method.
package de.scrum_master.aspect;
import de.scrum_master.app.Marker;
public aspect MarkerAnnotationInterceptor {
after() : execution((#Marker *).new(..)) && !within(MarkerAnnotationInterceptor) {
System.out.println(thisJoinPoint);
}
after() : execution(#Marker * *(..)) && !within(MarkerAnnotationInterceptor) {
System.out.println(thisJoinPoint);
}
}
Unfortunately the aspect prints nothing, just as if class Application and method two() did not have any #Marker annotation. Why does AspectJ not intercept them?
The problem here is not AspectJ but the JVM. In Java, annotations on
interfaces,
methods or
other annotations
are never inherited by
implementing classes,
overriding methods or
classes using annotated annotations.
Annotation inheritance only works from classes to subclasses, but only if the annotation type used in the superclass bears the meta annotation #Inherited, see JDK JavaDoc.
AspectJ is a JVM language and thus works within the JVM's limitations. There is no general solution for this problem, but for specific interfaces or methods you wish to emulate annotation inheritance for, you can use a workaround like this:
package de.scrum_master.aspect;
import de.scrum_master.app.Marker;
import de.scrum_master.app.MyInterface;
/**
* It is a known JVM limitation that annotations are never inherited from interface
* to implementing class or from method to overriding method, see explanation in
* JDK API.
* <p>
* Here is a little AspectJ trick which does it manually.
*
*/
public aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare #type: MyInterface+ : #Marker;
// Overriding methods 'two' should inherit marker annotation
declare #method : void MyInterface+.two() : #Marker;
}
Please note: With this aspect in place, you can remove the (literal) annotations from the interface and from the annotated method because AspectJ's ITD (inter-type definition) mechanics adds them back to the interface plus to all implementing/overriding classes/methods.
Now the console log when running the Application says:
execution(de.scrum_master.app.Application())
execution(void de.scrum_master.app.Application.two())
By the way, you could also embed the aspect right into the interface so as to have everything in one place. Just be careful to rename MyInterface.java to MyInterface.aj in order to help the AspectJ compiler to recognise that it has to do some work here.
package de.scrum_master.app;
public interface MyInterface {
void one();
void two();
// Cannot omit 'static' here due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=571104
public static aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare #type: MyInterface+ : #Marker;
// Overriding methods 'two' should inherit marker annotation
declare #method : void MyInterface+.two() : #Marker;
}
}
Update 2021-02-11: Someone suggested an edit to the latter solution, saying that the aspect MarkerAnnotationInheritor nested inside interface MyInterface is implicitly public static, so the modifiers in the aspect declaration could be omitted. In principle this is true, because members (methods, nested classes) of interfaces are always public by default and a non-static inner class definition would not make sense inside an interface either (there is no instance to bind it to). I like to be explicit in my sample code, though, because not all Java developers might know these details.
Furthermore, currently the AspectJ compiler in version 1.9.6 throws an error if we omit static. I have just created AspectJ issue #571104 for this problem.
In Java annotations marked as #Inherited will only work when annotating classes:
Note that this meta-annotation type has no effect if the annotated
type is used to annotate anything other than a class. Note also that
this meta-annotation only causes annotations to be inherited from
superclasses; annotations on implemented interfaces have no effect.
So interfaces or methods annotated with an #Inherited annotation will not result in implementing classes/methods to also be annotated with the annotation. The reason for this is most likely, that the compiler would'n know which of the annotations to choose, if there are multiple annotations in the class hierarchy as described here.
Now Java 8 introduced the new annotation #Repeatable. I think it would have been natural to remove the above restrictions for annotations that are both marked as #Inherited and #Repeatable, because the compiler should then be able to add the conflicting annotations to the #Repeatable annotation.
Given the following example:
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Inherited
#interface RepeatableAnnotations {
RepeatableAnnotation[] value();
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Inherited
#Repeatable(RepeatableAnnotations.class)
#interface RepeatableAnnotation {
String value();
}
#RepeatableAnnotation("A")
interface IntefaceA {}
#RepeatableAnnotation("B")
interface IntefaceB {}
#RepeatableAnnotation("C")
#RepeatableAnnotation("D")
public class TestClass implements IntefaceA, IntefaceB {
public static void main(String[] args) {
for (RepeatableAnnotation a : TestClass.class.getAnnotation(RepeatableAnnotations.class).value()) {
System.out.print(a.value());
}
}
}
I would have hoped the output to be ABCD but it is "just" CD (i.e. #Inherited is working exactly like pre Java 8).
Does anyone know if there was good reason for not removing the #Inherited restrictions regarding interfaces and methods in the case of #Repeatable annotations for Java 8?
Is there any workaround to achieve the ABCD output for the above type hierarchy? (other than using reflection to scan the super interfaces for annotations...)
Please recall the documentation of #Inherited:
If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type.
In other words, #Inherited never was intended to be a feature for collecting multiple annotations on a type hierarchy. Instead, you will get the annotation of the most specific type which has an explicit annotation.
In other words, if you change your declaration to
#RepeatableAnnotation("FOO") #RepeatableAnnotation("BAR") class Base {}
#RepeatableAnnotation("C") #RepeatableAnnotation("D")
public class TestClass extends Base implements IntefaceA, IntefaceB {
it won’t change the result; FOO and BAR of Base are not inherited by TestClass as it has the explicit annotation values C and D.
Expanding this to the interface hierarchy would be awkward due to the multiple inheritance and the fact that a super-interface may turn out to be a sub-interface of another super-interface so finding the most specific one is not trivial. This differs heavily from the linear search of the superclass hierarchy.
You may encounter the situation where multiple unrelated annotated interfaces exist but it’s not clear why this ambiguity should be resolved by joining them into one repeated annotation. This would not harmonize with the behavior in all other scenarios.
Note that the answer you have linked is a bit odd as it shows code using a method annotation but method annotations are never inherited, regardless of whether you specified #Inherited or not (an audit tool should generate a warning when you combine #Target(ElementType.METHOD) with #Inherited, imho). #Inherited is relevant for type annotations only.
but I found this thread searching for a similar solution. In the end I wrote this helper method:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Labels
{
Label[] value();
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Repeatable(Labels.class)
public #interface Label
{
String value();
}
#Label("A")
class A
{
}
#Label("B")
class B extends A
{
}
#Test
void LabelStackTest()
{
var labels = ClassUtils.getAnnotatedLabels(B.class);
assertThat(labels).contains("A");
assertThat(labels).contains("B");
}
public static List<String> getAnnotatedLabels(Class<?> labeledClass)
{
var labels = new ArrayList<String>();
do
{
labels.addAll(Arrays.asList(labeledClass.getAnnotationsByType(Label.class))
.stream()
.map(labelAnnotations -> labelAnnotations.value())
.toList());
labeledClass = labeledClass.getSuperclass();
} while (labeledClass != Object.class);
return labels;
}
I used to add #Transactional annotations to all spring services (classes). And then I thought: do I really have to, if the transactinal behaviour should be the same? (Of course, if it shouldn't, we would add #Transational with other parameters to methods.) I tried to find some useful information about inheritance with #Transactional, read about #Inherited (and it looks like #Transactional is #Inherited). I experimented with rollbackFor and noRollbackFor for the following example, and it looks like #Transactional in GenericService worked for doSmthSpecific.
#Transactional
public abstract class GenericService {
public void doSmthGeneric() {
}
}
public class SpecificService extends GenericService {
public void doSmthSpecific() {
}
}
And in case GenericService was an interface, I think it wouldn't work. I guess it's more like "correct me if I'm wrong" question, I'd like to know if it's actually all right to add #Transactional to superclass only, and if I'm missing something here. A detailed explanation (or a link to such explanation) would be appreciated.
Quoting the docs
You can place the #Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class...
They also recommend against annotating interfaces/interface methods.
Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the #Transactional annotation, as opposed to annotating interfaces. You certainly can place the #Transactional annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies.
Later they go on to explain that it doesn't work when you're using class-based proxies or aspectj weaving.