@Conditional条件注入
不使用条件注入前
package com.xiong.config;
import com.xiong.bean.Person;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
@Configuration
public class PersonConfig {
@Bean(value = "linus")
Person linus() {
return new Person("linus", 50);
}
@Bean(value = "bill")
Person bill() {
return new Person("bill gate", 58);
}
}
package com.xiong;
import com.xiong.bean.Person;
import com.xiong.config.PersonConfig;
import com.xiong.service.PersonService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest {
@Test
public void noConditionalBeanTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig.class);
System.out.println("====================bean对象调用前后=======================");
String[] persons = context.getBeanNamesForType(Person.class);
for (String person : persons) {
System.out.println(person);
}
}
}

使用条件注入后
根据操作系统类型进行条件注入: 在Linux系统中注入linus, 在Windows系统中注入bill
- 自定义条件判断类XXXCondition实现Condition接口
- 满足条件返回true, 否则返回false
- 在@Bean上面使用@Conditional(XXXCondition.class)来使用自定义条件判断类
自定义条件判断类
package com.xiong.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Locale;
/**
* 用来判断当前系统是否是Linux系统
*/
public class LinuxCondition implements Condition {
/**
* @param context 表示上下文环境, 可以获取需要的信息
* @param metadata 标注了@Conditional注解的类的元数据
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取环境变量
Environment environment = context.getEnvironment();
//获取操作系统名称
String osType = environment.getProperty("os.name");
return osType != null && osType.toLowerCase(Locale.ROOT).contains("linux");
}
}
package com.xiong.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Locale;
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取环境变量
Environment environment = context.getEnvironment();
//获取操作系统名称
String osType = environment.getProperty("os.name");
//获取保存所有bean对象的registry
BeanDefinitionRegistry registry = context.getRegistry();
boolean hasPerson = registry.containsBeanDefinition("person");
return osType != null && osType.toLowerCase(Locale.ROOT).contains("windows");
}
}
使用条件判断类
package com.xiong.config;
import com.xiong.bean.Person;
import com.xiong.condition.LinuxCondition;
import com.xiong.condition.WindowsCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersonConfig {
@Conditional(LinuxCondition.class)
@Bean(value = "linus")
Person linus() {
return new Person("linus", 50);
}
@Conditional(WindowsCondition.class)
@Bean(value = "bill")
Person bill() {
return new Person("bill gate", 58);
}
}
测试类
package com.xiong;
import com.xiong.bean.Person;
import com.xiong.config.PersonConfig;
import com.xiong.service.PersonService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest {
@Test
public void conditionalBeanTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig.class);
System.out.println("====================bean对象调用前后=======================");
String[] persons = context.getBeanNamesForType(Person.class);
for (String person : persons) {
System.out.println(person);
}
}
}
实验结果

拓展学习
在使用自定义条件判断类实现Condition接口后, match()方法中有两个类型参数, 学习了解ConditionContext和AnnotatedTypeMetadata可以更好地帮助实现各种需求的条件判断类
ConditionContext 和 AnnotatedTypeMetadata类
ConditionContext 使用的实现类是 ConditionEvaluator$ConditionContextImpl(即 ConditionEvaluator 中的内部类 ConditionContextImpl)。但是由于ConditionEvaluator 是一个包级别的私有类(缺省修饰),因此通过 Class.forName() 反射调用的方式来对其进行测试。其内部包含 5 个字段:
BeanDefinitionRegistry
实际上仅需要BeanDefinitionRegistry,其本身就是一个BeanFactory 或者 ApplicationContext,下面的配置在默认情况下都由BeanDefinitionRegistry导出生成。如果这里实际是一个 ApplicationContext,那么其实现了 Resource、Environment 等接口,可以直接获取其中的 Resource、Environment 等对象。但如果这里是一个 BeanFactory,同时在外部没有传值的情况下,那么就使用这些接口的一个标准的或默认的实现。
ConfigurableListableBeanFactory
Environment
ResourceLoader:加载类路径下的资源文件
ClassLoader:
AnnotatedTypeMetadata 使用的实现类是 SimpleMethodMetadata,表示方法的元数据信息。在该案例中,用于获取 @Conditional 所标注的方法的元数据。而MethodMetadata 是 AnnotatedTypeMetadata 的子接口,其中包含的信息有:
- 方法名(methodName)
- 返回值类型名(returnTypeName)
- 所在的类名(declaringClassName)
- 方法上包含的注解(annotations)
- 是否使用final修饰、是否静态、是否重载、是否是抽象方法等标识位
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import java.lang.reflect.Constructor;
@SpringBootTest
public class ConditionalAnnotationTest {
// GenericWebApplicationContext
@Autowired
private ApplicationContext context;
@Test
public void conditionalAnnotationTest() throws Exception {
// 加载内部类使用 "$"
Class<?> clazz = Class.forName("org.springframework.context.annotation.ConditionEvaluator$ConditionContextImpl");
Class<?> enclosingClass = clazz.getEnclosingClass();
System.out.println(enclosingClass.getName());
Constructor<?> constructor = clazz.getDeclaredConstructor(BeanDefinitionRegistry.class, Environment.class, ResourceLoader.class);
constructor.setAccessible(true);
ConditionContext conditionContext = (ConditionContext) constructor.newInstance(context, null, null);
ResourceLoader resourceLoader = conditionContext.getResourceLoader();
Resource resource = resourceLoader.getResource("classpath:application.yml");
String absolutePath = resource.getFile().getAbsolutePath();
System.out.println("absolutePath = " + absolutePath);
}
}