docs/Spring全家桶/SpringBoot源码解析/SpringBoot自动装配(二):条件注解.md
springboot Զװ֮ԶװһУǷ springboot META-INF/spring.factories ļжԶװ࣬صЩԶװЩе bean һʼ𣿲ǣǿڶӦ Bean ɷʹעǷгʼ
springboot ṩע£
оٲ£
| ע | ע | ˵ |
|---|---|---|
| class ע | @ConditionalOnClass/@ConditionalOnMissingClass | ָ** / ȱʧ**ʱʼ bean |
| bean ע | @ConditionalOnBean/@ConditionalOnMissingBean | ָ bean ** / ȱʧ**ʱʼ bean |
| ע | @ConditionalOnProperty | ָԴڳʼ bean |
| Resource ע | @ConditionalOnResource | ָԴڳʼ bean |
| Web Ӧע | @ConditionalOnWebApplication / @ConditionalOnNotWebApplication | ǰӦΪ / Ϊ web Ӧʱʼ bean |
| spring ʽע | @ConditionalOnExpression | ʽΪ true ʱʼ bean |
ǽ @ConditionalOnClass עݣ
...
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
...
}
ԿConditionalOnClass @Conditional עĹܣ OnClassCondition.class
@Conditional עԲο ConfigurationClassPostProcessor ֮ @Conditional עֱ˵ @Conditional ʹ÷ʽ
@Conditional spring ע⣻
@Conditional ṩһ valueΪ Class Condition ࣺ
Class<? extends Condition>[] value();
Condition һӿڣһ matches(...)
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
ֻ matches(...) true ʱConfigurationClassPostProcessor ŻὫӦ bean עᵽ beanFactory beanDefinitionMap .
ܽ @Conditional ʹ÷ʽǾˣOnClassCondition.class Condition ࣬ matches(...) ҪͬעĴʽҲƣܽעжࣺ
| ע | ע | ж |
|---|---|---|
| class ע | @ConditionalOnClass/@ConditionalOnMissingClass | OnClassCondition |
| bean ע | @ConditionalOnBean/@ConditionalOnMissingBean | OnBeanCondition |
| ע | @ConditionalOnProperty | OnPropertyCondition |
| Resource ע | @ConditionalOnResource | OnResourceCondition |
| Web Ӧע | @ConditionalOnWebApplication / @ConditionalOnNotWebApplication | OnWebApplicationCondition |
| spring ʽע | @ConditionalOnExpression | OnExpressionCondition |
ĿľͺȷˣҪЩעжֻҪӦж matches(...) Ϳˡ
SpringBootCondition#matchesOnClassCondition#matches SpringBootConditionط£
public abstract class SpringBootCondition implements Condition {
private final Log logger = LogFactory.getLog(getClass());
@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);
// ﷵսtrue false
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException(...);
}
catch (RuntimeException ex) {
throw new IllegalStateException(...);
}
}
/**
* Ǹʽʵ
*/
public abstract ConditionOutcome getMatchOutcome(
ConditionContext context, AnnotatedTypeMetadata metadata);
...
}
SpringBootCondition matches(...) ؼУ
...
ConditionOutcome outcome = getMatchOutcome(context, metadata);
...
return outcome.isMatch();
SpringBootCondition getMatchOutcome(...) ǸṩOnClassCondition ʵ֮һʵϣж SpringBootCondition ࣬Ǿֱӽ getMatchOutcome(...) ˡ
getMatchOutcome(...) صĽ ConditionOutcome ConditionOutcome Ǹɶ
public class ConditionOutcome {
private final boolean match;
private final ConditionMessage message;
/**
* 췽
*/
public ConditionOutcome(boolean match, String message) {
this(match, ConditionMessage.of(message));
}
/**
* 췽
*/
public ConditionOutcome(boolean match, ConditionMessage message) {
Assert.notNull(message, "ConditionMessage must not be null");
this.match = match;
this.message = message;
}
/**
* ƥĽ
*/
public boolean isMatch() {
return this.match;
}
...
}
ӴװȽϽģڲԣmatch message:
match booleanƥɹʧܵıʶmessage ConditionMessageʾƥ˵ConditionMessage:
public final class ConditionMessage {
private String message;
private ConditionMessage() {
this(null);
}
private ConditionMessage(String message) {
this.message = message;
}
...
}
һԣmessageǶ˵Ϣİװ
@ConditionalOnClass: OnClassCondition#getMatchOutcomeOnClassCondition ƥֱӽ getMatchOutcome
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
// 1\. @ConditionalOnClass ע
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
// 1.1 ж
List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
if (!missing.isEmpty()) {
// 1.2 ؽƥ
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes").items(Style.QUOTE, missing));
}
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes")
.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
// 2\. @ConditionalOnMissingClass ע
List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
// 2.1 ж
List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
if (!present.isEmpty()) {
// 2.2 ؽƥ
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnMissingClass.class)
.found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes")
.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
}
// ƥĽ
return ConditionOutcome.match(matchMessage);
}
ͬʱ @ConditionalOnClass @ConditionalOnMissingClass ע⣬̼ƣעж϶ͨ FilteringSpringBootCondition#filter £
protected final List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,
ClassLoader classLoader) {
if (CollectionUtils.isEmpty(classNames)) {
return Collections.emptyList();
}
List<String> matches = new ArrayList<>(classNames.size());
for (String candidate : classNames) {
// ƥ
if (classNameFilter.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}
ɴ˿ɼ classNameFilter ˹ؼ
@ConditionalOnClass ʱclassNameFilter Ϊ ClassNameFilter.MISSING@ConditionalOnMissingClass ʱclassNameFilter Ϊ ClassNameFilter.PRESENTǽ ClassNameFilter һ̽ FilteringSpringBootCondition ࣬£
abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
...
/**
* classLoader ڣ ClassLoader#loadClass
* Class#forName
*/
protected static Class<?> resolve(String className, ClassLoader classLoader)
throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}
/**
* ƥ
*/
protected enum ClassNameFilter {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
abstract boolean matches(String className, ClassLoader classLoader);
/**
* Class Ƿ
* ͨʱ쳣жǷڣδ׳쳣ʾ
*/
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
// ͨ쳣жǷڸclass
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
}
...
}
Ǿˣж Class Ƿڣspring ͨ ClassLoader.load(String) Class.forName(String) 쳣ģ׳쳣ͱ Class ڡ
ܽ @ConditionalOnClass/@ConditionalOnMissingClass Ĵʽ**ߵĴΪ OnClassConditionͨ ClassLoader.load(String) Class.forName(String) 쳣ж Class Ƿڣ׳쳣ͱ Class **
@ConditionalOnBean: OnBeanCondition#getMatchOutcome@ConditionalOnBean ֱӽ OnBeanCondition#getMatchOutcome
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
MergedAnnotations annotations = metadata.getAnnotations();
// @ConditionalOnBean
if (annotations.isPresent(ConditionalOnBean.class)) {
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());
}
// @ConditionalOnSingleCandidate
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());
}
// @ConditionalOnMissingBean
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);
}
Կһעƥ䣺@ConditionalOnBean``@ConditionalOnSingleCandidate @ConditionalOnMissingBean߶ͬһ getMatchingBeans(...) ȡƥȻʹ matchResult.isAllMatched() matchResult.isAnyMatched() յĽжϡ
OnBeanCondition#getMatchingBeansgetMatchingBeans(...) Ĵ£
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
ClassLoader classLoader = context.getClassLoader();
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();
// 1\. ȡ ignoreTypeֻ @ConditionalOnMissingBean
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory,
considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers);
// 2\. types
for (String type : spec.getTypes()) {
Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy,
beanFactory, type, parameterizedContainers);
typeMatches.removeAll(beansIgnoredByType);
if (typeMatches.isEmpty()) {
result.recordUnmatchedType(type);
}
else {
result.recordMatchedType(type, typeMatches);
}
}
// 3\. ϵע @ConditionalOnMissingBean
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);
}
}
// 4\. beanName
for (String beanName : spec.getNames()) {
if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName,
considerHierarchy)) {
result.recordMatchedName(beanName);
}
else {
result.recordUnmatchedName(beanName);
}
}
return result;
}
Ҫ˵ǣᴦ 3 עƥ@ConditionalOnBean``@ConditionalOnSingleCandidate @ConditionalOnMissingBean£
ignoreTypeֻ @ConditionalOnMissingBeantypes ƥ@ConditionalOnMissingBeanbeanName ƥϲľϸڣľͲչˣṩ̣
ignoreType
ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean) ȡе ignoreType beanNamebeansIgnoredByType( Set<String>)types ƥ
1. ʹ ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean) ȡе type Ӧ beanNameΪ typeMatches
2. typeMatches еֵȥ ignoreType
3. жϵڶõ typeMatchesΪգǰ Type 浽 unmatchedTypes У浽 matchedTypes namesOfAllMatchesListableBeanFactory#getBeanNamesForAnnotation ȡе annotation Ӧ beanNameΪ annotationMatchesannotationMatches еֵȥ ignoreTypeannotationMatchesΪգǰ Annotation 浽 unmatchedAnnotations У浽 matchedAnnotations namesOfAllMatchesbeanName ƥ
1. ж beansIgnoredByType Ƿ beanName
2. ʹ BeanFactory#containsBean жи beanName
3. 2 Ϊ falseڶΪ trueǰ beanName 뵽 matchedNames namesOfAllMatches浽 unmatchedNamesõ matchedTypes``unmatchedNames ݺmatchResult.isAllMatched() matchResult.isAnyMatched() յжϽжЩṹǷգ
boolean isAllMatched() {
return this.unmatchedAnnotations.isEmpty() && this.unmatchedNames.isEmpty()
&& this.unmatchedTypes.isEmpty();
}
boolean isAnyMatched() {
return (!this.matchedAnnotations.isEmpty()) || (!this.matchedNames.isEmpty())
|| (!this.matchedTypes.isEmpty());
}
@ConditionalOnBean/@ConditionalOnMissingBean Ĺؼʹ ListableBeanFactory#getBeanNamesForType BeanFactory#containsBean ж beanName``beanType Ƿˡ
ʹ @ConditionalOnBean/@ConditionalOnMissingBean ʱһҪرע⣺עִʱ spring ConfigurationClassPostProcessor еģȷе˵ڽ bean 뵽 beanFactory beanDefinitionMap ֮ǰжϵģӵ beanDefinitionMap УͲӡ͵һ⣺ @ConditionalOnBean/@ConditionalOnMissingBean bean ڸ bean ֮뵽 beanDefinitionMap УпܳУ˵
ࣺ
@Component
@ConditionalOnMissingBean("b")
public class A {
}
@Component
public class B {
}
A B @Component spring beanȻ A ע @ConditionalOnMissingBean("b") b ʱA ŽгʼЩǰᣬ
b ӵ beanDefinitionMap Уڽ a ӵ beanDefinitionMap ʱ b ѾˣǾͲˣǵԤڣa ȱʱ beanDefinitionMap вû b a ӵ beanDefinitionMap Уٴ b``b Ҳᱻӵ beanDefinitionMapһa b ͬʱ beanDefinitionMap Уնᱻʼ spring beanǵԤڲô springboot νأ @ConditionalOnBean/@ConditionalOnMissingBean ˵
£
ֻƥ䵽ĿǰΪֹӦóе bean ˣǿҽԶʹáѡ bean ҪһԶ´ȷʹôڴ֮С
ݣҵĽ£
@ConditionalOnBean/@ConditionalOnMissingBean bean 뵽 beanDefinitionMap һ̣ƥĿǰΪֹ beanDefinitionMap Ѵڵ bean֮ bean ǣпУԲοٵ a b@ConditionalOnBean/@ConditionalOnMissingBean ע⣬Ҳ˵ԶʹõĻȷƥa b a b ֱλڲͬԶУô a Ҫ b ֮ص beanDefinitionMap Уͨ @AutoConfigureAfter``@AutoConfigureBefore``@AutoConfigureOrder עָԶļ˳ɡ
ƪľȵˣƪʣµע⡣
springboot עĵڶƪܽ springboot ļܽ
| ע | ע | ж |
|---|---|---|
| class ע | @ConditionalOnClass/@ConditionalOnMissingClass | OnClassCondition |
| bean ע | @ConditionalOnBean/@ConditionalOnMissingBean | OnBeanCondition |
| ע | @ConditionalOnProperty | OnPropertyCondition |
| Resource ע | @ConditionalOnResource | OnResourceCondition |
| Web Ӧע | @ConditionalOnWebApplication / @ConditionalOnNotWebApplication | OnWebApplicationCondition |
| spring ʽע | @ConditionalOnExpression | OnExpressionCondition |
ļжϡ
@ConditionalOnProperty``OnPropertyCondition#getMatchOutcome@ConditionalOnProperty Ĵ OnPropertyCondition#getMatchOutcome
class OnPropertyCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
// ȡ @ConditionalOnProperty ֵ
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
// determineOutcome(...) нжϣעcontext.getEnvironment()
ConditionOutcome outcome = determineOutcome(annotationAttributes,
context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}
...
}
DZȽϼģǻȡ @ConditionalOnProperty ֵٵ determineOutcome(...) дٽ OnPropertyCondition#determineOutcome
/**
*
* ע⣺resolver ĵ Environment applicationContext е Environment
*/
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,
PropertyResolver resolver) {
Spec spec = new Spec(annotationAttributes);
List<String> missingProperties = new ArrayList<>();
List<String> nonMatchingProperties = new ArrayList<>();
//
spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
// жϽ
if (!missingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnProperty.class, spec)
.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
}
// жϽ
if (!nonMatchingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnProperty.class, spec)
.found("different value in property", "different value in properties")
.items(Style.QUOTE, nonMatchingProperties));
}
// жϽ
return ConditionOutcome.match(ConditionMessage
.forCondition(ConditionalOnProperty.class, spec).because("matched"));
}
/**
*
*/
private void collectProperties(PropertyResolver resolver, List<String> missing,
List<String> nonMatching) {
for (String name : this.names) {
String key = this.prefix + name;
// resolver environment
// properties жϾж environment ûӦ
if (resolver.containsProperty(key)) {
if (!isMatch(resolver.getProperty(key), this.havingValue)) {
nonMatching.add(name);
}
}
else {
if (!this.matchIfMissing) {
missing.add(name);
}
}
}
}
Կ@ConditionalOnProperty ͨж environment Ƿижϵġ
@ConditionalOnResource``OnResourceCondition#getMatchOutcome@ConditionalOnResource Ĵһʹã
@Bean
@ConditionalOnResource(resources = "classpath:config.properties")
public Config config() {
return config;
}
ʾ classpath д config.properties ʱconfig Żᱻʼ springbean
ٽ OnResourceCondition#getOutcomes
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(ConditionalOnResource.class.getName(), true);
// ȡ ResourceLoader
ResourceLoader loader = context.getResourceLoader();
List<String> locations = new ArrayList<>();
collectValues(locations, attributes.get("resources"));
Assert.isTrue(!locations.isEmpty(),
"@ConditionalOnResource annotations must specify at least one resource location");
List<String> missing = new ArrayList<>();
// жԴǷ
for (String location : locations) {
// location пռλﴦ
String resource = context.getEnvironment().resolvePlaceholders(location);
// ж resource Ƿ
if (!loader.getResource(resource).exists()) {
missing.add(location);
}
}
//
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnResource.class)
.didNotFind("resource", "resources").items(Style.QUOTE, missing));
}
return ConditionOutcome.match(ConditionMessage.forCondition(ConditionalOnResource.class)
.found("location", "locations").items(locations));
}
ͨ OnResourceCondition#getOutcomes ȡ ResourceLoaderͨԷʽֵǰ ResourceLoader Ϊ AnnotationConfigServletWebServerApplicationContext
ȡ ResourceLoader ResourceLoader#getResource(String) ȡԴȻ Resource#exists жԴǷڣƥ
̵Ĺؼ ResourceLoader#getResource(String)÷Ĵ뵽 GenericApplicationContext#getResource
@Override
public Resource getResource(String location) {
if (this.resourceLoader != null) {
return this.resourceLoader.getResource(location);
}
return super.getResource(location);
}
this.resourceLoader Ϊ null븸ķ DefaultResourceLoader#getResource
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
// /ͷԴ
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
// classpathͷԴ
return new ClassPathResource(
location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// ϶㣬ʹ url
URL url = new URL(location);
return (ResourceUtils.isFileURL(url)
? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// url⣬ջ getResourceByPath(...)
return getResourceByPath(location);
}
}
}
/**
* ͨ·õ Resource
*/
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
ԿDefaultResourceLoader#getResource ͨж location ǰõ 4 Resource
ClassPathContextResourceFileUrlResourceUrlResourceõ Resource žжϸ Resource Ƿˣ ClassPathContextResource#exist ÷ ClassPathResource#exists
/**
* ж Resource Ƿ
*/
@Override
public boolean exists() {
return (resolveURL() != null);
}
/**
* ԴܻȡԴӦurlnull
*/
@Nullable
protected URL resolveURL() {
if (this.clazz != null) {
// ʹõǰ class Ӧ classLoader ȡ
return this.clazz.getResource(this.path);
}
else if (this.classLoader != null) {
// ʹָ classLoader ȡ
return this.classLoader.getResource(this.path);
}
else {
// ȡϵͳȡ
return ClassLoader.getSystemResource(this.path);
}
}
ӴԿͨ classLoader ȡļ urlͨжļ url ǷΪ null ж resource Ƿڡ
FileUrlResource жϣʵ FileUrlResource UrlResource exist() AbstractFileResolvingResource#existsͳһͿˣ÷£
public boolean exists() {
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// ļֱжļǷ
return getFile().exists();
}
else {
// ʹļ
URLConnection con = url.openConnection();
customizeConnection(con);
HttpURLConnection httpCon =
(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
// httpжϿӷص״̬
if (httpCon != null) {
int code = httpCon.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
return true;
}
else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
return false;
}
}
// contentLengthLong 0Ҳtrue
if (con.getContentLengthLong() > 0) {
return true;
}
if (httpCon != null) {
httpCon.disconnect();
return false;
}
else {
getInputStream().close();
return true;
}
}
}
catch (IOException ex) {
return false;
}
}
DZļֱʹ File#exists() жļǷڣжļǷڣжϷʽͲϸ˵ˡ
ܵ˵springboot @ConditionalOnResource жϻЩӵģܽ£
classpath ļͨ classloader ȡļӦ url ǷΪ null жļǷڣFile#exists() жļǷڣ@ConditionalOnWebApplication``OnWebApplicationCondition#getMatchOutcome@ConditionalOnWebApplication Ĵ OnWebApplicationCondition#getOutcomes
@Override
protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i < outcomes.length; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
//
outcomes[i] = getOutcome(autoConfigurationMetadata.get(autoConfigurationClass,
"ConditionalOnWebApplication"));
}
}
return outcomes;
}
/**
*
* springbootֵ֧web֣SERVLETREACTIVE
*/
private ConditionOutcome getOutcome(String type) {
if (type == null) {
return null;
}
ConditionMessage.Builder message = ConditionMessage
.forCondition(ConditionalOnWebApplication.class);
// ָ SERVLET
if (ConditionalOnWebApplication.Type.SERVLET.name().equals(type)) {
if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, getBeanClassLoader())) {
return ConditionOutcome.noMatch(
message.didNotFind("servlet web application classes").atAll());
}
}
// ָ REACTIVE
if (ConditionalOnWebApplication.Type.REACTIVE.name().equals(type)) {
if (!ClassNameFilter.isPresent(REACTIVE_WEB_APPLICATION_CLASS, getBeanClassLoader())) {
return ConditionOutcome.noMatch(
message.didNotFind("reactive web application classes").atAll());
}
}
// ûָweb
if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS, getBeanClassLoader())
&& !ClassUtils.isPresent(REACTIVE_WEB_APPLICATION_CLASS, getBeanClassLoader())) {
return ConditionOutcome.noMatch(
message.didNotFind("reactive or servlet web application classes").atAll());
}
return null;
}
ܼΪ @ConditionalOnWebApplication ָͣж϶ӦǷڣжϷʽ @ConditionalOnClass жǷһ£ͶӦ£
org.springframework.web.context.support.GenericWebApplicationContextorg.springframework.web.reactive.HandlerResult@ConditionalOnExpression``OnExpressionCondition#getMatchOutcome@ConditionalOnExpression Ĵ OnExpressionCondition#getOutcomes
/**
* ƥ
*/
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
// ȡʽ
String expression = (String) metadata.getAnnotationAttributes(
ConditionalOnExpression.class.getName()).get("value");
expression = wrapIfNecessary(expression);
ConditionMessage.Builder messageBuilder = ConditionMessage
.forCondition(ConditionalOnExpression.class, "(" + expression + ")");
// ռλ
expression = context.getEnvironment().resolvePlaceholders(expression);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory != null) {
// ʽֵ
boolean result = evaluateExpression(beanFactory, expression);
return new ConditionOutcome(result, messageBuilder.resultedIn(result));
}
return ConditionOutcome.noMatch(messageBuilder.because("no BeanFactory available."));
}
/**
* ʽֵ
*/
private Boolean evaluateExpression(ConfigurableListableBeanFactory beanFactory,
String expression) {
BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
if (resolver == null) {
resolver = new StandardBeanExpressionResolver();
}
// ʽֵ
BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null);
Object result = resolver.evaluate(expression, expressionContext);
return (result != null && (boolean) result);
}
Կspringboot ͨ BeanExpressionResolver#evaluate ʽ spring ʽľͲչˡ
ˣspring עķ͵ˣҪ˵ǣspringboot ע⣺
ЩעжϷʽ뱾ĵķʽƣͲһһзˡ
ԭӣhttps://my.oschina.net/funcy/blog/4921590 ߸ˮƽд֮ӭָԭףҵתϵȨҵתע