Add AutoConfigurationImportFilter base classes
Add `FilteringSpringBootCondition` base class and refactor the existing `OnClassCondition` to use it. Also update the `match` method so that the `autoConfigurationClasses` array may include `null` elements. See gh-13328
This commit is contained in:
parent
5765ed00e8
commit
e4f54a45be
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -45,7 +45,8 @@ public interface AutoConfigurationImportFilter {
|
|||
/**
|
||||
* Apply the filter to the given auto-configuration class candidates.
|
||||
* @param autoConfigurationClasses the auto-configuration classes being considered.
|
||||
* Implementations should not change the values in this array.
|
||||
* This array may contain {@code null} elements. Implementations should not change the
|
||||
* values in this array.
|
||||
* @param autoConfigurationMetadata access to the meta-data generated by the
|
||||
* auto-configure annotation processor
|
||||
* @return a boolean array indicating which of the auto-configuration classes should
|
||||
|
|
|
@ -263,6 +263,7 @@ public class AutoConfigurationImportSelector
|
|||
for (int i = 0; i < match.length; i++) {
|
||||
if (!match[i]) {
|
||||
skip[i] = true;
|
||||
candidates[i] = null;
|
||||
skipped = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.SortedMap;
|
|||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
|
@ -159,6 +160,20 @@ public final class ConditionEvaluationReport {
|
|||
return this.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find the {@link ConditionEvaluationReport} for the specified bean
|
||||
* factory.
|
||||
* @param beanFactory the bean factory (may be {@code null})
|
||||
* @return the {@link ConditionEvaluationReport} or {@code null}
|
||||
*/
|
||||
public static ConditionEvaluationReport find(BeanFactory beanFactory) {
|
||||
if (beanFactory != null && beanFactory instanceof ConfigurableBeanFactory) {
|
||||
return ConditionEvaluationReport
|
||||
.get((ConfigurableListableBeanFactory) beanFactory);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link ConditionEvaluationReport} for the specified bean factory.
|
||||
* @param beanFactory the bean factory
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for a {@link SpringBootCondition} that also implements
|
||||
* {@link AutoConfigurationImportFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class FilteringSpringBootCondition extends SpringBootCondition
|
||||
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Override
|
||||
public boolean[] match(String[] autoConfigurationClasses,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
ConditionEvaluationReport report = ConditionEvaluationReport
|
||||
.find(this.beanFactory);
|
||||
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
|
||||
autoConfigurationMetadata);
|
||||
boolean[] match = new boolean[outcomes.length];
|
||||
for (int i = 0; i < outcomes.length; i++) {
|
||||
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
|
||||
if (!match[i] && outcomes[i] != null) {
|
||||
logOutcome(autoConfigurationClasses[i], outcomes[i]);
|
||||
if (report != null) {
|
||||
report.recordConditionEvaluation(autoConfigurationClasses[i], this,
|
||||
outcomes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
protected abstract ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata);
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
protected final BeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
protected final ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
protected 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public abstract boolean matches(String className, ClassLoader classLoader);
|
||||
|
||||
public static boolean isPresent(String className, ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
classLoader = ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
try {
|
||||
forName(className, classLoader);
|
||||
return true;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> forName(String className, ClassLoader classLoader)
|
||||
throws ClassNotFoundException {
|
||||
if (classLoader != null) {
|
||||
return classLoader.loadClass(className);
|
||||
}
|
||||
return Class.forName(className);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -18,17 +18,10 @@ package org.springframework.boot.autoconfigure.condition;
|
|||
|
||||
import java.security.AccessControlException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
|
||||
|
@ -37,7 +30,6 @@ import org.springframework.context.annotation.ConditionContext;
|
|||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
|
@ -49,43 +41,10 @@ import org.springframework.util.MultiValueMap;
|
|||
* @see ConditionalOnMissingClass
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
class OnClassCondition extends SpringBootCondition
|
||||
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
class OnClassCondition extends FilteringSpringBootCondition {
|
||||
|
||||
@Override
|
||||
public boolean[] match(String[] autoConfigurationClasses,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
ConditionEvaluationReport report = getConditionEvaluationReport();
|
||||
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
|
||||
autoConfigurationMetadata);
|
||||
boolean[] match = new boolean[outcomes.length];
|
||||
for (int i = 0; i < outcomes.length; i++) {
|
||||
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
|
||||
if (!match[i] && outcomes[i] != null) {
|
||||
logOutcome(autoConfigurationClasses[i], outcomes[i]);
|
||||
if (report != null) {
|
||||
report.recordConditionEvaluation(autoConfigurationClasses[i], this,
|
||||
outcomes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
private ConditionEvaluationReport getConditionEvaluationReport() {
|
||||
if (this.beanFactory != null
|
||||
&& this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||
return ConditionEvaluationReport
|
||||
.get((ConfigurableListableBeanFactory) this.beanFactory);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
|
||||
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
// Split the work and perform half in a background thread. Using a single
|
||||
// additional thread seems to offer the best performance. More threads make
|
||||
|
@ -95,7 +54,7 @@ class OnClassCondition extends SpringBootCondition
|
|||
autoConfigurationClasses, 0, split, autoConfigurationMetadata);
|
||||
OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
|
||||
autoConfigurationClasses, split, autoConfigurationClasses.length,
|
||||
autoConfigurationMetadata, this.beanClassLoader);
|
||||
autoConfigurationMetadata, getBeanClassLoader());
|
||||
ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
|
||||
ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
|
||||
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
|
||||
|
@ -108,7 +67,7 @@ class OnClassCondition extends SpringBootCondition
|
|||
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
OutcomesResolver outcomesResolver = new StandardOutcomesResolver(
|
||||
autoConfigurationClasses, start, end, autoConfigurationMetadata,
|
||||
this.beanClassLoader);
|
||||
getBeanClassLoader());
|
||||
try {
|
||||
return new ThreadedOutcomesResolver(outcomesResolver);
|
||||
}
|
||||
|
@ -124,7 +83,8 @@ class OnClassCondition extends SpringBootCondition
|
|||
ConditionMessage matchMessage = ConditionMessage.empty();
|
||||
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
|
||||
if (onClasses != null) {
|
||||
List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
|
||||
List<String> missing = filter(onClasses, ClassNameFilter.MISSING,
|
||||
classLoader);
|
||||
if (!missing.isEmpty()) {
|
||||
return ConditionOutcome
|
||||
.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
|
||||
|
@ -133,12 +93,12 @@ class OnClassCondition extends SpringBootCondition
|
|||
}
|
||||
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
|
||||
.found("required class", "required classes").items(Style.QUOTE,
|
||||
getMatches(onClasses, MatchType.PRESENT, classLoader));
|
||||
filter(onClasses, ClassNameFilter.PRESENT, classLoader));
|
||||
}
|
||||
List<String> onMissingClasses = getCandidates(metadata,
|
||||
ConditionalOnMissingClass.class);
|
||||
if (onMissingClasses != null) {
|
||||
List<String> present = getMatches(onMissingClasses, MatchType.PRESENT,
|
||||
List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT,
|
||||
classLoader);
|
||||
if (!present.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(
|
||||
|
@ -147,8 +107,9 @@ class OnClassCondition extends SpringBootCondition
|
|||
.items(Style.QUOTE, present));
|
||||
}
|
||||
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
|
||||
.didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE,
|
||||
getMatches(onMissingClasses, MatchType.MISSING, classLoader));
|
||||
.didNotFind("unwanted class", "unwanted classes")
|
||||
.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING,
|
||||
classLoader));
|
||||
}
|
||||
return ConditionOutcome.match(matchMessage);
|
||||
}
|
||||
|
@ -174,72 +135,6 @@ class OnClassCondition extends SpringBootCondition
|
|||
}
|
||||
}
|
||||
|
||||
private List<String> getMatches(Collection<String> candidates, MatchType matchType,
|
||||
ClassLoader classLoader) {
|
||||
List<String> matches = new ArrayList<>(candidates.size());
|
||||
for (String candidate : candidates) {
|
||||
if (matchType.matches(candidate, classLoader)) {
|
||||
matches.add(candidate);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
private enum MatchType {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static boolean isPresent(String className, ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
classLoader = ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
try {
|
||||
forName(className, classLoader);
|
||||
return true;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> forName(String className, ClassLoader classLoader)
|
||||
throws ClassNotFoundException {
|
||||
if (classLoader != null) {
|
||||
return classLoader.loadClass(className);
|
||||
}
|
||||
return Class.forName(className);
|
||||
}
|
||||
|
||||
public abstract boolean matches(String className, ClassLoader classLoader);
|
||||
|
||||
}
|
||||
|
||||
private interface OutcomesResolver {
|
||||
|
||||
ConditionOutcome[] resolveOutcomes();
|
||||
|
@ -304,10 +199,12 @@ class OnClassCondition extends SpringBootCondition
|
|||
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
|
||||
for (int i = start; i < end; i++) {
|
||||
String autoConfigurationClass = autoConfigurationClasses[i];
|
||||
Set<String> candidates = autoConfigurationMetadata
|
||||
.getSet(autoConfigurationClass, "ConditionalOnClass");
|
||||
if (candidates != null) {
|
||||
outcomes[i - start] = getOutcome(candidates);
|
||||
if (autoConfigurationClass != null) {
|
||||
Set<String> candidates = autoConfigurationMetadata
|
||||
.getSet(autoConfigurationClass, "ConditionalOnClass");
|
||||
if (candidates != null) {
|
||||
outcomes[i - start] = getOutcome(candidates);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outcomes;
|
||||
|
@ -315,7 +212,7 @@ class OnClassCondition extends SpringBootCondition
|
|||
|
||||
private ConditionOutcome getOutcome(Set<String> candidates) {
|
||||
try {
|
||||
List<String> missing = getMatches(candidates, MatchType.MISSING,
|
||||
List<String> missing = filter(candidates, ClassNameFilter.MISSING,
|
||||
this.beanClassLoader);
|
||||
if (!missing.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(
|
||||
|
|
Loading…
Reference in New Issue