Annotations and reflections

1. Annotation

1.1 Definition of Annotation

Annotation is the metadata of the source code, which is the label in the code. Annotations have the following characteristics:

  1. An annotation is an add-on that depends on other elements (packages, classes, methods, properties, etc.) to exist.
  2. The annotation itself has no effect, it will only work when it is parsed by an external program at the right time.

1.2 Classification of annotations

  1. By source
    • JDK comes with annotations such as @Override, @Deprecated, @SuppressWornings.
    • Third-party annotations.
    • Custom annotations.
  2. By life cycle
    • SOURCE: Only exists in the source code, compiled into a class file does not exist.
    • Class: exists in source code and in class files.
    • RUNTIME: Annotations are retained until runtime.

1.3 Meta annotations

Meta-annotations refer to annotations used to modify annotations, including the following:

  1. @Retention: Indicates the life cycle of Annotation. The incoming value is an enumeration type. The optional values ​​are:
    • RetentionPolicy.SOURCE
    • RetentionPolicy.CLASS
    • RetentionPolicy.RUNTIME
  2. @Target: Indicates which elements of the program can be decorated by Annotation. The incoming value is of ElemetType[] type, and the value can be:
    • ElementType.CONSTRUCTOR : Constructor
    • ElementType.FIELD: property
    • ElementType.LOCAL_VARIABLE: local variable
    • ElementType.METHOD: method
    • ElementType.PACKAGE: package
    • ElementType.PARAMETER: parameter
    • ElementType.TYPE: classes, interfaces (including annotation types and enum declarations)
  3. @Documented: An annotation using this modification will be extracted into a document by the javadoc tool. To use this annotation, its @Retention must be set to RetentionPolicy.RUNTIME .
  4. @Inherited: has inheritance.

1.4 Custom annotations

Problems to be aware of when custom annotations are made:

  1. Defined using the @interface keyword.

  2. Automatically inherits the java.lang.annotation.Annotation interface.

  3. The types of configuration parameters can only be eight basic types, String, Class, enum, Annotation and corresponding array types.

  4. The syntax format of the configuration parameter declaration is as follows ([] indicates that it can be omitted):

    type variable name() [default Defaults];
    
  5. If there is only one configuration parameter, its parameter name must be value.

  6. If the defined annotation contains configuration parameters, when using the annotation, the parameter value must be specified in the form: "parameter name=parameter value". If there is only one parameter, you can directly write the parameter value. The parameter with the default value specified in the definition may not specify the value, but the value must be specified if there is no value.

  7. Annotations without members are called tags, and those with members are called metadata.

1.5 Analysis of annotations

Reference Code:

(1)Info.java

 1 package com.hkl;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * annotation
10  */
11 @Retention(RetentionPolicy.RUNTIME)
12 @Target({ElementType.TYPE, ElementType.METHOD})
13 public @interface Info {
14     String info();
15     String birthday();
16     int age() default 0;
17 }
18 
19 @Retention(RetentionPolicy.RUNTIME)
20 @Target({ElementType.TYPE, ElementType.METHOD})
21 @interface Desc{
22     String value();
23 }

(2)App.java

 1 package com.hkl;
 2 
 3 import java.lang.reflect.Method;
 4 
 5 /**
 6  * Hello world!
 7  *
 8  */
 9 @Info(info = "hkl", birthday = "2019/7/20")
10 @Desc("this is a class")
11 public class App 
12 {
13     @Info(info = "hkl", birthday = "2019/7/20", age = 22)
14     @Desc("this is a method")
15     public static void main( String[] args )
16     {
17         // Parse annotations
18         try {
19             Class clazz = Class.forName("com.hkl.App");
20 
21             // Get annotations for class decoration
22             System.out.println("---------Annotations in a class---------");
23             if(clazz.isAnnotationPresent(Info.class)){
24                 Info classInfo = (Info) clazz.getAnnotation(Info.class);
25                 System.out.println(classInfo.info());
26                 System.out.println(classInfo.birthday());
27                 System.out.println(classInfo.age());
28             }
29 
30             if(clazz.isAnnotationPresent(Desc.class)){
31                 Desc classDesc = (Desc)clazz.getAnnotation(Desc.class);
32                 System.out.println(classDesc.value());
33             }
34 
35             // Get annotation for method modification
36             Method[] methods = clazz.getMethods();
37 
38             System.out.println("---------Annotation parsing in methods---------");
39             for(Method method : methods){
40                 if(method.isAnnotationPresent(Desc.class)){
41                     Desc methodDesc = (Desc)method.getAnnotation(Desc.class);
42                     System.out.println(methodDesc.value());
43                 }
44 
45                 if(method.isAnnotationPresent(Info.class)){
46                     Info methodInfo = (Info)method.getAnnotation(Info.class);
47                     System.out.println(methodInfo.info());
48                     System.out.println(methodInfo.birthday());
49                     System.out.println(methodInfo.age());
50                 }
51 
52             }
53         } catch (ClassNotFoundException e) {
54             e.printStackTrace();
55         }
56     }
57 }

