Annotation provides a secure annotation like mechanism to associate any information or metadata with program elements (classes, methods, member variables, etc.).
Some predefined annotations in JDK
@Override: check whether the method marked by the annotation inherits from the parent class (Interface)
@Deprecated: the method marked by the annotation indicates that the method is obsolete
@SuppressWarnings: Annotation for suppressing warnings. Generally, the parameter passed is all, such as @ SuppressWarnings("all")
Custom annotation
Format:
public @interface Annotation name{ Attribute list; }
Essence: Annotation essentially uses an interface that inherits the Annotation interface by default
Properties: abstract methods in interfaces
requirement:
The return value type of property (abstract method) needs to be the following types
- Basic data type
- String
- enumeration
- annotation
- Arrays of the above types
The attribute is defined, and it needs to be assigned when used:
-
If the default keyword is used to give the default initial value of the attribute when defining the attribute, the attribute can not be assigned when annotation is used
-
If only one attribute needs to be assigned and the name of the attribute is value, value can be omitted and the value can be defined directly.
-
When assigning an array value, the value is wrapped with {}. If the array summary has only one value, {} can be omitted
Meta annotation: an annotation used to describe an annotation
@Target: Describe where annotations can work, such as classes, methods, etc ElementType Value: TYPE:Can act on classes METHOD:Can act on Methods FIELD:Can act on member variables @Retention: Describes the stage in which annotations are retained RetentionPolicy Value @Retention(RetentionPolicy.RUNTIME) (1)RUNTIME:General custom annotation RUNTIME That is, the annotation representing the table description acts on the runtime stage @Documented:Describes whether annotations are extracted to api In document @Inherited:Describes whether annotations are inherited by subclasses
Common uses of annotations:
Generate annotations for the document, such as @ author, @ param.
Track code dependencies and implement alternative configuration file functions, such as spring mvc annotations.
Format check during compilation, such as @ override.
Code generation and completion are performed during compilation, such as @ Data of lombok plug-in.
Custom annotations replace configuration files
Create a normal class:
package com.lhq.annotation; public class Person { private String name; private int age; ... public void eat(){ System.out.println("call eat method!"); } }
Create annotation:
@Target(ElementType.TYPE) // Only works on classes @Retention(RetentionPolicy.RUNTIME) public @interface pro { String className(); String methodName(); }
Use one:
@pro(className = "com.lhq.annotation.Person",methodName = "eat") public class Demo { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { // Gets the bytecode file of this class Class demoClass = Demo.class; pro p = (pro)demoClass.getAnnotation(pro.class); // This line of code is equivalent to producing a subclass implementation object of annotation interface in memory, which is equivalent to the following code: /*class proImpl implements pro{ @Override public String className() { return "com.lhq.annotation.Person"; } @Override public String methodName() { return "eat"; } */ String className = p.className(); String methodName = p.methodName(); // Get the class object according to the reflection mechanism class name Class cls = Class.forName(className); // Create objects from class objects Object o = cls.newInstance(); Method method = cls.getMethod(methodName); //Call the invoke method to execute the method method in the object method.invoke(o); // output: call eat method! } }
Usage 2: configuration file
// yadi.properties // className=com.lhq.annotation.Person // methodName=eat public class Demo2 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, IOException { // Create Properties object Properties pro = new Properties(); //Load the contents of the properties file into the pro object through the class loader of this class ClassLoader classLoader = Demo2.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("com/lhq/annotation/yadi.properties"); pro.load(is); //Use the getProperty method in the properties object to obtain value using key String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); // Get the class object according to the reflection mechanism class name Class cls = Class.forName(className); // Create objects from class objects Object o = cls.newInstance(); Method method = cls.getMethod(methodName); //Call the invoke method to execute the method method in the object method.invoke(o); //output: call eat method! } }
Assignment and verification by annotation
Assignment notes:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.METHOD}) @Inherited public @interface InitSex { enum SEX_TYPE {MAN,WOMAN}; SEX_TYPE sex() default SEX_TYPE.MAN; }
Verification notes:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD,ElementType.METHOD}) @Inherited public @interface ValidateAge { int min() default 18; int max() default 99; int value() default 20; }
**General class: * * use annotation
public class User { private String username; @ValidateAge(min = 20,max = 35,value = 22) private int age; @InitSex(sex = InitSex.SEX_TYPE.MAN) private String sex; ... }
Test:
public class Test { public static void main(String[] args) throws IllegalAccessException { User user = new User(); initUser(user); // The age is 0 and the verification is passed // output: modify the attribute value. The modified value is MAN boolean checkResult = checkUser(user); // output: age value does not meet the conditions printResult(checkResult); // output: verification failed // Reset the age and verify the pass user.setAge(22); checkResult = checkUser(user); printResult(checkResult); // output: verification passed } static void initUser(User user) throws IllegalAccessException { // Get all the properties in the User class (getFields cannot get the private property:) // getFields: reflecting all the accessible public fields of the class // getDeclaredFields: objects reflecting all the fields declared by the class or interface Field[] fields = User.class.getDeclaredFields(); for(Field field:fields) { if(field.isAnnotationPresent(InitSex.class)) { InitSex init = field.getAnnotation(InitSex.class); field.setAccessible(true); // Set attribute gender field.set(user,init.sex().toString()); System.out.println("Finish modifying the attribute value. The modified value is:" + init.sex().toString()); } } } static boolean checkUser(User user) throws IllegalAccessException { // Get all the properties in the User class (getFields cannot get the private property) Field[] fields = User.class.getDeclaredFields(); boolean result = true; // Traverse all attributes for (Field field : fields) { // If there is this annotation on the attribute, the assignment operation is performed if (field.isAnnotationPresent(ValidateAge.class)) { ValidateAge validateAge = field.getAnnotation(ValidateAge.class); field.setAccessible(true); int age = (Integer) field.get(user); if (age < validateAge.min() || age > validateAge.max()) { result = false; System.out.println("Age value does not meet the criteria"); } } } return result; } static void printResult(boolean checkResult) { if (checkResult) { System.out.println("Verification passed"); } else { System.out.println("Verification failed"); } } }
Assign a value to the private property
public class PrivateSet { private String read; public String getReadOnly() { return read; } public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { PrivateSet t = new PrivateSet(); Field f = t.getClass().getDeclaredField("read"); f.setAccessible(true); f.set(t,"write something..."); System.out.println(t.getReadOnly()); // output: write something... } }
Why do you speak slowly
The source code of invoke is as follows:
public final class Method extends Executable { ... @CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { // Permission check if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } // ============================================ MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); // ============================================ } }
In fact, an interface MethodAccessor is maintained inside the Method object, which has two implementation classes, of which NativeMethodAccessorImpl is used to implement local native calls. DelegatingMethodAccessorImpl, as its name suggests, is a delegation implementation class, which delegates the invoke operation to the native Method.
public class ReflectSlow { public static void target(int i) { new Exception("#"+i).printStackTrace(); } public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class<?> kclass = Class.forName("com.lhq.annotation2.ReflectSlow"); // First, Class.forName belongs to the native method, which needs to undergo language execution level conversion. That is, the switch from java to c and then to java. Method method = kclass.getMethod("target", int.class); // The getMethod operation will traverse the public methods of the class. If it does not hit, it will also look in the parent class. And return the // A copy of the method object. After the search is successful, the copy object will occupy heap space and cannot be inlined optimized, // On the contrary, it will also cause the increase of gc frequency. It also has an impact on performance. method.invoke(null,0); } }
Output:
/** output: * java.lang.Exception: #0 * at com.lhq.annotation2.ReflectSlow.target(ReflectSlow.java:8) * at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) * at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) * at sun.reflect.DelegatingMethodAccessorImpl. invoke(DelegatingMethodAccessorImpl.java:43) * at java.lang.reflect.Method.invoke(Method.java:498) * at com.lhq.annotation2.ReflectSlow.main(ReflectSlow.java:14) */
Replace with method.invoke(null,i) that circulates multiple times
When the program calls the 16th time, the call stack is changed to GeneratedMethodAccessor1 instead of the native method. This is because the Jvm maintains a threshold - Dsun.reflect.inflationThreshold, which is 15 by default. When the reflection native calls more than 15 times, it will trigger the Jvm to dynamically generate bytecode, and all subsequent operations will call the dynamic implementation.
Compared with the native implementation, the dynamic implementation is much more efficient, because the native implementation needs to switch to the C language at the Java language level, and then switch to the Java language again. However, because the dynamic implementation needs to generate bytecode for the first generation, this operation is time-consuming. Therefore, compared with a single call, native is much faster than dynamic implementation.
To sum up, delegate implementation. If you use less, you can directly use native. Although the native method needs to be switched, the generation of bytecode is slower. If you use more invoke, the generated bytecode will be valuable, and the bytecode will be generated. The switching is omitted and it is fast. Both: slow.
And the annotation processor. Let's open another one.
reference resources
An article to fully master Java custom annotation
Customization of Java annotations
Java uses custom annotations instead of configuration files (Introduction to annotations)
Why are reflection calls slow? Fine push reflection details!
This article was published on orzlinux.cn