SpringBoot -- Conditional annotation for detailed explanation and application of automatic assembly principle

         As the mainstream framework of today's development, SpringBoot is impossible as a java developer. It is typical and easy to use, but when you understand the principle, you will unconsciously sigh, really TMNB!

        This paper mainly explains the principle of its automatic assembly and how we use it in our daily life.

1, Conditional annotation

        First, we create a Student object without any properties

public class Student {
}

Then we establish a proposition:

If fastjson is imported into the project, the Student will be placed in the IOC. If not, it will not be loaded.

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootConditionApplication.class, args);
    }

}

As shown in the figure above, this is the startup class (boot class) of springboot. Here, let's briefly talk about the run method of SpringApplication

  We can see that the run method has a return value. The object is ConfigurableApplicationContext, which is a configuration context object. The IOC in spring is a context object. Therefore, we can see that through this context object, we can obtain our environment and beans in the IOC.

  So we can write the code like this

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        // Gets the bean named student
        Object student = context.getBean("student");
        System.out.println(student);
    }

}

At this time, we will write a class of StudentConfig to configure students, as follows

@Configuration
public class StudentConfig {
    
    @Bean
    public Student student(){
        return new Student();
    }
}

When we start the project, we can see that the project is successfully started directly

However, this does not meet our requirements, that is, there is a requirement to load student s only after importing fastjson in the project. At this time, the Condition annotation is required  . The function of Conditional is that when you register a bean, you can add certain user-defined conditions to the bean. When this Condition is met, the bean is registered, otherwise it is not registered

Let's look at the code of Conditional

  This annotation only defines an array of Class type, which is nothing. The point is that the generic type is required to be Condition

Then let's look at the interface of Condition

This interface only defines a method named matches and the return value is a boolean. At this time, it is not difficult to guess that when the value in the Conditional annotation is true, the annotated method will be executed. Here, we write another class to implement the Condition interface  , You can see that the default return value is false, and then we will modify it slightly  

public class ClassOnCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Define a Boolean value
        boolean flag;
        try {
            // Find the class of fastjson through reflection
            Class<?> aClass = Class.forName("com.alibaba.fastjson.JSON");
            // Returns true if found
            flag = true;
        } catch (ClassNotFoundException e) {
            // false if not found
            flag = false;
        }
        return flag; 
    }
}

Finally, don't forget to specify the class we just wrote in StudentConfig

@Configuration
public class StudentConfig {

    @Bean
    @Conditional(ClassOnCondition.class)
    public Student student(){

        return new Student();
    }
}

Then we annotate the dependencies in the project

  Start the project, and sure enough, an error is reported, indicating that the student bean cannot be found

  At this time, there must be witty children who may ask questions. If this path is written dead, it will be very coupled. Don't you make a dynamic loading?

  Here, we can define an annotation called ConditionalOnClass, which defines an array, because there can be multiple set conditions

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassOnCondition.class)
public @interface ConditionalOnClass {
    String[] value();
}

At this time, let's change the annotation in StudentConfig to our custom and pass in the value

@Configuration
public class StudentConfig {

    @Bean
//    @Conditional(ClassOnCondition.class)
    @ConditionalOnClass("com.alibaba.fastjson.JSON")
    public Student student(){
        return new Student();
    }
}

Finally, we are modifying the previously defined ClassOnCondition, but before modifying, let's talk about the two input parameters:

ConditionContext: this object is a context object, which can be used to obtain environment, ioc and class loader

AnnotatedtypeMetadata: annotation meta object, which can obtain the value passed in the annotation

        We modify the code to the following figure

public class ClassOnCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Define a Boolean value
        boolean flag;
        try {
            // Get the value from the annotation name
            Map<String, Object> map = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
            // Gets the value of value
            String[] classNames = (String[]) map.get("value");
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
            }
            flag = true;
        } catch (ClassNotFoundException e) {
            // false if not found
            flag = false;
        }
        return flag;
    }
}

Canceling the fastjson dependency is still wrong

  After joining, I successfully obtained

  2, Application of Conditional annotation in spring boot

During the interview, the interviewer may ask how to limit in spring

This time

  The spring boot version of this example is 2.6.1. We can find this project, spring boot autoconfigure

  Find the condition folder of the project, and you can see many related annotations,

For example: ConditionOnProperty: when there are corresponding key value pairs in the configuration file, the corresponding methods will be executed

  These are defined by springboot. We can use them directly. The effect is the same as that we just customized

Then we take kafka as an example to see how spring boot does

  Find this class and let's look at the code

  When 1 in the figure means that there is no bytecode file of KafkaTemplate in the project, the following 2 will not be executed

         When 1 is satisfied, let's look at 2 (meaning that when there is no KafkaTemplate in the IOC container, the annotation method will be executed)

         This is the usage of the Conditional annotation in springboot. In the next page, we will explain how to switch the Enable annotation in springboot.

 

Tags: Java Spring Spring Boot

Posted on Sun, 05 Dec 2021 12:59:37 -0500 by jeff_valken