1. 程式人生 > >深入理解SpringBoot之裝配條件

深入理解SpringBoot之裝配條件

  我們知道自動裝配是SpringBoot微服務化的核心,它會把META-INF/spring.factoires裡配置的EnableAutoConfiguration註冊到IOC容器裡。但是,請大家考慮一個問題,根據需求我們要配置一個tomcat的內嵌容器,可是當前的執行環境裡都沒有servlet的相關API或者說當前的ApplicationContext不是一個WebApplicationContext,如果這樣的話,那麼建立tomcat的內嵌容器還有什麼意義上呢?如果根據需求我們想自動裝配一個Mybatis的SqlSessionFactory,可是執行環境裡連DataSource都沒有,恐怕要自動裝配Mybatis的願望也會落空吧!針對這種問題,SpringBoot早都考慮到了,下面我們來看看SpringBoot是怎麼解決的。

一、關於@Conditional

  conditional中文的意思為條件,其本身是Springframework提供的核心註解,通常情況下該註解可以加在類上或者方法上與@Configuration或者@Bean配合使用,當和@Configuration配合使用時,那麼該類下所有@Bean方法 或者@Import 或者 @ComponentScan都會受到其配置條件的影響,我們先看一下其原始碼:

/*
 * Copyright 2002-2017 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.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates that a component is only eligible for registration when all * {@linkplain #value specified conditions} match. * * <p>A <em>condition</em> is any state that can be determined programmatically * before the bean definition is due to be registered (see {@link Condition} for details). * * <p>The {@code @Conditional} annotation may be used in any of the following ways: * <ul> * <li>as a type-level annotation on any class directly or indirectly annotated with * {@code @Component}, including {@link Configuration @Configuration} classes</li> * <li>as a meta-annotation, for the purpose of composing custom stereotype * annotations</li> * <li>as a method-level annotation on any {@link Bean @Bean} method</li> * </ul> * * <p>If a {@code @Configuration} class is marked with {@code @Conditional}, * all of the {@code @Bean} methods, {@link Import @Import} annotations, and * {@link ComponentScan @ComponentScan} annotations associated with that * class will be subject to the conditions. * * <p><strong>NOTE</strong>: Inheritance of {@code @Conditional} annotations * is not supported; any conditions from superclasses or from overridden * methods will not be considered. In order to enforce these semantics, * {@code @Conditional} itself is not declared as * {@link java.lang.annotation.Inherited @Inherited}; furthermore, any * custom <em>composed annotation</em> that is meta-annotated with * {@code @Conditional} must not be declared as {@code @Inherited}. * * @author Phillip Webb * @author Sam Brannen * @since 4.0 * @see Condition */ @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(); }
View Code

在這裡文件註釋提醒我們去看Condition介面:

/*
 * Copyright 2002-2013 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.context.annotation;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * A single {@code condition} that must be {@linkplain #matches matched} in order
 * for a component to be registered.
 *
 * <p>Conditions are checked immediately before the bean-definition is due to be
 * registered and are free to veto registration based on any criteria that can
 * be determined at that point.
 *
 * <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
 * and take care to never interact with bean instances. For more fine-grained control
 * of conditions that interact with {@code @Configuration} beans consider the
 * {@link ConfigurationCondition} interface.
 *
 * @author Phillip Webb
 * @since 4.0
 * @see ConfigurationCondition
 * @see Conditional
 * @see ConditionContext
 */
public interface Condition {

    /**
     * Determine if the condition matches.
     * @param context the condition context
     * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
     * or {@link org.springframework.core.type.MethodMetadata method} being checked.
     * @return {@code true} if the condition matches and the component can be registered
     * or {@code false} to veto registration.
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}
View Code

該介面就有一個方法:matches方法。它定義了最基本的匹配規則,該方法傳入兩個引數一個是ConditionContext ,該介面定義了若干個方法來獲取spring核心介面的方法:

/*
 * Copyright 2002-2017 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.context.annotation;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;

/**
 * Context information for use by {@link Condition}s.
 *
 * @author Phillip Webb
 * @author Juergen Hoeller
 * @since 4.0
 */
public interface ConditionContext {

    /**
     * Return the {@link BeanDefinitionRegistry} that will hold the bean definition
     * should the condition match, or {@code null} if the registry is not available.
     */
    BeanDefinitionRegistry getRegistry();

