source-code-hunter/docs/SpringBoot/SpringBoot-ConditionalOnBea...

722 lines
20 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# SpringBoot ConditionalOnBean
- Author: [HuiFer](https://github.com/huifer)
- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read)
- 在 SpringBoot 中有下列当XXX存在或不存的时候执行初始化
- ConditionalOnBean
ConditionalOnClass
ConditionalOnCloudPlatform
ConditionalOnExpression
ConditionalOnJava
ConditionalOnJndi
ConditionalOnMissingBean
ConditionalOnMissingClass
ConditionalOnNotWebApplication
ConditionalOnProperty
ConditionalOnResource
ConditionalOnSingleCandidate
ConditionalOnWebApplication
## ConditionalOnBean
```java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
/**
* 需要匹配的 bean 类型
*/
Class<?>[] value() default {};
/**
* 需要匹配的 bean 类型
*/
String[] type() default {};
/**
* 匹配的 bean 注解
*/
Class<? extends Annotation>[] annotation() default {};
/**
* 需要匹配的 beanName
*/
String[] name() default {};
/**
* 搜索策略
*/
SearchStrategy search() default SearchStrategy.ALL;
/**
*/
Class<?>[] parameterizedContainer() default {};
}
```
## SearchStrategy
```java
public enum SearchStrategy {
/**
* 当前 上下文
*/
CURRENT,
/**
* 找所有的父容器
*/
ANCESTORS,
/**
* 当前上下文+父容器
*/
ALL
}
```
## OnBeanCondition
- org.springframework.boot.autoconfigure.condition.OnBeanCondition
- 这个类是一个条件类,相关的还有
```properties
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
```
- 类图
![image-20200824085726621](//images/SpringBoot//SpringBoot/image-20200824085726621.png)
在看这部分源码之前需要先了解 `Conditional`和`Condition`的源码
- 简单描述
通过实现`Condition` 来确认是否初始化bean
- 从类图上我们可以看到 `condition` 的继承关系. 在这里需要去找到`SpringBootCondition`
- `org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)`
```java
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 类名或者方法名标记
String classOrMethodName = getClassOrMethodName(metadata);
try {
// 比较类,子类实现
ConditionOutcome outcome = getMatchOutcome(context, metadata);
// 日志输出
logOutcome(classOrMethodName, outcome);
// 报告记录
recordEvaluation(context, classOrMethodName, outcome);
// 返回匹配结果
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "
+ "that class. This can also happen if you are "
+ "@ComponentScanning a springframework package (e.g. if you "
+ "put a @ComponentScan in the default package by mistake)", ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}
```
- `getOutcomes` 子类实现
`org.springframework.boot.autoconfigure.condition.OnBeanCondition#getOutcomes`
```java
String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata
```
- 第一个参数: 需要自动配置的类
- 配置注解信息
### ConditionOutcome 和 ConditionMessage
```java
public class ConditionOutcome {
/**
* 是否匹配
*/
private final boolean match;
/**
* 条件信息
*/
private final ConditionMessage message;
}
public final class ConditionMessage {
private String message;
}
```
- 造一个对象用来进行debug
```java
@Component
public class Beans {
@Bean
public A a() {
return new A();
}
@Bean
@ConditionalOnBean(value = A.class)
public B b() {
return new B();
}
}
```
## getMatchOutcome
```java
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 条件信息
ConditionMessage matchMessage = ConditionMessage.empty();
// 获取注解求和
MergedAnnotations annotations = metadata.getAnnotations();
// 注解是否匹配
if (annotations.isPresent(ConditionalOnBean.class)) {
// 搜索 ConditionalOnBean 注解
Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations,
ConditionalOnBean.class);
// 匹配结果
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
// 把注解解析出来获得文本
matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
}
else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
spec.getStrategy() == SearchStrategy.ALL)) {
return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
}
matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
ConditionalOnMissingBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (matchResult.isAnyMatched()) {
String reason = createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}
```
- 开始方法分析
### getMatchingBeans
- `org.springframework.boot.autoconfigure.condition.OnBeanCondition#getMatchingBeans`
```java
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
// 获取上下文
ClassLoader classLoader = context.getClassLoader();
// 获取 IOC 容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 扫描方式比较是否为当前上下文
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
"Unable to use SearchStrategy.ANCESTORS");
beanFactory = (ConfigurableListableBeanFactory) parent;
}
// 结果对象初始化
MatchResult result = new MatchResult();
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
spec.getIgnoredTypes(), parameterizedContainers);
for (String type : spec.getTypes()) {
// 通过类型获取 beanName
Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
parameterizedContainers);
typeMatches.removeAll(beansIgnoredByType);
if (typeMatches.isEmpty()) {
result.recordUnmatchedType(type);
}
else {
result.recordMatchedType(type, typeMatches);
}
}
for (String annotation : spec.getAnnotations()) {
Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
considerHierarchy);
annotationMatches.removeAll(beansIgnoredByType);
if (annotationMatches.isEmpty()) {
result.recordUnmatchedAnnotation(annotation);
}
else {
result.recordMatchedAnnotation(annotation, annotationMatches);
}
}
for (String beanName : spec.getNames()) {
if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
result.recordMatchedName(beanName);
}
else {
result.recordUnmatchedName(beanName);
}
}
return result;
}
```
- 在`MatchResult result = new MatchResult()` 之前的代码作用是确认ioc容器
#### getNamesOfBeansIgnoredByType
```java
/**
* 获取忽略的beans(返回对象是 beanName)
* 循环,忽略的类型, 将类型从 beanFactory 获取,返回
*/
private Set<String> getNamesOfBeansIgnoredByType(ClassLoader classLoader, ListableBeanFactory beanFactory,
boolean considerHierarchy, Set<String> ignoredTypes, Set<Class<?>> parameterizedContainers) {
Set<String> result = null;
for (String ignoredType : ignoredTypes) {
// 从 beanFactory 中获取忽略的beanNames
Collection<String> ignoredNames = getBeanNamesForType(classLoader, considerHierarchy, beanFactory,
ignoredType, parameterizedContainers);
result = addAll(result, ignoredNames);
}
return (result != null) ? result : Collections.emptySet();
}
```
#### getBeanNamesForType
```java
/**
* 通过类型获取 beanName
*/
private Set<String> getBeanNamesForType(ClassLoader classLoader, boolean considerHierarchy,
ListableBeanFactory beanFactory, String type, Set<Class<?>> parameterizedContainers) throws LinkageError {
try {
// 从beanFactory 中获取忽略的类 返回beanNanme
return getBeanNamesForType(beanFactory, considerHierarchy, resolve(type, classLoader),
parameterizedContainers);
}
catch (ClassNotFoundException | NoClassDefFoundError ex) {
return Collections.emptySet();
}
}
```
#### getBeanNamesForType
```java
/**
* 通过类型获取 beanName
*/
private Set<String> getBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy, Class<?> type,
Set<Class<?>> parameterizedContainers) {
// 获取beanName
Set<String> result = collectBeanNamesForType(beanFactory, considerHierarchy, type, parameterizedContainers,
null);
return (result != null) ? result : Collections.emptySet();
}
```
#### collectBeanNamesForType
- 这里最终回到了spring beanFactory 的方法 getBeanNamesForType
```java
private Set<String> collectBeanNamesForType(ListableBeanFactory beanFactory, boolean considerHierarchy,
Class<?> type, Set<Class<?>> parameterizedContainers, Set<String> result) {
result = addAll(result, beanFactory.getBeanNamesForType(type, true, false));
for (Class<?> container : parameterizedContainers) {
ResolvableType generic = ResolvableType.forClassWithGenerics(container, type);
result = addAll(result, beanFactory.getBeanNamesForType(generic, true, false));
}
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
if (parent instanceof ListableBeanFactory) {
result = collectBeanNamesForType((ListableBeanFactory) parent, considerHierarchy, type,
parameterizedContainers, result);
}
}
return result;
}
```
到这里需要忽略的beanName 就全部找出来了
```java
// 匹配类型在移除
for (String type : spec.getTypes()) {
// 通过类型获取 beanName
Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
parameterizedContainers);
typeMatches.removeAll(beansIgnoredByType);
if (typeMatches.isEmpty()) {
result.recordUnmatchedType(type);
}
else {
result.recordMatchedType(type, typeMatches);
}
}
// 注解匹配删除忽略的beanname
for (String annotation : spec.getAnnotations()) {
Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
considerHierarchy);
annotationMatches.removeAll(beansIgnoredByType);
if (annotationMatches.isEmpty()) {
result.recordUnmatchedAnnotation(annotation);
}
else {
result.recordMatchedAnnotation(annotation, annotationMatches);
}
}
```
- 在忽略bean找到之后做一个类型移除的操作.
![image-20200825140750035](/images/SpringBoot//image-20200825140750035.png)
### 返回值
- 在返回之前做一堆判断条件. 一旦符合条件这个地方会做一个noMatch的一个对象(`ConditionOutcome`) 通过返回match对象`ConditionOutcome`
```java
public static ConditionOutcome noMatch(ConditionMessage message) {
return new ConditionOutcome(false, message);
}
```
```java
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
// 把注解解析出来获得文本
matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
}
else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
spec.getStrategy() == SearchStrategy.ALL)) {
return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
}
matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
ConditionalOnMissingBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (matchResult.isAnyMatched()) {
String reason = createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
```
![image-20200825141506531](/images/SpringBoot//image-20200825141506531.png)
- 到此结果封装完毕.回到方法`org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)` 继续进行
- 再往后就继续执行spring的bean初始化咯
## MessageSourceAutoConfiguration
- 启动阶段的一个类运行解读
- `org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration`
```java
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {}
```
- 根据类的注解信息我们可以找到有`ResourceBundleCondition`
![image-20200825092343271](/images/SpringBoot//image-20200825092343271.png)
- 获取类名或者方法名的结果是`MessageSourceAutoConfiguration`全路径
- 继续往下是一个比较的方法(是否符合match)
`org.springframework.boot.autoconfigure.condition.SpringBootCondition#getMatchOutcome`这个方法是一个抽象方法子类实现
- 上图中红框内标注的类为`org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition`
同时继承`org.springframework.boot.autoconfigure.condition.SpringBootCondition`
并且重写了方法`getMatchOutcome`
```java
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 从 容器中获取
String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
// 从缓存中获取条件信息
ConditionOutcome outcome = cache.get(basename);
if (outcome == null) {
// 生成条件信息对象
outcome = getMatchOutcomeForBasename(context, basename);
// 放入缓存
cache.put(basename, outcome);
}
return outcome;
}
```
这个方法主要将比较信息放入,
- 后续的行为依然是判断是否匹配,匹配就创建.
## Spring Boot 启动阶段的自动注入
```java
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter
```
```java
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// 获取 AutoConfigurationImportFilter 相关配置
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 执行 aware 相关接口
invokeAwareMethods(filter);
// 是否可以初始化的结果
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
// 是否跳过
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
// 处理最终需要的类
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList<>(result);
}
```
- 在这里有一个关注点 循环方法`getAutoConfigurationImportFilters()`
```JAVA
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
```
在`spring.factories`文件中找到`AutoConfigurationImportFilter`后面的值
```properties
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
```
- 此时我们可以和前文的源码分析连接起来有一个完整的认识了
![image-20200825142332485](/images/SpringBoot//image-20200825142332485.png)
- 最后来看整体类图
![image-20200825142418115](/images/SpringBoot//image-20200825142418115.png)