Make writing a habit together! This is the 11th day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details

  • Spring Reading Directory

Accumulate over a long period, constant dripping wears away a stone 😄

preface

@ Conditional means conditional in Chinese. That’s exactly what it does.

@Conditional is an annotation provided by Spring4 to make a judgment according to specified conditions. Only when all conditions are met will the candidate Bean be registered with the container.

The source code to define

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * All {@link Condition Conditions} that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();

}
Copy the code

This annotation can be annotated on a class or method. The annotation contains a value attribute that is an array of interfaces that inherit Condition.

The Condition interface is defined as follows:

@FunctionalInterface
public interface Condition {

	/**
	 * Determine if the condition matches.
	 * @param context the condition context
	 * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
	 * or {@link org.springframework.core.type.MethodMetadata method} being checked
	 * @return {@code true} if the condition matches and the component can be registered,
	 * or {@code false} to veto the annotated component's registration
	 */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}
Copy the code

Condition is a functional interface, so you can assign values to targets using lambda expressions or method references. Condition (); if Condition () is true, inject bean (); if false, inject bean ().

test

  • Provide two beans, respectivelyUserServiceImpl,ProductServiceImpl.
@Configuration
public class BeanConfig {

    @Bean
    public UserServiceImpl u(a){
        UserServiceImpl userService = new UserServiceImpl();
        System.out.println("u = " + userService);
        return userService;
    }

    @Bean
    public ProductServiceImpl p(a){
        ProductServiceImpl productService = new ProductServiceImpl();
        System.out.println("p = " + productService);
        returnproductService; }}Copy the code
  • Start the class
@ComponentScan(basePackages = "com.cxyxj.conditional")
public class AppMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppMain.class);

        // Print the bean name
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for(String name : beanDefinitionNames){ System.out.println(name); }}}Copy the code
  • Start the result

You can see that both beans are registered with the container. With the foreplay ready, let’s move on to the main act. If I don’t want to inject both beans at the same time now, I want to be controlled by a flag bit, which if true will inject UserServiceImpl, false, or empty will inject ProductServiceImpl.

Custom Condition

UserCondition

public class UserCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("enable.flag");
        //如果 enable.flag 的值为 true
        if(Boolean.parseBoolean(property)){
            return true;
        }
        return false; }}Copy the code

ProductCondition

public class ProductCondition implements Condition {

    / * * * *@paramContext: Can get Bean factory, Environment, Bean definition registrar, ClassLoader, ResourceLoader *@paramMetadata: Obtain annotation information, such as method names and *@return* /
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("enable.flag");
        // If the enable.flag value is false or null
        if(! Boolean.parseBoolean(property)){return true;
        }
        return false; }}Copy the code

Mark the method

Annotated in terms of methods, a custom Condition can only control whether a Bean instance needs injection or not.

Add our custom Condition to the two @bean methods.

// If the flag bit is true, inject 'UserServiceImpl'
@Bean
@Conditional({UserCondition.class})
public UserServiceImpl u(a){
    UserServiceImpl userService = new UserServiceImpl();
    System.out.println("u = " + userService);
    return userService;
}

// If the flag bit is false or empty, 'ProductServiceImpl' is injected.
@Bean
@Conditional({ProductCondition.class})
public ProductServiceImpl p(a){
    ProductServiceImpl productService = new ProductServiceImpl();
    System.out.println("p = " + productService);
    return productService;
}
Copy the code
  • Start the result

ProductServiceImpl is injected because enable.flag is not yet provided and the value obtained is null. Matches the matching rules of ProductCondition.

Next, provide enable.flag=true. Instead of creating a configuration file, just add it to the startup parameters.

Since this is a Spring project, I cannot configure VM options variables, and Environment variables are also configured in the same format as key= value.

  • Start the result

Labeled on the class

@Conditional annotations can also be annotated ona class, where the annotation represents all the @bean methods in the class, and can be injected in batches if conditions need to be matched.

  • Modify theBeanConfig@ConditionalThe value of the annotation isUserCondition, which means thatenable.flag=true.UserServiceImpl,ProductServiceImplIt’s going to be injected.
@Configuration @Conditional({UserCondition.class}) public class BeanConfig { @Bean public UserServiceImpl u(){ UserServiceImpl userService = new UserServiceImpl(); System.out.println("u = " + userService); return userService; } @Bean public ProductServiceImpl p(){ ProductServiceImpl productService = new ProductServiceImpl(); System.out.println("p = " + productService); return productService; }}Copy the code
  • Start the result

The @Conditional annotation can be passed in a Class array, that is, multiple conditions can match. For example, when a method or class is annotated, all conditions must return true before injection is performed.


  • If you have any questions or errors in this article, please feel free to comment. If you find this article helpful, please like it and follow it.