The first few basic blog posts on beans have focused on the definition and use of beans, but are there actual scenarios where I don’t load my defined beans, or only load them if certain conditions are met?

This post will focus on the use of the Conditional annotation @Conditional in bean loading

I. @Conditionalannotations

This annotation, introduced in Spring4, is used to determine whether the condition is met and therefore whether to initialize and register the Bean with the container

Definition 1.

The @Conditional annotation is defined as follows, which internally uses the Condition interface to determine whether conditions are met and therefore whether the Bean needs to be loaded

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}
Copy the code

The following is the definition of the Condtion interface. This can be said to be the most basic entry. All other conditional annotations, after all, are extended by implementing this interface

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
Copy the code

ConditionContext (ConditionContext) ConditionContext (ConditionContext) ConditionContext (ConditionContext) ConditionContext (ConditionContext) ConditionContext (ConditionContext) ConditionContext (ConditionContext) ConditionContext (ConditionContext) ConditionContext

public interface ConditionContext {
    // Get the Bean definition
    BeanDefinitionRegistry getRegistry(a);

    // Get the Bean project, so you can get all the beans in the container
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory(a);

    // environment holds all configuration information
    Environment getEnvironment(a);
    
    // Resource information
    ResourceLoader getResourceLoader(a);

    // Class loading information
    @Nullable
    ClassLoader getClassLoader(a);
}
Copy the code

2. Instructions

How to use Condition and @Conditional annotations to implement bean Conditional loading

First we define a randomly generated data class, its function is to randomly generate some data

public class RandDataComponent<T> {
    private Supplier<T> rand;

    public RandDataComponent(Supplier<T> rand) {
        this.rand = rand;
    }

    public T rand(a) {
        returnrand.get(); }}Copy the code

We currently provide two types of beans for random data generation, but it is up to the configuration to choose which one is selected, so we define the bean as follows

@Configuration
public class ConditionalAutoConfig {

    @Bean
    @Conditional(RandIntCondition.class)
    public RandDataComponent<Integer> randIntComponent(a) {
        return new RandDataComponent<>(() -> {
            Random random = new Random();
            return random.nextInt(1024);
        });
    }

    @Bean
    @Conditional(RandBooleanCondition.class)
    public RandDataComponent<Boolean> randBooleanComponent(a) {
        return new RandDataComponent<>(() -> {
            Random random = new Random();
            returnrandom.nextBoolean(); }); }}Copy the code

The above configuration, regardless of the content of the @Conditional annotation, only look at the definition of two beans, one is to define int random number generation; One is to define a Boolean to be generated randomly;

However, in our system, only one random data generator is needed. We choose which one to use according to the value of conditional.rand. Type configuration, which is as follows

# int selects randomly generated int data; Non-int means that Boolean data conditional.rand. Type =int is generated randomlyCopy the code

Then have to see how this Condition added, which is above the content of the two annotations in the configuration class ConditionalAutoConfig, two classes are to realize the Condition of interface, specific as follows

public class RandBooleanCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
        return "boolean".equalsIgnoreCase(type); }}public class RandIntCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
        return "int".equalsIgnoreCase(type); }}Copy the code

The above implementation is also a little clearer, get the configuration value, then judge, and return true/fase; Return true to indicate that the condition is met and the Bean can be loaded; Otherwise, the Bean will not be created

3. Test and validation

For the above configuration and implementation, write a test class as follows

@RestController
@RequestMapping(path = "/conditional")
public class ConditionalRest {

    @Autowired
    private RandDataComponent randDataComponent;

    @GetMapping(path = "/show")
    public String show(a) {
        String type = environment.getProperty("conditional.rand.type");
        return randDataComponent.rand() + " >>> "+ type; }}Copy the code

When the value of the configuration file is int, each access should return a positive integer, as illustrated in the following figure

After changing the configured value to Boolean, test again as shown below

II. Expansion and summary

The above test demonstrates the configuration file for selecting an injected Bean. If a Bean is loaded through an automatic scan, can annotations be added directly to the Bean’s class to determine whether it is loaded or not?