2. Reflection

2.1 What reflection

Reflection means that the program obtains the internal information of any class with the help of the reflection API during runtime, and uses this internal information to operate the internal properties and methods of the corresponding object.

Any class, when used for the first time, will be loaded into the method area of ​​the heap memory by the JVM. After the JVM loads the class successfully, it will generate a corresponding Class object in the method area (a class only needs one Class object), and this Class object contains all the structural information of the loaded class.

2.2 Common ways to obtain Class objects

(1) class attribute of class

Each class has a class static attribute, which is the Class object corresponding to the class.

1 Class<Person> cl1 = Person.class; 

(2) getClass() method of Object object

1 Person p1 = new Person();
2 Class<Person> cl2 = (Class<Person>) p1.getClass(); 

(3) Through the forName() method of the Class class (most commonly used)

1 try {
2     Class cl3 = Class.forName("com.llm.hkl.Person");
3 } catch (ClassNotFoundException e) {
4     e.printStackTrace();
5 }

(4) Through the ClassLoader class (not commonly used)

1 ClassLoader cl = Person.class.getClassLoader();
2 try {
3     Class cl4 = cl.loadClass("com.llm.hkl.Person");
4 } catch (ClassNotFoundException e) {
5     e.printStackTrace();
6 }

2.3 Basic use of reflection

Basic uses of reflection include creating objects, setting properties, and calling methods. Most of the get methods in Class objects have Declared and no Declared, their differences are:

  1. No Declared: Only public-decorated ones can be obtained, including the current class and all parent classes.
  2. With Declared: Get all (including private) of the current class, but not including its parent class.

Person class:

 1 public class Person {
 2     private String name;
 3     private int age;
 4     public String habbit;
 5 
 6     public Person() {
 7     }
 8 
 9     public Person(String name, int age) {
10         this.name = name;
11         this.age = age;
12     }
13 
14     private Person(String name, int age, String habbit) {
15         this.name = name;
16         this.age = age;
17         this.habbit = habbit;
18     }
19 
20     public String getName() {
21         return name;
22     }
23 
24     public void setName(String name) {
25         this.name = name;
26     }
27 
28     public int getAge() {
29         return age;
30     }
31 
32     public void setAge(int age) {
33         this.age = age;
34     }
35 
36     public String getHabbit() {
37         return habbit;
38     }
39 
40     public void setHabbit(String habbit) {
41         this.habbit = habbit;
42     }
43 
44     private String say(String str){
45         String str1 = name+"Say:"+str;
46         System.out.println(str1);
47         return str1;
48     }
49 
50     public void eat(String food){
51         System.out.println(name+"eat"+food);
52     }
53 
54     @Override
55     public String toString() {
56         return "["+name+","+age+","+habbit+"]";
57     }
58 }
testing method:
 1 public class PersonTest {
 2     @Test
 3     public void ReflexTest() throws Exception{
 4         System.out.println("---------- new way to create objects ----------");
 5         // Create a Person object the normal way
 6         Person p1 = new Person("hkl", 22);
 7 
 8         // set properties directly
 9         p1.habbit = "programming";
10         // call method
11         System.out.println(p1);
12 
13         //Cannot directly set private properties and call private methods
14         //p1.name = "hkl";
15         //p1.say(""Hello);
16 
17         //Create objects by reflection
18         System.out.println("---------- Create objects by reflection ----------");
19         Class<Person> clazz = Person.class;
20 
21         // Call the no-argument constructor to join the object
22         Constructor<Person> constructor1 = clazz.getConstructor();
23         Person p2 = constructor1.newInstance();
24         System.out.println(p2);
25 
26         // via a parameterized constructor
27         Constructor<Person> constructor2 = clazz.getConstructor(String.class, int.class);
28         Person p3 = constructor2.newInstance("hkl", 22);
29         System.out.println(p3);
30 
31         // via private constructor
32         Constructor<Person> constructor3 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
33         constructor3.setAccessible(true);
34         Person p4 = constructor3.newInstance("hkl", 22, "programming");
35         System.out.println(p4);
36 
37         //Set public properties via reflection
38         Field personFeildHabbit = clazz.getDeclaredField("habbit");
39         personFeildHabbit.set(p2, "programming");
40 
41         //Set private properties via reflection
42         Field personFeildAge = clazz.getDeclaredField("age");
43         personFeildAge.setAccessible(true);
44         personFeildAge.set(p2, 18);
45 
46         //Calling a method via reflection
47         Method personMethodToString = clazz.getDeclaredMethod("toString");
48         // The return value of the method is the return value of the calling method
49         String str = (String)personMethodToString.invoke(p2);
50         System.out.println(str);
51 
52         // Calling private methods via reflection
53         Method personMethodSay = clazz.getDeclaredMethod("say", String.class);
54         personMethodSay.setAccessible(true);
55         String str2 = (String)personMethodSay.invoke(p2, "Hello");
56         System.out.println(str2);
57     }
58 }

