Common condition annotation

Posted by parboy on Tue, 21 Dec 2021 19:22:55 +0100

Common condition annotation

@ Conditional annotation in spring

Basically, all extended class conditional annotations are extended based on this annotation. This annotation is from spring 4 Use after 0. It is generally used to limit whether the configuration class is effective or whether a bean needs injection.

@Definition of Conditional

//This annotation can be annotated on classes and methods
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) 
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

This annotation can be defined on a class. It is generally used on the configuration configuration class to limit whether the configuration class is effective.

The annotation can also be defined on the method and used in combination with @ bean to judge whether the bean needs injection.

The attribute of this annotation is a Class array of the implementation classes of the Condition interface. Each implementation Class of the Condition interface represents a Condition. The overall Condition is true and the configuration Class or @ Bean will take effect only if all the conditions are met.

Condition interface

public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}

It can be seen that the Condition interface needs to implement the matches method. Returning true means that the Condition is met, and false means that the Condition is not met.

Example

This example is reproduced from: https://blog.csdn.net/xcy1193068639/article/details/81491071

Create a Condition implementation class, use conditionContext in the matches method to obtain the environment configuration, and judge whether the current system name contains Windows. That is, the Condition is intended to judge whether the running environment is Windows

public class WindowsCondition implements Condition {
	/**
	 * @param conditionContext:Determine the context in which the condition can be used
	 * @param annotatedTypeMetadata:Annotation information where the annotation is located
	 * */
	@Override
	public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
	    //Get beanFactory used by ioc
	    ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
	    //Get class loader
	    ClassLoader classLoader = conditionContext.getClassLoader();
	    //Get current environment information
	    Environment environment = conditionContext.getEnvironment();
	    //Gets the bean defined registration class
	    BeanDefinitionRegistry registry = conditionContext.getRegistry();
	 
	    //Get current system name
	    String property = environment.getProperty("os.name");
	    //If windows is included, it indicates that it is a Windows system and returns true
	    if (property.contains("Windows")){
	        return true;
	    }
	    return false;
	}
}

Note on the class. At this time, both bean s can be injected. After modifying the environment parameter to Linux (- Dos.name=Linux), neither class is injected.

@Conditional({WindowsCondition.class})
@Configuration
public class BeanConfig {

	@Bean(name = "bill")
	public Person person1(){
	    return new Person("Bill Gates",62);
	}
	 
	@Bean("linus")
	public Person person2(){
	    return new Person("Linus",48);
	}

}

Extension condition annotation in SpringBoot

There are 13 extended annotations, all on org. Org springframework. boot. autoconfigure. Condition package.

These annotations have the same meta annotation, which can be obtained by analyzing the meta annotation:

  • Can be applied to TYPE, that is, all classes (@ Configuration, @Component, @Service, @Repository, or @Controller) automatically scanned by Spring can be loaded by adding corresponding @ ConditionalOnXxxx
  • Can be applied to METHOD, so methods marked with @ Bean can also apply these annotations
  • They are marked with @ Conditional annotation. The OnBeanCondition and other custom conditions still implement the Condition interface. It's no mystery to change the soup without changing the dressing. It's just a more concrete encapsulation.

Let's analyze one by one:

@ConditionalOnProperty

This condition is true when the specified property exists in the configuration file (application.properties or application.yml file).

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
    //value and name can be separated by multiple middle commas.
    String[] value() default {};//The full name of the specified attribute cannot be used together with name.

    String prefix() default "";//Prefix of attributes in configuration file

    String[] name() default {};//Specified attribute name

    String havingValue() default "";//The attribute value of the specified attribute must be equal to the specified value. It is used when value or name is one

    boolean matchIfMissing() default false;//Whether to allow loading when there is a mismatch. When it is true, even if there is a mismatch, it will not affect the effectiveness of bean injection or configuration class.
}

@ConditionalOnBean and ConditionalOnMissingBean

