Spring boot 源码分析-Conditional

Spring Boot中大量的应用到@Conditional条件注解,Condition接口是spring4.0增加的条件判断接口,用于判断条件满足情况,主要集中在Bean的定义和使用。当前文章基于spring boot 1.5.4.RELEASE,spring-core 4.3.9.RELEASE。

目录

1. 用法演示

1.1 小结

1.2 spring Condtion拓展集

2. 源码

2.1 ConditionContext

2.2 AnnotatedBeanDefinitionReader

2.3 ClassPathScanningCandidateComponentProvider

2.4 ConfigurationClassPostProcessor

3. SpringBootCondition

总结


//源于org.springframework.context.annotation;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * All {@link Condition}s that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();
}
public interface Condition {
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

1. 用法演示

在不同系统环境中创建监控服务MonitorService,还算比较简单(spring帮我们做了很多事情)。

public class LinuxCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {        
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }
}
public class WindowsCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {    
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }    
}
public interface MonitorService {
    MonitorData ps();
}
public class LinuxService implements MonitorService {
    public MonitorData ps(){
        return ...;
    }    
}
public class WindowsService implements MonitorService {
    public String ps(){
        return ...;
    }    
}
@Configuration
public class MyConfiguration {
    @Bean(name = "monitor")
    @Conditional(WindowsCondition.class)
    public MonitorService windowsService() {
        return new WindowsService();
    }

    @Bean(name = "monitor")
    @Conditional(LinuxCondition.class)
    public MonitorService  linuxEmailerService() {
        return new LinuxService();
    }
}

当然如果你觉得再定义MyConfiguration太麻烦了,也可以用下面的写法,原理都是一样的。

@Component
@Conditional(LinuxCondition.class)
public class LinuxService implements MonitorService {
    public MonitorData ps(){
        return ...;
    }    
}

1.1 小结

通过@Conditional注解配合Condition接口,来决定给一个bean是否创建和注册到Spring容器中。这样做的好处是:

  • 当有多个同名bean时选择的问题
  • 解决某些bean的创建时依赖某些条件的场景

除此之前,还有其他的场景吗,继续向下看吧(ConfigurationClassPostProcessor)!

1.2 spring Condtion拓展集

@ConditionalOnMissingBean当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true.各类型间是or的关系
@ConditionalOnBean与上面相反,要求bean存在
@ConditionalOnMissingClass当给定的类名在类路径上不存在时返回true,各类型间是and的关系
@ConditionalOnClass与上面相反,要求类存在
@ConditionalOnProperty要求配置属性匹配条件
@ConditionalOnJava运行时的java版本号是否包含给定的版本号.如果包含,返回匹配,否则,返回不匹配
@ConditionalOnExpressionspel表达式执行为true
@ConditionalOnCloudPlatform当所配置的CloudPlatform为激活时返回true
@ConditionalOnSingleCandidate当给定类型的bean存在并且指定为Primary的给定类型存在时,返回true
@ConditionalOnJndi给定的jndi的Location 必须存在一个.否则,返回不匹配
@ConditionalOnNotWebApplicationweb环境不存在时
@ConditionalOnWebApplicationweb环境存在时
@ConditionalOnResource要求制定的资源存在

这里提问一个问题,如何像spring boot一样进行拓展而非采用上面示例呢?

2. 源码

2.1 ConditionContext

在上面我们看到过ConditionContext,它可以用来获取很多系统相关的信息,来丰富条件判断:

// 源于org.springframework.context.annotation;
public interface ConditionContext {
	BeanDefinitionRegistry getRegistry();                //Bean定义
	ConfigurableListableBeanFactory getBeanFactory();    //Bean容器
	Environment getEnvironment();        //所有的配置信息
	ResourceLoader getResourceLoader();//资源信息
	ClassLoader getClassLoader();
}
// 实现不展开讲
class ConditionEvaluator {
	private static class ConditionContextImpl implements ConditionContext {
    }
}

2.2 AnnotatedBeanDefinitionReader

AnnotatedBeanDefinitionReader管理ConditionContext,你可能不熟悉,不急!AnnotationConfigApplicationContext (这个总知道吧)是spring 3.0以后引入的类,用于处理spring注解。

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);//就是它
		this.scanner = new ClassPathBeanDefinitionScanner(this);//还有它
	}
}
public class AnnotatedBeanDefinitionReader {
	private ConditionEvaluator conditionEvaluator;
	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
                //实例化ConditionContext   
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}
}
//不知道AnnotationConfigApplicationContext 是啥的看这里吧
public static void main(String[] args) {
    ApplicationContext context = newAnnotationConfigApplicationContext(Application.class);
    MessagePrinter printer =context.getBean(MessagePrinter.class);
    printer.printMessage();
}

到这里Condition来自哪里的问题基本上已经解决了,下一步就是看,它是如何调用的?

AnnotatedBeanDefinitionReader.registerBean