2.5 Design Patterns: The Proxy Pattern

Wrap the original object with a proxy object, then replace the original object with the proxy object. Any calls to the original object go through the proxy object, which decides whether and when to route method calls to the original object.

2.5.1 Static proxy

The proxy class and the original object are determined during compilation, which is not conducive to the expansion of the program, and each proxy can only serve one interface, which will generate a large number of proxy classes during the development process.

Implementation of static proxy:

  1. The proxy class and the original object implement the same interface.
  2. The proxy class keeps a reference to the original object.
  3. The proxy class calls the method of the original object.

ClothFactory interface:

1 package com.hkl.proxy;
2 
3 
4 public interface ClothFactory {
5     String producer();
6 }

ClothFactoryProxy class:

 1 package com.hkl.proxy;
 2 
 3 public class ClothFactoryProxy implements ClothFactory{
 4     private ClothFactory clothFactory;
 5 
 6     public ClothFactoryProxy(ClothFactory clothFactory) {
 7         this.clothFactory = clothFactory;
 8     }
 9 
10     @Override
11     public String producer() {
12         System.out.println("The proxy object does some preparation");
13         clothFactory.producer();
14         System.out.println("The proxy object does some finishing touches");
15 
16         return null;
17     }
18 }

NikeFactory class:

 1 package com.hkl.proxy;
 2 
 3 public class NikeFactory implements ClothFactory{
 4 
 5     public NikeFactory() {
 6     }
 7 
 8     @Override
 9     public String producer() {
10         System.out.println("Nike making clothes");
11         return null;
12     }
13 }

testing method:

 1 @Test
 2 public void staticProxyTest(){
 3     // Create the proxied object
 4     NikeFactory nikeFactory = new NikeFactory();
 5 
 6     // Create proxy object
 7     ClothFactoryProxy clothFactoryProxy = new ClothFactoryProxy(nikeFactory);
 8 
 9     // Invoke a method of the proxied object through the proxy object
10     clothFactoryProxy.producer();
11 }

2.5.2 Dynamic proxy mode

Dynamic proxies are implemented through Java's reflection mechanism. With dynamic proxying, only one proxy object can proxy all objects.

Humen : interface

1 package com.llm.proxy;
2 public interface Human {
3     String belief();
4     void eat(String food);
5 }

SuperMan: Class

 1 package com.hkl.proxy;
 2 
 3 public class SuperMan implements Human {
 4     @Override
 5     public String belief() {
 6         return "i believe i can do it!";
 7     }
 8 
 9     @Override
10     public void eat(String food) {
11         System.out.println("eating"+food);
12     }
13 }

Dynamic proxy class:

 1 package com.hkl.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 
 8 public class DynamicProxy {
 9     // get proxy object
10     public static Object getInstance(Object obj){
11         MyInvocation h = new MyInvocation();
12         h.bind(obj);
13         // Create objects dynamically
14         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
15     }
16 }
17 
18 class MyInvocation implements InvocationHandler{
19     private Object obj;
20 
21     /**
22      * Bind the proxied object
23      * @param obj
24      */
25     public void bind(Object obj){
26         this.obj = obj;
27     }
28 
29     @Override
30     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
31         // The proxy object invokes the method of the proxied object
32         Object ret = method.invoke(obj, args);
33         return ret;
34     }
35 }

testing method:

 1 @Test
 2 public void dynamicProxyTest(){
 3     SuperMan sm = new SuperMan();
 4     Human h = (Human)DynamicProxy.getInstance(sm);
 5     System.out.println(h.belief());;
 6     h.eat("Mala Tang");
 7 
 8     NikeFactory nikeFactory = new NikeFactory();
 9     ClothFactory clothFactory = (ClothFactory) DynamicProxy.getInstance(nikeFactory);
10     clothFactory.producer();
11 }

Posted by huzefahusain on Fri, 06 May 2022 20:02:25 +0300