1. Conditional loading of automatic scan beans

In terms of usage, it’s the same as before, except for putting annotations on a specific class. Again, to give an example, define a bean first. Okay

@Component
@Conditional(ScanDemoCondition.class)
public class ScanDemoBean {

    @Value("${conditional.demo.load}")
    private boolean load;

    public boolean getLoad(a) {
        returnload; }}Copy the code

The corresponding judgment conditions are as follows. The configuration is loaded only when conditional.demo. Load in the configuration file is true

public class ScanDemoCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return "true".equalsIgnoreCase(conditionContext.getEnvironment().getProperty("conditional.demo.load")); }}Copy the code

The test class is the same as before, but with a little attention to automatic injection, change the necessary conditions to avoid bean error

@Autowired(required = false)
private ScanDemoBean scanDemoBean;

@GetMapping(path = "/scan")
public String showDemo(a) {
    String type = environment.getProperty("conditional.demo.load");
    if (scanDemoBean == null) {
        return "not exists! > > >" + type;
    } else {
        return "load : " + scanDemoBean.getLoad() + "> > >"+ type; }}Copy the code

When configured to true, the bean should exist, following the else logic above

When configured to false, the bean is not loaded, following the if logic

2. Summary

The @Conditional annotation is used in conjunction with the Condition interface to decide whether to create and register a bean in the Spring container, thus enabling selective loading of beans

A. advantages

What is the purpose of this?

  • When there are multiple beans with the same name, how to choose the problem
  • Resolve cases where some beans are created with other dependent conditions

B. More notes

The above can control the creation of beans, but through the above process, you will find that a little tedious. Is there any way to simplify the above process?

Instead of implementing the Condtion interface yourself, the Spring framework provides a series of annotations, as shown in the table below

annotations instructions
@ConditionalOnSingleCandidate Returns true when the bean of the given type exists and the given type specified as Primary exists
@ConditionalOnMissingBean Returns true if the given type, class name, annotation, nickname does not exist in a beanFactory. The relationship between the types is OR
@ConditionalOnBean Instead, require the bean to exist
@ConditionalOnMissingClass Returns true if the given class name does not exist on the classpath, and the relationship between types is and
@ConditionalOnClass Contrary to the above, the existence of the class is required
@ConditionalOnCloudPlatform Returns true if the Configured CloudPlatform is enabled
@ConditionalOnExpression The SPEL expression executes true
@ConditionalOnJava Whether the Java version number of the runtime contains the given version number. If contains, returns a match, otherwise, returns no match
@ConditionalOnProperty Attribute matching conditions must be configured
@ConditionalOnJndi A Location for a given JNDI must exist. Otherwise, a mismatch is returned
@ConditionalOnNotWebApplication The Web environment does not exist
@ConditionalOnWebApplication When the Web environment exists
@ConditionalOnResource Resources required for enactment exist

III. The other

Related 0.

A. More blog posts

Based on article

  • SpringBoot – Basic definition and use of beans
  • 181012- Automatic loading of SpringBoot Base beans
  • 181013- Dynamic registration of SpringBoot Base beans
  • 181018-SpringBoot Foundation Bean conditional injection @condition using posture
  • ConditionalOnBean and @conditionalonClass
  • 181019-SpringBoot Foundation Bean conditional injection @conditionalonProperty
  • 181019-SpringBoot Foundation Bean conditional Injection @conditionalonExpression

Application of article

  • 181017-SpringBoot Application Bean logout and dynamic registration implementation service mock

B. Project source code

  • Engineering: the spring – the boot – demo
  • module: 007-conditionbean

1. An ashy Blog

  • A grey Blog Personal Blog blog.hhui.top
  • A Grey Blog-Spring feature Blog Spring.hhui.top

A gray personal blog, recording all the study and work in the blog, welcome everyone to go to stroll

2. Statement

As far as the letter is not as good, the above content is purely one’s opinion, due to the limited personal ability, it is inevitable that there are omissions and mistakes, if you find bugs or have better suggestions, welcome criticism and correction, don’t hesitate to appreciate

3. Scan attention

A gray blog