@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
//只有当所有的Condition的匹配时,才向IoC容器中注册组件
Class<? extends Condition>[] value();
}
public interface Condition {
/**
* 判断是否满足当前条件
* @param context 当前条件所处的上下文环境
* @param metadata @Conditional注解所描述的类型的元信息
* @return 返回true时,表示满足条件,组件可以被注册
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/25 13:39
* @Description:操作系统Service接口
*/
public interface OSService {
String showOSInfo();
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/25 13:40
* @Description:Windows操作系统实现
*/
public class WindowsOSServiceImpl implements OSService{
@Override
public String showOSInfo() {
return "Windows 操作系统";
}
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/25 13:41
* @Description:Mac操作系统实现
*/
public class MacOSServiceImpl implements OSService{
@Override
public String showOSInfo() {
return "Mac 操作系统";
}
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/25 13:44
* @Description:Windows操作系统Condition
*/
public class WindowsCondition implements Condition{
private static final String WINDOWS_OS_NAME = "windows";
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
return (!StringUtils.isEmpty(osName) && osName.contains(WINDOWS_OS_NAME));
}
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/25 13:48
* @Description:Mac操作系统Condition
*/
public class MacCondition implements Condition{
private static final String MAC_OS_NAME = "Mac";
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
return (!StringUtils.isEmpty(osName) && osName.contains(MAC_OS_NAME));
}
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/21 10:15
* @Description:Spring配置类
*/
@Configuration
@ComponentScan
public class MainConfig {
@Bean
@Conditional(MacCondition.class)
public OSService macOSService(){
return new MacOSServiceImpl();
}
@Bean
@Conditional(WindowsCondition.class)
public OSService windowsOSService(){
return new WindowsOSServiceImpl();
}
}
/**
* @Auther: ZhangShenao
* @Date: 2018/9/21 10:17
* @Description:
*/
public class AnnotationMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Map<String, OSService> osServices = applicationContext.getBeansOfType(OSService.class);
osServices.forEach((s, osService) -> System.err.println(osService));
}
}
在Condition接口的matches()方法加断点,可以追溯到ConfigurationClassBeanDefinitionReader类的loadBeanDefinitionsForBeanMethod方法中,该方法会根据配置类的标记了@Bean注解的方法,向容器中注入Bean,方法定义如下:
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
// Consider name and any aliases
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
String beanName = (names.size() > 0 ? names.remove(0) : methodName);
// Register aliases even when overridden
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// Has this effectively been overridden before (e.g. via XML)?
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
return;
}
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
if (metadata.isStatic()) {
// static @Bean method
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
beanDef.setFactoryMethodName(methodName);
}
else {
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.getString("destroyMethod");
if (destroyMethodName != null) {
beanDef.setDestroyMethodName(destroyMethodName);
}
// Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
beanDef.setScope(attributes.getAliasedString("value", Scope.class, configClass.getResource()));
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
// Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
该方法会扫描所有@Configuration配置类的标记了@Bean的注解的方法,将方法封装成一个BeanMethod,向容器中注入。在处理前,会调用ConditionEvaluator的shouldSkip()方法判断当前Bean是否要跳过注册。ConditionEvaluator对象是在ConfigurationClassBeanDefinitionReader构造器中实例化的,其主要作用就是处理@Conditional注解的相关逻辑。
在shouldSkip()方法中,会@Bean所在方法或者类上的@Conditional注解,并获取@Conditional注解的所有Condition条件对象,依次调用matcher()方法。只要有一个Condition匹配不成功,就跳过该Bean的注册。具体逻辑如下:
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
//phase为ConfigurationPhase.REGISTER_BEAN注册Bean阶段
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
//获取该@Bean方法上所有的Condition对象,包括方法上定义的和类上定义的
List<Condition> conditions = new ArrayList<Condition>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
//依次调用所有Condition对象的matches()方法,只要有一个匹配失败,就跳过该Bean的注册
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(this.context, metadata)) {
return true;
}
}
}
@Conditional注解在SpringBoot框架中得到了广泛的使用,SpringBoot定义了大量的基于@Conditional注解的衍生注解,并通过这些注解控制Bean的注册。下面以常用的@ConditionalOnBean注解为例,进行简单的介绍。
@ConditionalOnBean是SpringBoot定义的一个常用的条件注解,含义是只有当IoC容器中已经存在指定Class的实例时,才满足条件。源码如下:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
/**
* The class type of bean that should be checked. The condition matches when all of
* the classes specified are contained in the {@link ApplicationContext}.
* @return the class types of beans to check
*/
Class<?>[] value() default {};
/**
* The class type names of bean that should be checked. The condition matches when all
* of the classes specified are contained in the {@link ApplicationContext}.
* @return the class type names of beans to check
*/
String[] type() default {};
/**
* The annotation type decorating a bean that should be checked. The condition matches
* when all of the annotations specified are defined on beans in the
* {@link ApplicationContext}.
* @return the class-level annotation types to check
*/
Class<? extends Annotation>[] annotation() default {};
/**
* The names of beans to check. The condition matches when all of the bean names
* specified are contained in the {@link ApplicationContext}.
* @return the name of beans to check
*/
String[] name() default {};
/**
* Strategy to decide if the application context hierarchy (parent contexts) should be
* considered.
* @return the search strategy
*/
SearchStrategy search() default SearchStrategy.ALL;
}
可以看到,@ConditionalOnBean注解使用了@Conditional,并指定了条件OnBeanCondition。
OnBeanCondition继承自SpringBootCondition,SpringBootCondition是SpringBoot定义的Condition的父类,其核心逻辑是通过getMatchOutcome()方法拿到匹配条件,并进行判断。getMatchOutcome()是一个模板方法,交给子类去实现。