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. @Conditional
annotations
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