@ConditionalOnBean: it is loaded only when the specified bean exists in the context

@ConditionalOnMissingBean: the context will be loaded only when the specified bean does not exist

public @interface ConditionalOnBean {
    Class<?>[] value() default {};//Specify the class object array of the bean. You can specify multiple. It is true only when they all exist

    String[] type() default {};//The name, class. Of the class that needs to be a condition getName()

    Class<? extends Annotation>[] annotation() default {};//The annotation class required by the condition (bean decorated with the specified annotation)

    String[] name() default {};//The name of the bean in the spring container

    SearchStrategy search() default SearchStrategy.ALL;//Search container hierarchy, current container, parent container

    Class<?>[] parameterizedContainer() default {};//Other classes of the specified bean type may be included in their generic parameters
}


public @interface ConditionalOnMissingBean {
    Class<?>[] value() default {};

    String[] type() default {};

    Class<?>[] ignored() default {};//When identifying matching beans, the class type of beans that can be ignored

    String[] ignoredType() default {};//When identifying matching beans, the class type name of the bean that can be ignored

    Class<? extends Annotation>[] annotation() default {};

    String[] name() default {};

    SearchStrategy search() default SearchStrategy.ALL;

    Class<?>[] parameterizedContainer() default {};
}
//For other functions, refer to the @ ConditionalOnBean annotation.

@ConditionalOnClass and @ ConditionalOnMissingClass

The function is similar to the above two. The above two judge the objects in the context. These two judge whether there is this class under the calsspath. When there is a bean definition order problem using the above two (the bean injection as a condition is initialized after the bean successfully injected by the condition), you can use these two instead.

@ConditionalOnExpression

It is similar to @ ConditionalOnProperty, but SpEL can be used here to judge more complex properties

public @interface ConditionalOnExpression {
    String value() default "true";
}


//There is only one value attribute, so the spel expression when used should return true or false
@ConditionalOnExpression("${mybean.enable:true} and ${mybean.name:test}")

@ConditionalOnSingleCandidate

This annotation is similar to @ ConditionalOnBean, but it requires that there is only one object of the specified class in the context, that is, when a class has multiple objects in the context with different names, the annotation is false and the onbean annotation is true.

//See @ ConditionalOnBean for the meaning of each attribute
public @interface ConditionalOnSingleCandidate {
    Class<?> value() default Object.class;

    String type() default "";

    SearchStrategy search() default SearchStrategy.ALL;
}

@ConditionalOnResource

The bean to be loaded depends on whether the specified resource exists in the classpath, that is, the resource to be used when loading the bean must exist in the calsspath, otherwise it will not be loaded.

public @interface ConditionalOnResource {
    String[] resources() default {};//The dependent resource path takes classpath as the root path and can be multiple
}

@ConditionalOnResource(resources = "/logback.xml")

The following are unexpected and will not be explained in detail

@ConditionalOnJndi

The bean is loaded only after the specified resource is loaded through JNDI

@ConditionalOnJava

Only Java running the specified version will load beans

@Conditional onwebapplication and @ conditional onnotwebapplication

This bean is loaded only when it is running (not in) a web application

@ConditionalOnCloudPlatform

Only when running on the specified cloud platform can the specified bean be loaded

Combination condition

Combination condition OR

What should we do if we want the condition of combination to be the relationship of or? We can complete this requirement by inheriting AnyNestedCondition. Open the AnyNestedCondition class and view the class description.

Conditional combination NONE

bean

@ConditionalOnCloudPlatform

Only when running on the specified cloud platform can the specified bean be loaded

Combination condition

Combination condition OR

What should we do if we want the condition of combination to be the relationship of or? We can complete this requirement by inheriting AnyNestedCondition. Open the AnyNestedCondition class and view the class description.

Conditional combination NONE

We can do this by inheriting NoneNestedConditions.

Topics: Java Spring Spring Boot