Custom Annotation Injection Property Values (Based on Reflection and Static Variables)

Links to the original text: http://markey.cc/2019/02/16/SpringBoot%E4%B9%8B%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3%EF%BC%88%E5%9F%BA%E4%BA%8E%E5%8F%8D%E5%B0%84%E5%92%8C%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F%EF%BC%89/

This article will illustrate how to customize an annotation Name and inject values into the class's properties through annotations

How to process annotations through Spring configuration classes

  1. Definition Notes
  2. Define a Spring configuration class
  3. Injecting static properties using Java reflection in configuration classes

Definition Notes

Define an annotation "Name" that accepts a String type attribute and can be used on a class or attribute.

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Name {

    String value() default "";
}

Define Sping configuration class

Define a Spring configuration class and use the @PostConstruct annotation method to process annotations during Spring startup

@Configuration
public class NameConfig {

    List<Class> classList = new ArrayList<>();

    @PostConstruct
    public void init() {
        //Processing annotations
    }
}

Injecting static properties using Java reflection

Get the properties of the specified class through Java reflection, and determine whether Name annotations are used or not. In some cases, get the values of Name annotations and inject them into the static properties.

tips: Here you specify the class that will use Name annotations, for example Xiaohong.class

@Configuration
public class NameConfig {

    List<Class> classList = new ArrayList<>();

    @PostConstruct
    public void init() {
        classList.add(Xiaohong.class);
        classList.add(Xiaobai.class);
        classList.forEach(e -> setName(e));
    }

    public void setName(Class clazz) {
        //Processing annotations on classes
        if (clazz.isAnnotationPresent(Name.class)) {
            Name annotation = (Name) clazz.getAnnotation(Name.class);
            try {
                Field field = clazz.getDeclaredField("name");
                field.setAccessible(true);
                field.set(null, annotation.value());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return;
        }
        //Processing annotations on attributes
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Name annotation = declaredField.getAnnotation(Name.class);
            if (null == annotation) {
                continue;
            }
            declaredField.setAccessible(true);
            try {
                declaredField.set(null, annotation.value());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

Use annotations on attributes

Use annotation Name on an attribute in a class and specify a value of "Xiaohong"

public class Xiaohong {

    @Name("Xiaohong")
    static String name;

    public void sayHi() {
        System.out.println("hello, my name is " + name);
    }
}

Test it out

The following examples will be printed out:
hello, my name is Xiaohong

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloTest {

    @Test
    public void HelloXiaohongTest() {
        Xiaohong xiaohong = new Xiaohong();
        xiaohong.sayHi();
    }
}

Use annotations on classes

Use annotation Name on the class and specify a value of "Xiaobai"

@Name("Xiao Bai")
public class Xiaobai {

    static String name;

    public void sayHi() {
        System.out.println("hello, my name is " + name);
    }
}

Test it out

The following examples will be printed out:
hello, my name is Xiaobai

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloTest {

    @Test
    public void HelloXiaobaiTest() {
        Xiaobai xiaobai = new Xiaobai();
        xiaobai.sayHi();
    }
}

Why can only annotate the function and static attributes

Through the above method, we automatically inject Java attributes in the Spring boot process, not the running process, so at this time we only know the class, not the object.
However, only static attributes are injected without specifying objects, so static attributes can be injected in this way.

If we want to handle the injection of non-static attributes, we can encapsulate an annotation processing method. After creating objects in the running process (for example, in the construction method), we can call the annotation processing method to achieve the effect.

For example:

Processing annotations in construction methods

Define a method setName(), which handles Name annotations and takes the object using Name annotations as a parameter; call this method in the constructor.

@Name("Blue")
public class Xiaolan {

    private String name;

    public Xiaolan() {
        setName(this);
    }

    public void sayHi() {
        System.out.println("hello, my name is " + name);
    }

    public void setName(Xiaolan xiaolan) {
        Class clazz = this.getClass();
        if (clazz.isAnnotationPresent(Name.class)) {
            Name annotation = (Name) clazz.getAnnotation(Name.class);
            xiaolan.name = annotation.value();
            return;
        }
    }
}

Test it out

The following examples will be printed out:
hello, my name is Xiaolan

@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloTest {

    @Autowired
    NameConfig nameConfig;

    @Test
    public void HelloXiaolanTest() {
        Xiaolan xiaolan = new Xiaolan();
        xiaolan.sayHi();
    }
}

summary

Following the ideas above, we can see that the focus of custom annotations is to consider how to handle annotations according to the purpose of the annotations.

For example, the requirement of this article is to inject an attribute through annotations. Considering that the attribute can be static, the Spring configuration adds reflection; if the attribute cannot be static, it can be implemented by construction.

In practical development, there are many other ways to process annotations, each with its advantages and disadvantages.

For example:

  • Processing annotations in Bean's post-processor
    Through the implementation of Bean's post-processor method, it cooperates with Java reflection.
    This annotation can be used on classes, methods, and attributes.
    The limitation is that such annotations can only be used in Spring Bean s.
    Reference resources Custom Annotations for SpringBoot (Implementation Based on BeanPostProcessor Interface)

  • Processing through Spring AOP
    The principle is to define the tangent point through annotations.
    The advantage is that it is easy to write.
    But such annotations can only be used in Spring Bean s, and only in methods.
    Reference resources Custom Annotations for SpringBook (based on AOP implementation)

  • Through AspectJ section processing
    Through SspectJ slice programming, similar to SPring AOP slice. There are no limitations that can only be used in methods.

  • Processing through Spring Configuration Class
    By configuring classes, annotations are processed during Spring startup, and Java reflection mechanism is configured to inject static variables.
    The limitation is that only static attributes can be injected.
    This is also the method used in this paper.

Keywords: Spring Java Attribute SpringBoot

Added by sheffrem on Wed, 04 Sep 2019 10:41:34 +0300