    /**
     * Return the {@link ConfigurableListableBeanFactory} that will hold the bean
     * definition should the condition match, or {@code null} if the bean factory
     * is not available.
     */
    ConfigurableListableBeanFactory getBeanFactory();

    /**
     * Return the {@link Environment} for which the current application is running,
     * or {@code null} if no environment is available.
     */
    Environment getEnvironment();

    /**
     * Return the {@link ResourceLoader} currently being used, or {@code null} if
     * the resource loader cannot be obtained.
     */
    ResourceLoader getResourceLoader();

    /**
     * Return the {@link ClassLoader} that should be used to load additional classes,
     * or {@code null} if the default classloader should be used.
     */
    ClassLoader getClassLoader();

}
View Code

在這裡我們能獲取到BeanFactory,ResourceLoader,Enviroment等。而另外一個引數是AnnotatedTypeMetadata介面,該介面主要獲取該類上標記的註解。在這裡我先寫一個簡單的例子,來試驗一下:

MyTestConditional:

package com.hzgj.lyrk.autoconfigure;

import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.util.Map;

public class MyTestConditional implements ConfigurationCondition {
    @Override
    public ConfigurationPhase getConfigurationPhase() {
        return ConfigurationPhase.REGISTER_BEAN;
    }

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> map = metadata.getAnnotationAttributes("org.springframework.context.annotation.Description");
        System.out.println(map);
        return false;
    }
}

  這個是自定義的Conditional,該類實現了ConfigurationCondition介面,該介面繼承了Condition,只不過它多添加了一個用於設定解析Condition階段的方法,在這裡有兩個階段進行解析:

  1)PARSE_CONFIGURATION:會在解析@Configuration時進行condition的解析

  2)REGISTER_BEAN:會在註冊Bean的時候進行condition的解析

ServerAutoConfiguration:

package com.hzgj.lyrk.autoconfigure;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;

@Configuration
public class ServerAutoConfiguration {

    @Configuration
    @Conditional(MyTestConditional.class)
    @Description(value = "student")
    public static class StudentAutoConfiguration {
        @Bean
        public Student student() {
            System.out.println("student create....");
            return new Student();
        }


    }

    @Configuration
    @Conditional(MyTestConditional.class)
    @Description(value = "teacher")
    public static class TeacherAutoConfiguration {
        @Bean
        public Teacher teacher() {
            System.out.println("teacher create.....");
            return new Teacher();
        }
    }
}

此時由於自定義的Conditional的match方法返回值是false,因此不能註冊@Bean配置的物件:

當值改為true時,則能註冊@Bean配置的物件:

二、SpringBoot中對Conditional的擴充套件

  在SpringBoot中定義了一個SpringBootCondition類對Condition進行了擴充套件,該類原始碼如下:

