In this article, we are going to discuss Annotations, Annotation is a very useful feature in Java, many Java frameworks like spring-boot, Hibernate (ORM Framework), Junit (Testing Framework), and libraries like Lombok depend heavily on annotation. In this article, we will cover the basics of annotation, see coding examples, and learn about custom annotations.
Content
Before jumping to annotations, let's first understand about metadata.
Metadata, it's data that provides information about various elements of Java code like classes, methods, fields,
parameters, and packages. This metadata is used by the compiler, runtime environment, and framework. Some Examples
are Annotation and Javadoc comments.
Now coming to Annotation, It is just metadata, that provides information about application code but not part of it.
We can attach annotations to various Java code elements like methods, classes, fields, parameters, and packages. It
is used by compilers, frameworks, and runtime environments to perform logic like detecting errors, suppressing
warnings, generating code, and configuration files at runtime.
For Example,
we have annotation @Override, we use it on top of the method in the subclass to tell that this method
is from the superclass and we are overriding it in the subclass. Here we just put annotation on top of the method,
and that annotation also doesn't have any logic inside it.
Then who performs this validation that this method is from the superclass? This logic is performed by compiler,
development tools like IDE. Let's say you put @Override on top of a method that is not part of the
super class then it will give you a compile-time error, again all this logic is performed by the compiler.
In Java standard library various annotations are defined in Java. lang and java.lang.annotatioan packages, below are
some of them.
@Override, it provides information to compile about the superclass method overriding in the subclass.- In case the method in the subclass is annotated with
@Overrideand that method is not part of the superclass it will give compile time error, Like "error: method does not override or implement a method from a supertype@Override"
- In case the method in the subclass is annotated with
@Deprecated, this annotation is used to mark any Java code elements (class, fields, methods, etc) as deprecated and no longer valid for use.
@SuppressWarnings, this we mostly use to suppress compiler warnings like deprecation, rawtypes, and unchecked on Java code elements (class, fields, methods, etc).deprecation, this warning we get when we use any method or fields that are marked as deprecated using@Deprecatedannotation but we still want to use them.unchecked, it happens when the compiler can not ensure type safety. Let's say we are casting anObjectto aStringwithout checking its type. another example is let's say you are adding a String type value into a list of rawtype.rawtypes, it happens when we create an object of type parametrized interface or class, without specifying its type. example if we define a list likeList list = new ArrayList();
@SafeVarargs, this is mostly used on top of the method and constructor, it tells the compiler that this method/constructor is safe to use with varargs parameter of generic type.
@FunctionalInterface, this is introduced in Java 8, It is annotated on top of the interface and it helps to mark that interface as a functional interface, whose implementation is provided by Lambda expression. Also, functional interface means SAM (Single Abstract Method), so if any interface annotated with@FunctionalInterfacedoes not follow the rule of functional interface then this annotation will give a compile time error.
Java libraries like Lombok and frameworks like Spring Boot depend heavily on annotations.
- In Lombok, it reduces boilerplate code by providing annotation that can generate code at compile time. e.g.
@Getter,@Setter, generate getter and setter fields for the class annotated with these annotations.@EqualsAndHashCode, generate equals and hashcode method for the class. - In Spring Boot, we have a huge list of annotations for various purposes. Some of them are
@SpringbootApplication: This we mostly use to mark the class as the main class or starting point of the spring application. It combines 3 annotations within itself,@Configuration,@EnableAutoConfigurationand@ComponentScan.@Autowired, Spring use it for injecting dependency,@RESTController@RequestMappingfor creating Restful webservices.
- ORM Framework like Hibernate, also uses annotation to map Java classes to tables. Examples,
@Id,@Entity,@Table,@Column. - Testing framework like Junit also uses annotation to define test cases, setup methods, and test-related
configurations. E.g ,
@Test,@Before,@After.
So far we read about annotations provided by Java, but can we create our annotations? Ans is yes, we can create our
annotation. Let's see the code first and then we will understand it in detail,
- To create annotation we must use the '@interface' keyword, it's similar as we use the 'class' keyword to create a class and the 'interface' keyword to create an interface.
- On top of it we must use two annotations,
@Retentionand@Target.
Annotations that apply to another annotation are called meta-annotations. So here @interface, @Retention,
and @Target all are meta-annotations.
@Retention, annotation tells how long annotation should retain, that is it indicates if it's available at runtime or compile time.- It accepts one parameter of type RetentionPolicy Enum, it has 3 values.
RetentionPolicy.RUNTIME, for custom annotation we mostly used this retention policy, it allows the annotation to be available at runtime, and using Java reflection API we (The programmer) can access it. Examples:@DeprecatedRetentionPolicy.CLASS, allows the annotation to be available in compiled code (.classfile), so such annotation is available during compilation and in generated.classfiles but cannot access at runtime even through Java reflection API.RetentionPolicy.SOURCE, allows the annotation to be available in source code (.javafile) but it is not included in compiled code (.classfile). Such annotation can be accessed during compile time by the compiler but its presence/absence will not impact application operations. It is mostly used by IDEs (to show warning and error while writing code) and compilers. It cannot be accessed through Java Reflection API. Example:@Override,@SupressWarnings.
- It accepts one parameter of type RetentionPolicy Enum, it has 3 values.
@Target, defines the target of our custom annotation, and on what program elements we can apply it, like class, methods, fields, and parameters. It accepts one/list of the parameters of the typeElementTypeenum,ElementTypehas various elements that are as below,ElementType.TYPE:Indicates that the annotation can be applied to a class, interface, or enumeration.ElementType.METHOD:Indicates that the annotation can be applied to a method.ElementType.FIELD:Indicates that the annotation can be applied to a field.ElementType.PARAMETER:Indicates that the annotation can be applied to a method parameter/argument.ElementType.CONSTRUCTOR:Indicates that the annotation can be applied to a class constructor.ElementType.LOCAL_VARIABLE:Indicates that the annotation can be applied to a local variable.ElementType.ANNOTATION_TYPE:Indicates that the annotation can be applied to another annotation.ElementType.PACKAGE:Indicates that the annotation can be applied to a package.
- In our example, inside annotation we have some elements like String value and int count. Elements in custom
annotation hold the data provided by the user within annotation, we can define annotation elements as a method
without a body, and it can also have default values as we define below examples.
- Our custom annotation can have no, one, or more than one element, we can define it as mentioned below.
- If there is no element in the annotation then it is called a marker annotation.
- If it accepts only one then it is called a single value annotation.
- If it accepts more than one then it is called a full annotation.
- Our custom annotation can have no, one, or more than one element, we can define it as mentioned below.
So, far we understood what is Annotation, read about some Java standard annotations and how can we create our custom
annotation. but annotation is just metadata, the actual logic of annotation performed by the compiler, let's
understand annotation processing and how the compiler handles it.
The compiler uses an annotation processor, Which processes annotation at compile or run time, and if required
generates source code or resources.
- In the compilation process Java files
(.java)convert into Class files(.class). - During this compilation, the compiler also scans for annotation and once an annotation is found, it processes
that annotation using an annotation processor.
- What is an annotation processor? An annotation processor is a class that implements
javax.annotation.processing.Processorinterface. such class having actual logic that will perform on behalf of annotation. This may include logic to generate source code and new resources. - To create our Custome annotation processor we can implement
javax.annotation.processing.Processorinterface and provide an implementation of all of its methods or can extendjavax.annotation.processing.AbstractProcessorabstract class and override its 'process' method.
- What is an annotation processor? An annotation processor is a class that implements
- Compiler finds and loads annotation processor using Service Provider Interface (SPI) mechanism. Processor
classes are specified in a file named
javax.annotation.processing.Processoris located in theMETA-INF/servicesdirectory of processor JAR files. During compilation, the compiler locates and instantiates these processor classes to perform annotation processing.- here SPI, is a design pattern, using which we can customize applications or libraries by providing
implementation of certain interfaces. It enables dynamic discovery and loading of the service provider
at run time without explicit configuration. using this, whatever interface we want to implement we have
to create a file of the fully qualified name of that interface inside the
META-INF/servicesdirectory, inside the file we need to write the fully qualified name of the class which will provide implementation of it.- here we implemented
javax.annotation.processing.Processorinterface, so we created a file with that name inside theMETA-INF/servicesdirectory, and inside this file, we mentioned the full qualified name of our custom annotation processor class.
- here we implemented
- To create a file in the META-INF/services directory, we can do all this configuration manually or we can
use the below dependency and use annotation
'@`AutoService(Processor.class)'on top of our custom annotation processor, which will handle all this configuration automatically.
- here SPI, is a design pattern, using which we can customize applications or libraries by providing
implementation of certain interfaces. It enables dynamic discovery and loading of the service provider
at run time without explicit configuration. using this, whatever interface we want to implement we have
to create a file of the fully qualified name of that interface inside the
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
- Compiler perform processing in rounds, so it first process source code, let's say it finds some annotation and after its processing, some new source code is generated, then in the next round processing only the source files that have changed since the previous compilation or newly introduced by the annotation processor.
- In the end compiled bytecode files
(".class")generate.
In case we are just creating small programs we can use the command javac to compile code and we can pass our custom processor in this command, so that compiler can use it.
javac -processor com.coderstuff01.MyAnnotationProcessor MyClass.java
Thank you for reading this article, hope you learn something new and find it interesting. if you have anything to share with us please feel free to comment below.










Comments
Post a Comment