在注册bean的时候会拿Condition判断是否这个是一个满足条件应该注册的bean,看代码

 /*
 * Register a bean from the given bean class, deriving its metadata from class-declared annotations.
 */
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
	//注解判断  true就不注册这个class了
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
		return;
	}

	ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
	abd.setScope(scopeMetadata.getScopeName());
	String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
	if (qualifiers != null) {
		for (Class<? extends Annotation> qualifier : qualifiers) {
			if (Primary.class == qualifier) {
				abd.setPrimary(true);
			}
			else if (Lazy.class == qualifier) {
				abd.setLazyInit(true);
			}
			else {
				abd.addQualifier(new AutowireCandidateQualifier(qualifier));
			}
		}
	}

	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
	//创建ScopedProxyMode代理
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

2.3 ClassPathScanningCandidateComponentProvider

AnnotationConfigApplicationContext 的scan(ClassPathBeanDefinitionScanner),

//从类路径中扫描component
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
		for (String basePackage : basePackages) {
                        //看这里,扫描
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
	private boolean isConditionMatch(MetadataReader metadataReader) {
		if (this.conditionEvaluator == null) {
                //实例化ConditionContext
		this.conditionEvaluator = new ConditionEvaluator(getRegistry(), getEnvironment(), getResourceLoader());
		}
                //注解校验
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                                                //满足是一个组件的所有要求
						if (isCandidateComponent(metadataReader)) {
                                                        //ScannedGenericBeanDefinition其实就是一个带有注解信息,没有任何构造函数参数等等的GenericBeanDefinition
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
                                                    //判断是否给定的beandefinition是一个具体的类,不是接口或者抽象类
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}
	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}
}

2.4 ConfigurationClassPostProcessor

处理上面的用法外,Spring按照Configuration类的方式进行配置的时候,会加载解析Configuration组件类,加载的时候会调用Condition的子类ConfigurationCondition去判断是否需要解析Configuration或者是否注册Bean(ConfigurationClassPostProcessor)。

public interface ConfigurationCondition extends Condition {
	ConfigurationPhase getConfigurationPhase();
	enum ConfigurationPhase {
		PARSE_CONFIGURATION,
		REGISTER_BEAN
	}
}

首先还是从入口开始,ConfigurationClassPostProcessor的postProcessBeanFactory,首先调用ConfigurationClassParser 的parse方法,再调用ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法完成依赖bean的判决及注入。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    //就是它
    private ConfigurationClassBeanDefinitionReader reader;
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
          if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then.
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}        
    }
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        // Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            parser.parse(candidates);
			parser.validate();
        // Read the model and create bean definitions based on its content
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
		this.reader.loadBeanDefinitions(configClasses);
    }
}
class ConfigurationClassBeanDefinitionReader {
	private final ConditionEvaluator conditionEvaluator;
	/**
	 * Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
	 * to populate the given {@link BeanDefinitionRegistry}.
	 */
	ConfigurationClassBeanDefinitionReader(BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
			ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator,
			ImportRegistry importRegistry) {

		this.registry = registry;
		this.sourceExtractor = sourceExtractor;
		this.resourceLoader = resourceLoader;
		this.environment = environment;
		this.importBeanNameGenerator = importBeanNameGenerator;
		this.importRegistry = importRegistry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
	}
	/**
	 * Read {@code configurationModel}, registering bean definitions
	 * with the registry based on its contents.
	 */
	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    }
}
class ConfigurationClassParser {
	private final ConditionEvaluator conditionEvaluator;
	public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
			ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
			BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

		this.metadataReaderFactory = metadataReaderFactory;
		this.problemReporter = problemReporter;
		this.environment = environment;
		this.resourceLoader = resourceLoader;
		this.registry = registry;
		this.componentScanParser = new ComponentScanAnnotationParser(
				environment, resourceLoader, componentScanBeanNameGenerator, registry);
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
	}
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		processDeferredImportSelectors();
	}
	protected final void parse(Class<?> clazz, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(clazz, beanName));
	}
	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
	}
}

3. SpringBootCondition

spring boot提供了SpringBootCondition(Spring Boot项目实现Condition的基础,它提供合理的日志记录以帮助用户诊断加载的类)注解来实现更丰富的内容。使用方式也特别简单,完全可以参考spring Condtion拓展集中的@ConditionalOnJava

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)//此次
public @interface ConditionalOnClass {
	Class<?>[] value() default {};
	String[] name() default {};
}
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends SpringBootCondition
		implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
}

这时候你可能会问SpringBootCondition和ConfigurationCondition有什么区别?

ConfigurationCondition为spring框架特有的规约接口(与@Configuration一起使用时提供更细粒度控制的条件),SpringBootCondition是一个实现类,说实话没可比性,拓展的时候建议使用它。

总结

spring中使用Conditional的用法及实现原理支持两种,一种是注册bean的时候拿Condition判断,另一种是需要解析Configuration的时候拿Condition判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值