/*
 * Copyright 2012-2017 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * Base of all {@link Condition} implementations used with Spring Boot. Provides sensible
 * logging to help the user diagnose what classes are loaded.
 *
 * @author Phillip Webb
 * @author Greg Turnquist
 */
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);
            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);
        }
    }

    private String getName(AnnotatedTypeMetadata metadata) {
        if (metadata instanceof AnnotationMetadata) {
            return ((AnnotationMetadata) metadata).getClassName();
        }
        if (metadata instanceof MethodMetadata) {
            MethodMetadata methodMetadata = (MethodMetadata) metadata;
            return methodMetadata.getDeclaringClassName() + "."
                    + methodMetadata.getMethodName();
        }
        return metadata.toString();
    }

    private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) {
        if (metadata instanceof ClassMetadata) {
            ClassMetadata classMetadata = (ClassMetadata) metadata;
            return classMetadata.getClassName();
        }
        MethodMetadata methodMetadata = (MethodMetadata) metadata;
        return methodMetadata.getDeclaringClassName() + "#"
                + methodMetadata.getMethodName();
    }

    protected final void logOutcome(String classOrMethodName, ConditionOutcome outcome) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(getLogMessage(classOrMethodName, outcome));
        }
    }

    private StringBuilder getLogMessage(String classOrMethodName,
            ConditionOutcome outcome) {
        StringBuilder message = new StringBuilder();
        message.append("Condition ");
        message.append(ClassUtils.getShortName(getClass()));
        message.append(" on ");
        message.append(classOrMethodName);
        message.append(outcome.isMatch() ? " matched" : " did not match");
        if (StringUtils.hasLength(outcome.getMessage())) {
            message.append(" due to ");
            message.append(outcome.getMessage());
        }
        return message;
    }

    private void recordEvaluation(ConditionContext context, String classOrMethodName,
            ConditionOutcome outcome) {
        if (context.getBeanFactory() != null) {
            ConditionEvaluationReport.get(context.getBeanFactory())
                    .recordConditionEvaluation(classOrMethodName, this, outcome);
        }
    }

    /**
     * Determine the outcome of the match along with suitable log output.
     * @param context the condition context
     * @param metadata the annotation metadata
     * @return the condition outcome
     */
    public abstract ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata);

    /**
     * Return true if any of the specified conditions match.
     * @param context the context
     * @param metadata the annotation meta-data
     * @param conditions conditions to test
     * @return {@code true} if any condition matches.
     */
    protected final boolean anyMatches(ConditionContext context,
            AnnotatedTypeMetadata metadata, Condition... conditions) {
        for (Condition condition : conditions) {
            if (matches(context, metadata, condition)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return true if any of the specified condition matches.
     * @param context the context
     * @param metadata the annotation meta-data
     * @param condition condition to test
     * @return {@code true} if the condition matches.
     */
    protected final boolean matches(ConditionContext context,
            AnnotatedTypeMetadata metadata, Condition condition) {
        if (condition instanceof SpringBootCondition) {
            return ((SpringBootCondition) condition).getMatchOutcome(context, metadata)
                    .isMatch();
        }
        return condition.matches(context, metadata);
    }

}
View Code

  在這裡,我們需要重寫getMatchOutcome方法來進行,匹配結果的過濾,下面我們列舉一下常見的Conditional:

2.1、Class Conditions

  常見的有ConditionalOnClass,ConditionalOnMissingClass

  ConditionalOnClass:表明當前classpath有對應指定的型別才去建立Bean,我們來看一下原始碼:

/*
 * Copyright 2012-2017 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.Conditional;

/**
 * {@link Conditional} that only matches when the specified classes are on the classpath.
 *
 * @author Phillip Webb
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

    /**
     * The classes that must be present. Since this annotation is parsed by loading class
     * bytecode, it is safe to specify classes here that may ultimately not be on the
     * classpath, only if this annotation is directly on the affected component and
     * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
     * use this annotation as a meta-annotation, only use the {@link #name} attribute.
     * @return the classes that must be present
     */
    Class<?>[] value() default {};

    /**
     * The classes names that must be present.
     * @return the class names that must be present.
     */
    String[] name() default {};

}
View Code

  根據註釋我們去尋找一下:OnClassCondition這個類,我貼出部分程式碼:

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        ClassLoader classLoader = context.getClassLoader();
        ConditionMessage matchMessage = ConditionMessage.empty();
        List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
        if (onClasses != null) {
            List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
            if (!missing.isEmpty()) {
                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,
                            getMatches(onClasses, MatchType.PRESENT, classLoader));
        }
        List<String> onMissingClasses = getCandidates(metadata,
                ConditionalOnMissingClass.class);
        if (onMissingClasses != null) {
            List<String> present = getMatches(onMissingClasses, MatchType.PRESENT,
                    classLoader);
            if (!present.isEmpty()) {
                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,
                            getMatches(onMissingClasses, MatchType.MISSING, classLoader));
        }
        return ConditionOutcome.match(matchMessage);
    }

在這裡我們關注一下getMatches方法:

private List<String> getMatches(Collection<String> candidates, MatchType matchType,
            ClassLoader classLoader) {
        List<String> matches = new ArrayList<String>(candidates.size());
        for (String candidate : candidates) {
            if (matchType.matches(candidate, classLoader)) {
                matches.add(candidate);
            }
        }
        return matches;
    }


//.....

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);
        }

  我們可以看到這裡是通過ClassLoader或者Class.forName來載入類的

2.2、Bean Conditionals

  在這裡常見的是ConditionalOnBean和ConditionalOnMissingBean,只有當BeanFactory裡(不)包含指定的Bean時,才能通過匹配。注意:官網建議我們在AutoConfiguration裡使用此註解,因為受到bean裝配順序影響,很有可能不能達到我們的預期效果。

package com.hzgj.lyrk.autoconfigure;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;

@Configuration
public class ServerAutoConfiguration {

    @Configuration
    @ConditionalOnBean(Teacher.class)
    public static class StudentAutoConfiguration {
        @Bean
        public Student student() {
            System.out.println("student create....");
            return new Student();
        }


    }

    @Configuration
    public static class TeacherAutoConfiguration {
        @Bean
        public Teacher teacher() {
            System.out.println("teacher create.....");
            return new Teacher();
        }
    }
}

