Annotation
is a kind of metadata, that do not directly affect program semantics, but they
do affect the way programs are treated by tools and libraries, which can in
turn affect the semantics of the running program. Annotations can be read from
source files, class files, or reflectively at run time.
How to
define an annotation?
Defining
an annotation is similar to the interface declaration.
Syntax
modifier
@interface AnnotationName {
....
....
}
Modifier:
public, protected, private,
abstract, static, strictfp can be used as modifiers.
@interface: To distinguish an annotation type
declaration from a normal interface declaration, the keyword ‘interface’ is
preceded by an at-sign (@).
AnnotationName: specifies the name of the annotation.
AnnotationBody: contains method declarations. Each
method declaration defines an element of the annotation type. Method
declarations must not have any parameters or a throws clause. Return types are
restricted to primitives, String, Class, enums, annotations, and arrays of the
preceding types. Methods can have default values.
Example
public @interface AuthorDetails{
String author();
String date();
int currentVersion() default 1;
String lastModified() default "N/A";
String[] Reviewers();
}
Annotation
uses
a.
Annotations
can be used by compiler to detect errors and suppress warnings
b.
Software
tools can use annotations to generate code, xml files, documentation etc., For
example, Javadoc use annotations while generating java documentation for your class.
c.
Runtime
processing of the application can be possible via annotations.
d.
You
can use annotations to describe the constraints (Ex: @Null, @NotNull, @Max,
@Min, @Email).
e.
Annotations
can be used to describe type of an element. Ex: @Entity, @Repository, @Service,
@Controller, @RestController, @Resource etc.,
f.
Annotation
can be used to specify the behaviour. Ex: @Transactional, @Stateful
g.
Annotation
are used to specify how to process an element. Ex: @Column, @Embeddable,
@EmbeddedId
h.
Test
frameworks like junit and testing use annotations to define test cases (@Test),
define test suites (@Suite) etc.,
i.
AOP
(Aspect Oriented programming) use annotations (@Before, @After, @Around etc.,)
j.
ORM
tools like Hibernate, Eclipselink use annotations
Restriction
on Annotations
a.
Annotations
are not inherited
b.
Method
declarations in annotation must not have any parameters or a throws clause.
c.
Return
type of methods are restricted to primitives, String, Class, enums, annotations,
and arrays of the preceding types. Methods can have default values.
Core
annotations in Java
Java define
number of annotations. Following table summarizes the annotations that are used
frequently.
a.
@Target
b.
@Retention
c.
@Inherited
d.
@Override
f.
@Deprecated
g.
@SafeVarargs
h.
@Repeatable
Annotation
Types
There are
3 categories of Annotation types
a.
Marker
Annotation type
b.
Single-Element
Annotation type
c.
Multi-element
Annotation type
Marker
Annotation type
An
Annotation type with no elements is called Marker Annotation type
Example
@interface
Test{
}
Single-Element
Annotation type
An
Annotation type with single element is called Single-Element Annotation type.
By convention, the name of the element in a single-element annotation type is
value.
@interface
BookPublisher{
String value();
}
Multi-element
Annotation type
An
Annotation type with more than one element is called Multi-Element Annotation
type.
public @interface AuthorDetails {
String author();
String date();
int currentVersion() default 1;
String lastModified() default "N/A";
String[] Reviewers();
}
An
annotation type declaration contains an element whose type is also an
annotation type.
@interface
BookPublisher {
String value();
}
public
@interface Sample {
BookPublisher name();
}
Where
can I apply annotations?
Annotation
types may be applicable in two contexts.
a.
Declaration
Context
b.
Type
Context
Declaration
Context : Where the annotations
Apply to Declarations
There are
eight declaration contexts, each corresponding to an enum constant of
java.lang.annotation.ElementType.
1.
Package
Declarations
2.
Type
Declarations : include class, interface, enum and Annotation type declarations.
3.
Method
Declarations
4.
Constructor
Declarations
5.
Type
Parameter declarations of generic classes, methods, interfaces and constructors.
6.
Field
declarations including enum constants
7.
Formal
and Exception parameter declarations (A catch clause declares exactly one
parameter, which is called an exception parameter)
8.
Local
variable declarations (including loop variables of for statements and resource
variables of try-with-resources statements)
Type
contexts: where
annotations apply to types used in declarations and expressions.
There are
16 type contexts, all represented by the enum constant TYPE_USE of
java.lang.annotation.ElementType.
16 type
contexts are
1.
A
type in the extends or implements clause of a class declaration
2.
A
type in the extends clause of an interface declaration
3.
The
return type of a method (including the type of an element of an annotation
type)
4.
A
type in the throws clause of a method or constructor
5.
A
type in the extends clause of a type parameter declaration of a generic class,
interface, method, or constructor
6.
The
type in a field declaration of a class or interface (including an enum constant)
7.
The
type in a formal parameter declaration of a method, constructor, or lambda
expression
8.
The
type of the receiver parameter of a method
9.
The
type in a local variable declaration
10.
The
type in an exception parameter declaration
In
expressions
1.
A
type in the explicit type argument list to an explicit constructor invocation statement
or class instance creation expression or method invocation expression
2.
In
an unqualified class instance creation expression, as the class type to be instantiated
or as the direct superclass or direct super interface of an anonymous class to
be instantiated.
3.
The
element type in an array creation expression
4.
The
type in the cast operator of a cast expression
5.
The
type that follows the instanceof relational operator
6.
In
a method reference expression , as the reference type to search for a member
method or as the class type or array type to construct.
Retention
Policies
Retention
policy defines how long an Annotation is retained. There are three
different retention policies supported in java.
RetentionPolicy.SOURCE: Annotation with this retention policy
is discarded during the compilation. These annotations don't make any sense
after the compilation, so they aren't written to the bytecode generated by java
compiler.
Example: @Override, @SuppressWarnings
RetentionPolicy.CLASS: Annotation with this retention policy
are stored in .class file during compilation but not available at run time
(discarded during class loading). These are useful when doing bytecode-level
post-processing. If you do not specify any retention policy it is default.
RetentionPolicy.RUNTIME: Annotation with this retention
policy is not discarded and be part of
.class file, available at runtime.
Testing
tool using annotations
Let’s create
a simple testing tool using annotations.
Step 1:
Create Test
annotation with retention policy RUNTIME.
Test.java
package com.sample.app.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
Step 2:
Create TestClass like
below.
TestClass.java
package com.smaple.app;
import com.sample.app.annotations.Test;
public class TestClass {
@Test
public static void m1() {
}
public static void m2() {
}
@Test
public static void m3() {
throw new RuntimeException("Boom");
}
public static void m4() {
}
@Test
public static void m5() {
}
public static void m6() {
}
@Test
public static void m7() {
throw new RuntimeException("Crash");
}
public static void m8() {
}
}
Any method
defined with @Test annotation is treated as test case.
Step 3:
Lets run all the tesy
cases by parsing the annotation.
App.java
package com.smaple.app;
import java.lang.reflect.Method;
import com.sample.app.annotations.Test;
public class App {
public static void main(String[] args) throws Exception {
String classToTest = TestClass.class.getCanonicalName();
int passed = 0, failed = 0, total = 0;
for (Method m : Class.forName(classToTest).getMethods()) {
if (m.isAnnotationPresent(Test.class)) {
try {
total++;
m.invoke(null);
passed++;
} catch (Throwable ex) {
System.out.printf("Test %s failed: %s %n", m, ex.getCause());
failed++;
}
}
}
System.out.printf("Total %d, Passed: %d, Failed %d%n", total, passed, failed);
}
}
Run
App.java, you will see below messages in console.
Test public static void com.smaple.app.TestClass.m3() failed: java.lang.RuntimeException: Boom Test public static void com.smaple.app.TestClass.m7() failed: java.lang.RuntimeException: Crash Total 4, Passed: 2, Failed 2
You may like
Example was intersting.
ReplyDelete