Compell a method with specific annotation to have specific parameters/signature - java

I have an annotation as:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String annotationArgument1() default "";
String annotationArgument2();
}
I have two classes as:
class MyClass1 {
#MyAnnotation(annotationArgument1="ABC", annotationArgument2="XYZ")
public void method1(MyClass2 object) {
//do something
}
#MyAnnotation(annotationArgument1="MNO", annotationArgument2="PQR")
public void method2(MyClass2 object) {
//do something
}
}
class MyClass2 {
int num;
}
I want method1 and method2 (or any other method in any other class annotated with #MyAnnotation) to take only one argument as MyClass2 because they are annotated with #MyAnnotation. If some other argument is passed, it must give a compile time error.
Is it actually possible to do this?
If yes, how can it be done and if no, what is alternate to make this kind of behavior possible?

AFAIK, you can use an annotation processor to check the method signature at compile-time.
I recommend to:
consider AbstractProcessor as a base class
consider to use the annotations provide by the javax.annotation.processing package
register the Processor as a service in META-INF/services
package the annotation processor and the annotations in the same jar - together with the registration as a service this will enable the processor whenever your custom annotation processor is used

Related

How to force annotations to have an "id" field?

I have the following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface IdentifiableMethod {
String id() default "";
}
I will have to loop through a list of annotations and for each of them, perform a annotation.id().
Hence, I would have liked to use this "base" annotation to make it extended by other annotations:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface SpecificMethod extends IdentifiableMethod{
//invalid: annotation cannot have extends list
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface OtherSpecificMethod extends IdentifiableMethod{
//invalid: annotation cannot have extends list
}
... and then generically access the .id() method in a loop by getting in parameter a List<A extends IdentifiableMethod>, so that the compiler always makes me access that method.
However, I've just found out that in the Java specification, all Java annotations extend natively the interface Annotation and they cannot have an extends list [Source: this Stack Overflow question and its answers].
Is there any way to reach something similar?
Just to clarify the need, I need to get all the methods of all the classes within my package by reflection and scan for these annotations. They may be different (they may have more or less properties, different usages etc.), but they all need to have a String id field:
List<Class<?>> classes = getClasses(packageName);
for (Class<?> clazz : classes) {
for (Method method : clazz.getMethods()) {
for (Class<A> annotation : annotations) { //<-- annotations is a Collection<Class<A extends Annotation>>
if (method.isAnnotationPresent(annotation)) {
A targetAnnotation = method.getAnnotation(annotation);
String id = targetAnnotation.id(); //<-- this is not valid because A extends Annotation, not IdentifiableMethod
//rest of code (not relevant)
}
}
}
}
P.s. I already did this but I was looking for something cleaner:
String id = targetAnnotation.getClass().getMethod("id").invoke(targetAnnotation).toString();

Unable to get annotations from Java classes when trying to autowire multiple implementations

In a Java 11/Spring REST API project I have an interface with multiple implementations. I want to choose the implementation in the configuration (in this case, application.yml file):
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Retention(RUNTIME)
#Target(TYPE)
public #interface PickableImplementation {
// This id will match an entry in the config file
public String id() default "";
}
So I have the two possible "pickable" implementations:
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.mycompany.api.util.PickableImplementation;
#Component
#Service
#PickableImplementation(id = "s3")
public class BatchProcessServiceS3 implements BatchProcessService {
// Some implementation biz logic
}
// In another file, with the same imports:
#Component
#Service
#PickableImplementation(id = "azure")
public class BatchProcessServiceAzure implements BatchProcessService {
// Another implementation biz logic
}
In the consumer class which in this case is a Controller, I try to pick the desired implementation:
import com.mycompany.api.util.PickableImplementation;
import java.util.List;
#RestController
#RequestMapping("/batch")
public class BatchController {
#Autowired private Environment env;
private BatchProcessService batchProcessService;
private final List<BatchProcessService> batchProcessImplementations;
public BatchController(List<BatchProcessService> batchProcessImplementations,
Environment environment){
this.env = environment;
var activeSvcImplementationId = env.getRequiredProperty("buckets-config.active");
this.batchProcessImplementations = batchProcessImplementations;
for (var batchProcessService : this.batchProcessImplementations) {
if(batchProcessService.getClass().isAnnotationPresent(PickableImplementation.class)) {
// Verify the id, compare with the one from config file, etc.
}
}
}
}
Expected behavior: inside the loop, I expected to get the annotations of each implementation, traverse the list, verify if it matches with the one with the application.yml and if it does, pick it to populate the service layer (private BatchProcessService batchProcessService).
Actual behavior: not only the isAnnotationPresent() method returns false, but also if I try getAnnotations() I get an empty array, like there are no annotations in the class. And besides my custom one, there are at least two additional annotations (Component, Service and others related to logging and the like).
As another puzzling detail, if I run getAnnotations() on the qualified name of the class in the middle of a debugging session, the annotations are present. But in that very moment, running that method on the elements on the list return 0 annotations.
I've run out of ideas, has anyone tried this same combination of autowiring several implementations and at the same time, relying in custom annotations?
Additional references:
Custom Annotations in Java:
https://www.baeldung.com/java-custom-annotation
Autowired:
https://www.baeldung.com/spring-autowire
Useful answer on autowiring multiple implementations:
https://stackoverflow.com/a/51778396/6315428

Java function which only takes JPA entities as parameter [duplicate]

I have an annotation as:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String annotationArgument1() default "";
String annotationArgument2();
}
I have two classes as:
class MyClass1 {
#MyAnnotation(annotationArgument1="ABC", annotationArgument2="XYZ")
public void method1(MyClass2 object) {
//do something
}
#MyAnnotation(annotationArgument1="MNO", annotationArgument2="PQR")
public void method2(MyClass2 object) {
//do something
}
}
class MyClass2 {
int num;
}
I want method1 and method2 (or any other method in any other class annotated with #MyAnnotation) to take only one argument as MyClass2 because they are annotated with #MyAnnotation. If some other argument is passed, it must give a compile time error.
Is it actually possible to do this?
If yes, how can it be done and if no, what is alternate to make this kind of behavior possible?
AFAIK, you can use an annotation processor to check the method signature at compile-time.
I recommend to:
consider AbstractProcessor as a base class
consider to use the annotations provide by the javax.annotation.processing package
register the Processor as a service in META-INF/services
package the annotation processor and the annotations in the same jar - together with the registration as a service this will enable the processor whenever your custom annotation processor is used

#Inherited annotation is not being inherited

I have a SubClass and a SuperClass, as well as an annotation DocAnnotation. I need a call to SubClass.foo() to get all class annotations from SubClass and SuperClass. The classes are defined like this:
SuperClass
package Code
import Annotations.DocAnnotation;
import java.util.Arrays;
#DocAnnotation("Super Annotation")
public class SuperClass {
public void foo() {
System.out.println(Arrays.toString(this.getClass().getAnnotations()));
System.out.println(Arrays.toString(this.getClass().getDeclaredAnnotations()));
}
}
SubClass
package Code;
import Annotations.DocAnnotation;
#DocAnnotation("Sub Annotation")
public class SubClass extends SuperClass{
}
DocAnnotation
package Annotations;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(DocAnnotations.class)
public #interface DocAnnotation {
String value();
}
Running SubClass.foo I expect to see both "Super Annotation" and "Sub Annotation" but instead I see only [#Annotations.DocAnnotation(value=Sub Annotation)]. Am I misunderstanding what #inherited does, or am I doing something incorrectly?
Edit:
After adding the annotation #DocAnnotation("Super Annotation") to SubClass (that's the same one as in SuperClass) it actually shows up twice, once for its use in SubClass and once for its use in SuperClass! Now I'm nearly certain I'm misunderstanding something...
This seems to be the intended behavior, or at least it's specified to work this way (doc):
If [...] 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, since SubClass is already annotated with #DocAnnotation, the superclass is not queried.
On further inspection, the behavior seems a bit weird, though, especially after experimenting with presence of the containing annotation type. I came up with the following example which illustrates this (link):
import java.lang.annotation.*;
import java.util.*;
#Inherited
#Repeatable(Container.class)
#Retention(RetentionPolicy.RUNTIME)
#interface Ann {
String value();
}
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#interface Container {
Ann[] value();
}
// Basic case. Result is that
// only #Ann("2") is present on
// ExhibitASub.
#Ann("1")
class ExhibitASuper {
}
#Ann("2")
class ExhibitASub extends ExhibitASuper {
}
// Because this case results in the
// #Container being present on ExhibitBSuper,
// rather than #Ann, all three annotations
// end up appearing on ExhibitBSub.
#Ann("1")
#Ann("2")
class ExhibitBSuper {
}
#Ann("3")
class ExhibitBSub extends ExhibitBSuper {
}
// Similar to the preceding case, by
// forcing the use of #Container, both
// annotations are present on ExhibitCSub.
#Container(#Ann("1"))
class ExhibitCSuper {
}
#Ann("2")
class ExhibitCSub extends ExhibitCSuper {
}
// Yet when we force both to use #Container,
// only #Container(#Ann("2")) is present on
// ExhibitDSub.
#Container(#Ann("1"))
class ExhibitDSuper {
}
#Container(#Ann("2"))
class ExhibitDSub extends ExhibitDSuper {
}
class Main {
public static void main(String[] args) {
for (Class<?> cls : Arrays.asList(ExhibitASub.class,
ExhibitBSub.class,
ExhibitCSub.class,
ExhibitDSub.class)) {
System.out.printf("%s:%n", cls);
for (Annotation ann : cls.getAnnotations()) {
System.out.printf(" %s%n", ann);
}
System.out.println();
}
}
}
The output of which is as follows:
class ExhibitASub:
#Ann(value=2)
class ExhibitBSub:
#Container(value=[#Ann(value=1), #Ann(value=2)])
#Ann(value=3)
class ExhibitCSub:
#Container(value=[#Ann(value=1)])
#Ann(value=2)
class ExhibitDSub:
#Container(value=[#Ann(value=2)])
Note that for B and C we see both the annotations on the superclass and subclass. Presumably this is because (in a strict sense) the annotation present on the superclass is of a different type than the annotation present on the subclass. Note that for D we return to only seeing the subclass annotation because both classes use the container type.
Using the containing annotation type explicitly could be a workaround for some cases, but it's not a general solution because of case D.
I might file a bug report for this tomorrow since this seems pretty undesirable. Since #Inherited predates #Repeatable, this behavior could be from previous versions where this situation couldn't occur.
You are getting this annotation wrong. The javadoc clearly states:
Indicates that an annotation type is automatically 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: if you query the subclass, then you would find the super class being annotated. But this thing is not meant for inheritance in the "OO sense". If you want to see both annotations, you have to write code that checks each class in the class inheritance tree manually.