比如說如上程式碼,執行後將得到如下結果:

我們可以看到此時Student並未建立。因為受其順序影響當註冊Student時,IOC容器裡並沒有Teacher,我在這裡貼出OnBeanCondition的關鍵程式碼:

@Override
    public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        ConditionMessage matchMessage = ConditionMessage.empty();
        if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
            BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
                    ConditionalOnBean.class);
            List<String> matching = getMatchingBeans(context, spec);
            if (matching.isEmpty()) {
                return ConditionOutcome.noMatch(
                        ConditionMessage.forCondition(ConditionalOnBean.class, spec)
                                .didNotFind("any beans").atAll());
            }
            matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec)
                    .found("bean", "beans").items(Style.QUOTE, matching);
        }
        if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
            BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
                    ConditionalOnSingleCandidate.class);
            List<String> matching = getMatchingBeans(context, spec);
            if (matching.isEmpty()) {
                return ConditionOutcome.noMatch(ConditionMessage
                        .forCondition(ConditionalOnSingleCandidate.class, spec)
                        .didNotFind("any beans").atAll());
            }
            else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matching,
                    spec.getStrategy() == SearchStrategy.ALL)) {
                return ConditionOutcome.noMatch(ConditionMessage
                        .forCondition(ConditionalOnSingleCandidate.class, spec)
                        .didNotFind("a primary bean from beans")
                        .items(Style.QUOTE, matching));
            }
            matchMessage = matchMessage
                    .andCondition(ConditionalOnSingleCandidate.class, spec)
                    .found("a primary bean from beans").items(Style.QUOTE, matching);
        }
        if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
            BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
                    ConditionalOnMissingBean.class);
            List<String> matching = getMatchingBeans(context, spec);
            if (!matching.isEmpty()) {
                return ConditionOutcome.noMatch(ConditionMessage
                        .forCondition(ConditionalOnMissingBean.class, spec)
                        .found("bean", "beans").items(Style.QUOTE, matching));
            }
            matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec)
                    .didNotFind("any beans").atAll();
        }
        return ConditionOutcome.match(matchMessage);
    }

  @SuppressWarnings("deprecation")
    private List<String> getMatchingBeans(ConditionContext context,
            BeanSearchSpec beans) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        if (beans.getStrategy() == SearchStrategy.PARENTS
                || beans.getStrategy() == SearchStrategy.ANCESTORS) {
            BeanFactory parent = beanFactory.getParentBeanFactory();
            Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
                    "Unable to use SearchStrategy.PARENTS");
            beanFactory = (ConfigurableListableBeanFactory) parent;
        }
        if (beanFactory == null) {
            return Collections.emptyList();
        }
        List<String> beanNames = new ArrayList<String>();
        boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
        for (String type : beans.getTypes()) {
            beanNames.addAll(getBeanNamesForType(beanFactory, type,
                    context.getClassLoader(), considerHierarchy));
        }
        for (String ignoredType : beans.getIgnoredTypes()) {
            beanNames.removeAll(getBeanNamesForType(beanFactory, ignoredType,
                    context.getClassLoader(), considerHierarchy));
        }
        for (String annotation : beans.getAnnotations()) {
            beanNames.addAll(Arrays.asList(getBeanNamesForAnnotation(beanFactory,
                    annotation, context.getClassLoader(), considerHierarchy)));
        }
        for (String beanName : beans.getNames()) {
            if (containsBean(beanFactory, beanName, considerHierarchy)) {
                beanNames.add(beanName);
            }
        }
        return beanNames;
    }

  在這裡我們關注getMatchingBeans方法,此方法從當前的BeanFactory找所需要的Bean。由於BeanFactory層次化的關係,因此在ConditionalOn(Missing)Bean裡有相關屬性來配置尋找策略:

/**
     * Strategy to decide if the application context hierarchy (parent contexts) should be
     * considered.
     * @return the search strategy
     */
    SearchStrategy search() default SearchStrategy.ALL;
/*
 * 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;

/**
 * Some named search strategies for beans in the bean factory hierarchy.
 *
 * @author Dave Syer
 */
public enum SearchStrategy {

    /**
     * Search only the current context.
     */
    CURRENT,

    /**
     * Search all parents and ancestors, but not the current context.
     * @deprecated as of 1.5 in favor of {@link SearchStrategy#ANCESTORS}
     */
    @D