Definition of annotations
Definition: Annotations, also called metadata. A code level specification. It is a feature introduced in JDK1.5 and later and is on the same level as classes, interfaces, and enumerations. It can be declared in front of packages, classes, fields, methods, local variables, method parameters, etc., used to describe and comment these elements. Common annotations are as follows:
Main functions of annotations
- Generate documentation. This is the most common and the earliest annotation provided in Java. Common ones are @see @param @return
- Track code dependencies, implement alternative configuration file functions, alternative XML functions, and compare how much is used in SpringMVC.
- Do formatting checks at compile time, such as @overRider@callsuper@nullable and @nonNULL
- @layoutres, @integerres, 3) Process class annotation @UithRead, @binderThread,@MainThread, @workerThread 4) ButterKnife , EventBus, Dagger2 framework
Yuan comments:
So how is an annotation defined? Let’s first look at the commonly used Override annotation.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
Copy the code
Here involves meta-annotations, we generally use meta-annotations to define annotations, meta-annotations mainly have the following. We will introduce them one by one.
- @Target
- @Retention
- @Documented
- @Inherited
- @repTable JAVA8 (New)
@Target
Describe the scope of use of annotations (that is, where the modified annotations can be used). The main types are as follows:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since1.8 * /
TYPE_PARAMETER,
/**
* Use of a type
*
* @since1.8 * /
TYPE_USE
}
Copy the code
@Documented
Describes whether to retain annotation information when generating help documents for classes using the Javadoc tool.
@Inherited
Inherited annotations are used to make annotations that are modified by @Inherited Inherited. (If a class uses an annotation modified by @Inherited, its child classes automatically inherit the annotation.)
@Retention
Retention annotation values are in RetentionPolicy
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
Copy the code
- SOURCE: valid in the SOURCE file (i.e. retained in the SOURCE file). @Override and @SuppressWarnings are annotations of this type;
- CLASS: valid in a CLASS file (i.e., CLASS reserved)
- RUNTIME: valid at RUNTIME (i.e., reserved at RUNTIME)
RetentionPolicy.Source
Take a look at the retentionPolicy.source scenario: Since the annotation is retained during compilation, you can do something about the policy during compilation.
- Enumerated type
We sometimes define enumerations using enums, which often take up twice as much memory as static constants, but we prefer to define enumerations using annotations. The usage is as follows:
@IntDef({Color.RED , Color.YELLOW ,Color.BLUE})
@Retention(RetentionPolicy.SOURCE)
public @interface Color{
int RED = 1;
int YELLOW = 2;
int BLUE= 3;
}
Copy the code
- Lombok has a similar scenario
There are also scenarios such as those where Java code is automatically generated. The most common is the use of lombok, can automatically generate the field of the get and set methods, and toString method, constructor and detailed implementation methods and principle to see: www.jianshu.com/p/fc06578e8…
RetentionPolicy.Class
CLASS: valid in a CLASS file (that is, CLASS reserved). Compile-time annotation annotation processor implementation rely mainly on AbstractProcessor, this class is in the javax.mail annotation. Processing package, convenience for ourselves generate Java source files at the same time, we also need to introduce some third-party libraries, Javapoet is mainly used to generate Java source files for referenceGithub.com/square/java…The auto-service is used to generate auxiliary information, such as meta-INF /services information.Typical case frame: ButterknifeThe basic principles are as follows:
Annotation processor
First, let’s look at what an Annotation handler is. An Annotation handler is a javAC tool that scans and processes annotations at compile time. You can customize annotations and register them with the annotation handler that handles your annotations. An annotation processor that takes Java code (or compiled bytecode) as input and generates a file (usually a.java file) as output. The generated Java code is in a generated.java file, so you cannot modify existing Java classes, such as adding methods to existing classes. These generated Java files are compiled by JavAC just like any other plain, hand-written Java source code.
Customize the RetentionPolicy.Class annotation scenario
AbstractProcessor interfaces are only introduced here, and custom annotations will be explained later.
public class MyProcessor extends AbstractProcessor {
private Elements mElementsUtils;
/** * note initialization interface, generally used to do some initialization preparation operations; * <p> * processingEnvironment provides several tools * including action elements, print information, file managers, and other tools * *@param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
// Element manipulation tool
Elements elementUtils = processingEnvironment.getElementUtils();
mElementsUtils = elementUtils;
// Create Java source files or class files
Filer filer = processingEnvironment.getFiler();
// Region information
Locale locale = processingEnvironment.getLocale();
// Processor-specific options passed to the annotation processing tool
Map<String, String> options = processingEnvironment.getOptions();
// Any generated source and class files should conform to the source version
SourceVersion sourceVersion = processingEnvironment.getSourceVersion();
// Implementations of utility methods used to operate on types
Types typeUtils = processingEnvironment.getTypeUtils();
// Returns messages to report errors, warnings, and other notifications.
Messager messager = processingEnvironment.getMessager();
}
/** ** Annotation processing interface, annotation really implemented logic * we can generate Java source files through JavaPoet **@paramAnnotations Specifies the type of annotation that the request handles *@paramRoundEnvironment The environment for the current and previous round of information *@returnIf true is returned, comment types are declared and subsequent handlers are not required to process them; If false is returned, the annotation types are unclaimed and may require subsequent processors to process them */
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
// Return true if an error was raised in the previous round of processing; Otherwise return false
boolean b = roundEnvironment.errorRaised();
// Returns root elements for annotation processing generated in the previous round
Set<? extends Element> rootElements = roundEnvironment.getRootElements();
// Returns true if the type generated in this round is not affected by comment processing in subsequent rounds; Otherwise return false.
boolean b1 = roundEnvironment.processingOver();
// Returns the element annotated with the given annotation type.
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(MyAnnotations.class);
for (Element element : elements) {
TypeElement typeElement = (TypeElement) element;
// Get the value of the annotation
MyAnnotations annotation = typeElement.getAnnotation(MyAnnotations.class);
int value = annotation.value();
// Gets the fully qualified name of the element of this type
String s = typeElement.getQualifiedName().toString();
// Get the package path of the current annotation
PackageElement packageElement = mElementsUtils.getPackageOf(typeElement);
String packageName = packageElement.getQualifiedName().toString();
}
return true;
}
/** * The name of the annotation type supported by this processor **@return* /
@Override
public Set<String> getSupportedAnnotationTypes(a) {
/ / this processor only handle MyAnnotations annotation
Set<String> supportedAnnotationTypes = new HashSet<>();
supportedAnnotationTypes.add(MyAnnotations.class.getCanonicalName());
return supportedAnnotationTypes;
}
/** * Returns the latest source version supported by this comment processor **@return* /
@Override
public SourceVersion getSupportedSourceVersion(a) {
return super.getSupportedSourceVersion();
}
@Target(ElementType.PACKAGE)
@Retention(RetentionPolicy.CLASS)
private @interface MyAnnotations {
int value(a); }}Copy the code
RetentionPolicy.RUNTIME
Retentionpolicy. RUNTIME: Annotations are not only saved to the class file, but still exist after the JVM loads the class file.
Annotations provide annotation related operations on various elements, including Class, Method, Field, and so on. Let’s take a look at what annotation-related methods they provide, primarily the AnnotatedElement interface.
/** * Returns true if an annotation of the specified type exists on this element, false */ otherwise
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
returngetAnnotation(annotationClass) ! =null;
}
/** * Returns a comment for this element if the specified comment type exists on it, otherwise null */
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/** * returns a comment that exists on this element. If no annotation exists on this element, the return value is length 0. Callers of this method can modify the array of the returned array at will. It has no effect on arrays returned to other callers. * /
Annotation[] getAnnotations();
/** * returns the comment associated with this element. If there is no comment associated with this element, the return value is an array of length 0. * /
<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) ;
/** * Returns an annotation for this element of the specified type if such an annotation exists directly, or null otherwise. This method ignores inherited comments. * /
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) ;
/** * Returns a comment for this element of the specified type if such a comment exists directly or indirectly. * This method ignores inherited comments. The difference between this method and [' getDeclaredAnnotation(Class) ']) * is that it detects if the argument is a repeatable annotation type * */
<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);
/** * returns comments that appear directly on this element. This method ignores inherited comments. If no comment exists directly on this element, an array of length 0 */ is returned
Annotation[] getDeclaredAnnotations();
Copy the code
The following is a simple example
@AnnotationDemo.TypeRuntimeAnnotation(11)
public class AnnotationDemo {
private static final String TAG = "AnnotationDemo";
public static void testAnnotationDemo(a) {
Log.d(TAG, "testAnnotationDemo() called");
Class<AnnotationDemo> testAnnotationsClass = AnnotationDemo.class;
// Find class annotations
TypeRuntimeAnnotation[] annotations = new TypeRuntimeAnnotation[0];
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
annotations = testAnnotationsClass.getAnnotationsByType(TypeRuntimeAnnotation.class);
}
for (TypeRuntimeAnnotation annotation : annotations) {
int value = annotation.value();
Log.d(TAG,"TypeRuntimeAnnotation: " + value);
}
// How to find annotations
Method[] declaredMethods = testAnnotationsClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
MethodRuntimeAnnotation annotation = declaredMethod.getAnnotation(MethodRuntimeAnnotation.class);
if(annotation ! =null) {
Log.d(TAG, "declaredMethod: " + declaredMethod.getName());
boolean value = annotation.value();
Log.d(TAG, "annotation value: "+ value); }}// Find the annotation field
Field[] declaredFields = testAnnotationsClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
FieldRuntimeAnnotation annotation = declaredField.getAnnotation(FieldRuntimeAnnotation.class);
if(annotation ! =null) {
Log.d(TAG, "declaredMethod: " + declaredField.getName());
int value = annotation.value();
Log.d(TAG, "annotation value: "+ value); }}}@FieldRuntimeAnnotation(1)
public int mValue;
@MethodRuntimeAnnotation(true)
public void testMethodRuntimeAnnotation(a) {}/** * method annotation */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodRuntimeAnnotation {
boolean value(a);
}
/** * field annotation */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldRuntimeAnnotation {
int value(a);
}
/** * class, interface annotation */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeRuntimeAnnotation {
int value(a); }}Copy the code