use of custom annotations

I found several related (not duplicate) question to this, but they didn't satisfy me.
I am unable to understand where and why to use custom annotations?
I read an example of custom annotation in a book, but it was not explained thoroughly.
#interface MyAnno
{
String str();
int val();
}
class MyClass
{
#MyAnno(str = "Annotation example", val = 100)
public static void myMeth()
{
System.out.println("Inside myMeth()");
}
}
class CustomAnno
{
public static void main(String args[])
{
MyClass.myMeth();
}
}
The output is as expected Inside myMeth().
I am having few questions regarding this example.
1- How can I use String str() and int val() in this program? OR
What is the use of any abstract method of an custom annotation?
2- Why custom annotations. I mean that what effect they are having on any code.
3- How can I create an annotation which is having effects like #override is having?(I mean any kind of effect which can be noticed)
If this example is useless for you, then please give me a suitable small example in which a custom annotation is used.
Three main reasons to use custom annotations are:
To reduce the effort of writing code (a compile-time annotation processor generates code for you). Here is a tutorial: part 1, part 2.
To provide additional correctness guarantees (a compile-time annotation processor warns you about errors). One nice tool for this is the Checker Framework, which prevents null pointer dereferences, concurrency errors, and more.
To customize behavior (at run time, your code checks for the annotation using reflection and behaves differently depending on whether the annotation is present). Frameworks such as Hibernate use annotations this way; also see an Oracle article.
In each case, use of annotations reduces the likelihood of errors in your code, compared to other non-annotation approaches.
Here is a minimal example. The following code demonstrates use of custom annotation.
It is about Employees and Benefits. If we have a requirement such that BasicBenefits has to be applied to all types of employess then we can come up with custom annotation such as BasicBenefits, and annotate all types of Employee implementations (e.g. CorporateEmployee, ContractEmployee, ManagerEmployee etc. etc.) with the BasicBenefits.
Custom Annotation Class(interface)
import java.lang.annotation.*;
#Inherited
#Documented
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface BasicBenefits {
String bId() default "B-101";
String bName() default "General Class A Employee";
}
Class using the custom annotation(no need of any imports):
#BasicBenefits(bId="B-400", bName="General Plus Class A Employee")
public class Employee {
String eId;
String eName;
public Employee(String eId, String eName){
this.eId = eId;
this.eName = eName;
}
public void getEmployeeDetails(){
System.out.println("Employee ID: "+eId);
System.out.println("Employee Name: "+eName);
}
}
Driver class to test out the above.
import java.lang.annotation.Annotation;
public class TestCustomAnnotationBasicBenefits {
public static void main(String[] args) throws Exception{
Employee emp = new Employee("E-100", "user3320018");
emp.getEmployeeDetails();
Class reflectedClass = emp.getClass();
Annotation hopeBenefitAnn = reflectedClass.getAnnotation(BasicBenefits.class);
BasicBenefits bBenefits = (BasicBenefits)hopeBenefitAnn;
System.out.println("Benefit ID: "+bBenefits.bId());
System.out.println("Benefit Name: "+bBenefits.bName());
}
}
Your code look almost there, just two things need to be included in the main method.
1.) Need reference to MyClass
2.) Need to get the annotation using reflection from MyClass.
Here is a bit modified code from what you have:
#Inherited
#Documented
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnno
{
String str();
int val();
}
//using above custom annotation on class level
//can also use method level
//just need to change t #Target(ElementType.METHOD)
#MyAnno(str = "Annotation example", val = 100)
class MyClass
{
public static void myMeth()
{
System.out.println("Inside myMeth()");
}
}
import java.lang.annotation.Annotation;
class CustomAnno
{
public static void main(String args[])
{
//1. getting reference to the class where the custom annotation is applied.
//2. then getting the annotation to get the values
MyClass myClass = new MyClass();
Class cls = myClass.getClass();
Annotation getMyAnno = cls.getAnnotation(MyAnno.class);
MyAnno myAnno = (MyAnno)getMyAnno;
MyClass.myMeth(); //left this as is.
System.out.println("myAnno.str(): "+ myAnno.str());
System.out.println("myAnno.str(): "+ myAnno.val());
}
}
The abstract methods of the annotation define the values you can pass to it (in your case str = "Annotation example", val = 100). You can access them using reflection (Method.<T>getAnnotation(Class<T>)). Custom annotations don’t have direct impact. They are only useful if you evaluate them.
Note that you have to annotate your custom annotation with #Retention(value=RUNTIME) to be able to read it via reflection.
To be of any use, annotations must be parsed first. The built-in annotations (such as #Override or #FunctionalInterface, to name the most obvious ones) are parsed by the compiler itself. As for custom annotations, these guys are commonly parsed by third-party frameworks, although we can also use the reflection mechanism to demonstrate this technique in standalone code.
By way of an example, the code below changes its behaviour at run time depending on the value of the field declared in the custom annotation named #SwitchingAnnotation:
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#interface SwitchingAnnotation{
boolean flag();
}
public class Worker{
void doThis(){ System.out.println("Doing this"); }
void doThat(){ System.out.println("Doing that"); }
#SwitchingAnnotation(
flag = false
)
public void work(boolean flag) {
if (flag) doThis();
else doThat();
}
}
class Test{
public static void main(String[] args) {
try{
SwitchingAnnotation sw = Worker.class.getMethod("work", boolean.class)
.getAnnotation(SwitchingAnnotation.class);
new Worker().work(sw.flag()); // prints Doing that
}
catch(NoSuchMethodException nsme){
System.out.println(nsme);
}
}
}

Categories

Resources