zoukankan      html  css  js  c++  java
  • 深入理解SpringBoot之自动装配

      SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提。其实它并不那么神秘,我在这之前已经写过最基本的实现了,大家可以参考这篇文章。这次主要的议题是,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉。

    一、自动装配过程分析

    1.1、关于@SpringBootApplication

      我们在编写SpringBoot项目时,@SpringBootApplication是最常见的注解了,我们可以看一下源代码:

    /*
     * 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;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.boot.SpringBootConfiguration;
    import org.springframework.boot.context.TypeExcludeFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.core.annotation.AliasFor;
    
    /**
     * Indicates a {@link Configuration configuration} class that declares one or more
     * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
     * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
     * annotation that is equivalent to declaring {@code @Configuration},
     * {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
     *
     * @author Phillip Webb
     * @author Stephane Nicoll
     * @since 1.2.0
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    
        /**
         * Exclude specific auto-configuration classes such that they will never be applied.
         * @return the classes to exclude
         */
        @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
        Class<?>[] exclude() default {};
    
        /**
         * Exclude specific auto-configuration class names such that they will never be
         * applied.
         * @return the class names to exclude
         * @since 1.3.0
         */
        @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
        String[] excludeName() default {};
    
        /**
         * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
         * for a type-safe alternative to String-based package names.
         * @return base packages to scan
         * @since 1.3.0
         */
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
        String[] scanBasePackages() default {};
    
        /**
         * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
         * scan for annotated components. The package of each class specified will be scanned.
         * <p>
         * Consider creating a special no-op marker class or interface in each package that
         * serves no purpose other than being referenced by this attribute.
         * @return base packages to scan
         * @since 1.3.0
         */
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
        Class<?>[] scanBasePackageClasses() default {};
    
    }
    View Code

      这里面包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan,此处@ComponentScan由于没有指定扫描包,因此它默认扫描的是与该类同级的类或者同级包下的所有类,另外@SpringBootConfiguration,通过源码得知它是一个@Configuration:

    /*
     * Copyright 2012-2016 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;
    
    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.Configuration;
    
    /**
     * Indicates that a class provides Spring Boot application
     * {@link Configuration @Configuration}. Can be used as an alternative to the Spring's
     * standard {@code @Configuration} annotation so that configuration can be found
     * automatically (for example in tests).
     * <p>
     * Application should only ever include <em>one</em> {@code @SpringBootConfiguration} and
     * most idiomatic Spring Boot applications will inherit it from
     * {@code @SpringBootApplication}.
     *
     * @author Phillip Webb
     * @since 1.4.0
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    
    }
    View Code

      由此我们可以推断出@SpringBootApplication等同于@Configuration @ComponentScan @EnableAutoConfiguration

    1.2、@EnableAutoConfiguration

      一旦加上此注解,那么将会开启自动装配功能,简单点讲,Spring会试图在你的classpath下找到所有配置的Bean然后进行装配。当然装配Bean时,会根据若干个(Conditional)定制规则来进行初始化。我们看一下它的源码:

    /*
     * 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;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
    import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.core.io.support.SpringFactoriesLoader;
    
    /**
     * Enable auto-configuration of the Spring Application Context, attempting to guess and
     * configure beans that you are likely to need. Auto-configuration classes are usually
     * applied based on your classpath and what beans you have defined. For example, If you
     * have {@code tomcat-embedded.jar} on your classpath you are likely to want a
     * {@link TomcatEmbeddedServletContainerFactory} (unless you have defined your own
     * {@link EmbeddedServletContainerFactory} bean).
     * <p>
     * When using {@link SpringBootApplication}, the auto-configuration of the context is
     * automatically enabled and adding this annotation has therefore no additional effect.
     * <p>
     * Auto-configuration tries to be as intelligent as possible and will back-away as you
     * define more of your own configuration. You can always manually {@link #exclude()} any
     * configuration that you never want to apply (use {@link #excludeName()} if you don't
     * have access to them). You can also exclude them via the
     * {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied
     * after user-defined beans have been registered.
     * <p>
     * The package of the class that is annotated with {@code @EnableAutoConfiguration},
     * usually via {@code @SpringBootApplication}, has specific significance and is often used
     * as a 'default'. For example, it will be used when scanning for {@code @Entity} classes.
     * It is generally recommended that you place {@code @EnableAutoConfiguration} (if you're
     * not using {@code @SpringBootApplication}) in a root package so that all sub-packages
     * and classes can be searched.
     * <p>
     * Auto-configuration classes are regular Spring {@link Configuration} beans. They are
     * located using the {@link SpringFactoriesLoader} mechanism (keyed against this class).
     * Generally auto-configuration beans are {@link Conditional @Conditional} beans (most
     * often using {@link ConditionalOnClass @ConditionalOnClass} and
     * {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
     *
     * @author Phillip Webb
     * @author Stephane Nicoll
     * @see ConditionalOnBean
     * @see ConditionalOnMissingBean
     * @see ConditionalOnClass
     * @see AutoConfigureAfter
     * @see SpringBootApplication
     */
    @SuppressWarnings("deprecation")
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(EnableAutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        /**
         * Exclude specific auto-configuration classes such that they will never be applied.
         * @return the classes to exclude
         */
        Class<?>[] exclude() default {};
    
        /**
         * Exclude specific auto-configuration class names such that they will never be
         * applied.
         * @return the class names to exclude
         * @since 1.3.0
         */
        String[] excludeName() default {};
    
    }
    View Code

      虽然根据文档注释的说明它指点我们去看EnableAutoConfigurationImportSelector。但是该类在SpringBoot1.5.X版本已经过时了,因此我们看一下它的父类AutoConfigurationImportSelector:

    /*
     * 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;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.Aware;
    import org.springframework.beans.factory.BeanClassLoaderAware;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.boot.bind.RelaxedPropertyResolver;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.context.ResourceLoaderAware;
    import org.springframework.context.annotation.DeferredImportSelector;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.Environment;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.io.support.SpringFactoriesLoader;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.util.Assert;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.StringUtils;
    
    /**
     * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
     * auto-configuration}. This class can also be subclassed if a custom variant of
     * {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
     *
     * @author Phillip Webb
     * @author Andy Wilkinson
     * @author Stephane Nicoll
     * @author Madhura Bhave
     * @since 1.3.0
     * @see EnableAutoConfiguration
     */
    public class AutoConfigurationImportSelector
            implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
            BeanFactoryAware, EnvironmentAware, Ordered {
    
        private static final String[] NO_IMPORTS = {};
    
        private static final Log logger = LogFactory
                .getLog(AutoConfigurationImportSelector.class);
    
        private ConfigurableListableBeanFactory beanFactory;
    
        private Environment environment;
    
        private ClassLoader beanClassLoader;
    
        private ResourceLoader resourceLoader;
    
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            try {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                        .loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = getAttributes(annotationMetadata);
                List<String> configurations = getCandidateConfigurations(annotationMetadata,
                        attributes);
                configurations = removeDuplicates(configurations);
                configurations = sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = getExclusions(annotationMetadata, attributes);
                checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = filter(configurations, autoConfigurationMetadata);
                fireAutoConfigurationImportEvents(configurations, exclusions);
                return configurations.toArray(new String[configurations.size()]);
            }
            catch (IOException ex) {
                throw new IllegalStateException(ex);
            }
        }
    
        protected boolean isEnabled(AnnotationMetadata metadata) {
            return true;
        }
    
        /**
         * Return the appropriate {@link AnnotationAttributes} from the
         * {@link AnnotationMetadata}. By default this method will return attributes for
         * {@link #getAnnotationClass()}.
         * @param metadata the annotation metadata
         * @return annotation attributes
         */
        protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
            String name = getAnnotationClass().getName();
            AnnotationAttributes attributes = AnnotationAttributes
                    .fromMap(metadata.getAnnotationAttributes(name, true));
            Assert.notNull(attributes,
                    "No auto-configuration attributes found. Is " + metadata.getClassName()
                            + " annotated with " + ClassUtils.getShortName(name) + "?");
            return attributes;
        }
    
        /**
         * Return the source annotation class used by the selector.
         * @return the annotation class
         */
        protected Class<?> getAnnotationClass() {
            return EnableAutoConfiguration.class;
        }
    
        /**
         * Return the auto-configuration class names that should be considered. By default
         * this method will load candidates using {@link SpringFactoriesLoader} with
         * {@link #getSpringFactoriesLoaderFactoryClass()}.
         * @param metadata the source metadata
         * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
         * attributes}
         * @return a list of candidate configurations
         */
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
                AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                    getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
            Assert.notEmpty(configurations,
                    "No auto configuration classes found in META-INF/spring.factories. If you "
                            + "are using a custom packaging, make sure that file is correct.");
            return configurations;
        }
    
        /**
         * Return the class used by {@link SpringFactoriesLoader} to load configuration
         * candidates.
         * @return the factory class
         */
        protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }
    
        private void checkExcludedClasses(List<String> configurations,
                Set<String> exclusions) {
            List<String> invalidExcludes = new ArrayList<String>(exclusions.size());
            for (String exclusion : exclusions) {
                if (ClassUtils.isPresent(exclusion, getClass().getClassLoader())
                        && !configurations.contains(exclusion)) {
                    invalidExcludes.add(exclusion);
                }
            }
            if (!invalidExcludes.isEmpty()) {
                handleInvalidExcludes(invalidExcludes);
            }
        }
    
        /**
         * Handle any invalid excludes that have been specified.
         * @param invalidExcludes the list of invalid excludes (will always have at least one
         * element)
         */
        protected void handleInvalidExcludes(List<String> invalidExcludes) {
            StringBuilder message = new StringBuilder();
            for (String exclude : invalidExcludes) {
                message.append("	- ").append(exclude).append(String.format("%n"));
            }
            throw new IllegalStateException(String
                    .format("The following classes could not be excluded because they are"
                            + " not auto-configuration classes:%n%s", message));
        }
    
        /**
         * Return any exclusions that limit the candidate configurations.
         * @param metadata the source metadata
         * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
         * attributes}
         * @return exclusions or an empty set
         */
        protected Set<String> getExclusions(AnnotationMetadata metadata,
                AnnotationAttributes attributes) {
            Set<String> excluded = new LinkedHashSet<String>();
            excluded.addAll(asList(attributes, "exclude"));
            excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
            excluded.addAll(getExcludeAutoConfigurationsProperty());
            return excluded;
        }
    
        private List<String> getExcludeAutoConfigurationsProperty() {
            if (getEnvironment() instanceof ConfigurableEnvironment) {
                RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
                        this.environment, "spring.autoconfigure.");
                Map<String, Object> properties = resolver.getSubProperties("exclude");
                if (properties.isEmpty()) {
                    return Collections.emptyList();
                }
                List<String> excludes = new ArrayList<String>();
                for (Map.Entry<String, Object> entry : properties.entrySet()) {
                    String name = entry.getKey();
                    Object value = entry.getValue();
                    if (name.isEmpty() || name.startsWith("[") && value != null) {
                        excludes.addAll(new HashSet<String>(Arrays.asList(StringUtils
                                .tokenizeToStringArray(String.valueOf(value), ","))));
                    }
                }
                return excludes;
            }
            RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(getEnvironment(),
                    "spring.autoconfigure.");
            String[] exclude = resolver.getProperty("exclude", String[].class);
            return (Arrays.asList(exclude == null ? new String[0] : exclude));
        }
    
        private List<String> sort(List<String> configurations,
                AutoConfigurationMetadata autoConfigurationMetadata) throws IOException {
            configurations = new AutoConfigurationSorter(getMetadataReaderFactory(),
                    autoConfigurationMetadata).getInPriorityOrder(configurations);
            return configurations;
        }
    
        private List<String> filter(List<String> configurations,
                AutoConfigurationMetadata autoConfigurationMetadata) {
            long startTime = System.nanoTime();
            String[] candidates = configurations.toArray(new String[configurations.size()]);
            boolean[] skip = new boolean[candidates.length];
            boolean skipped = false;
            for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
                invokeAwareMethods(filter);
                boolean[] match = filter.match(candidates, autoConfigurationMetadata);
                for (int i = 0; i < match.length; i++) {
                    if (!match[i]) {
                        skip[i] = true;
                        skipped = true;
                    }
                }
            }
            if (!skipped) {
                return configurations;
            }
            List<String> result = new ArrayList<String>(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<String>(result);
        }
    
        protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
            return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
                    this.beanClassLoader);
        }
    
        private MetadataReaderFactory getMetadataReaderFactory() {
            try {
                return getBeanFactory().getBean(
                        SharedMetadataReaderFactoryContextInitializer.BEAN_NAME,
                        MetadataReaderFactory.class);
            }
            catch (NoSuchBeanDefinitionException ex) {
                return new CachingMetadataReaderFactory(this.resourceLoader);
            }
        }
    
        protected final <T> List<T> removeDuplicates(List<T> list) {
            return new ArrayList<T>(new LinkedHashSet<T>(list));
        }
    
        protected final List<String> asList(AnnotationAttributes attributes, String name) {
            String[] value = attributes.getStringArray(name);
            return Arrays.asList(value == null ? new String[0] : value);
        }
    
        private void fireAutoConfigurationImportEvents(List<String> configurations,
                Set<String> exclusions) {
            List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
            if (!listeners.isEmpty()) {
                AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
                        configurations, exclusions);
                for (AutoConfigurationImportListener listener : listeners) {
                    invokeAwareMethods(listener);
                    listener.onAutoConfigurationImportEvent(event);
                }
            }
        }
    
        protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
            return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,
                    this.beanClassLoader);
        }
    
        private void invokeAwareMethods(Object instance) {
            if (instance instanceof Aware) {
                if (instance instanceof BeanClassLoaderAware) {
                    ((BeanClassLoaderAware) instance)
                            .setBeanClassLoader(this.beanClassLoader);
                }
                if (instance instanceof BeanFactoryAware) {
                    ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
                }
                if (instance instanceof EnvironmentAware) {
                    ((EnvironmentAware) instance).setEnvironment(this.environment);
                }
                if (instance instanceof ResourceLoaderAware) {
                    ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
                }
            }
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
            this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
        }
    
        protected final ConfigurableListableBeanFactory getBeanFactory() {
            return this.beanFactory;
        }
    
        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.beanClassLoader = classLoader;
        }
    
        protected ClassLoader getBeanClassLoader() {
            return this.beanClassLoader;
        }
    
        @Override
        public void setEnvironment(Environment environment) {
            this.environment = environment;
        }
    
        protected final Environment getEnvironment() {
            return this.environment;
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    
        protected final ResourceLoader getResourceLoader() {
            return this.resourceLoader;
        }
    
        @Override
        public int getOrder() {
            return Ordered.LOWEST_PRECEDENCE - 1;
        }
    
    }
    View Code

      首先该类实现了DeferredImportSelector接口,这个接口继承了ImportSelector:

    /*
     * 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.core.type.AnnotationMetadata;
    
    /**
     * Interface to be implemented by types that determine which @{@link Configuration}
     * class(es) should be imported based on a given selection criteria, usually one or more
     * annotation attributes.
     *
     * <p>An {@link ImportSelector} may implement any of the following
     * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
     * methods will be called prior to {@link #selectImports}:
     * <ul>
     * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
     * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
     * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
     * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
     * </ul>
     *
     * <p>ImportSelectors are usually processed in the same way as regular {@code @Import}
     * annotations, however, it is also possible to defer selection of imports until all
     * {@code @Configuration} classes have been processed (see {@link DeferredImportSelector}
     * for details).
     *
     * @author Chris Beams
     * @since 3.1
     * @see DeferredImportSelector
     * @see Import
     * @see ImportBeanDefinitionRegistrar
     * @see Configuration
     */
    public interface ImportSelector {
    
        /**
         * Select and return the names of which class(es) should be imported based on
         * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
         */
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    
    }
    View Code

      该接口主要是为了导入@Configuration的配置项,而DeferredImportSelector是延期导入,当所有的@Configuration都处理过后才会执行。

      回过头来我们看一下AutoConfigurationImportSelector的selectImport方法:

    @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            try {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                        .loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = getAttributes(annotationMetadata);
                List<String> configurations = getCandidateConfigurations(annotationMetadata,
                        attributes);
                configurations = removeDuplicates(configurations);
                configurations = sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = getExclusions(annotationMetadata, attributes);
                checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = filter(configurations, autoConfigurationMetadata);
                fireAutoConfigurationImportEvents(configurations, exclusions);
                return configurations.toArray(new String[configurations.size()]);
            }
            catch (IOException ex) {
                throw new IllegalStateException(ex);
            }
        }

      该方法刚开始会先判断是否进行自动装配,而后会从META-INF/spring-autoconfigure-metadata.properties读取元数据与元数据的相关属性,紧接着会调用getCandidateConfigurations方法:

    /**
         * Return the auto-configuration class names that should be considered. By default
         * this method will load candidates using {@link SpringFactoriesLoader} with
         * {@link #getSpringFactoriesLoaderFactoryClass()}.
         * @param metadata the source metadata
         * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
         * attributes}
         * @return a list of candidate configurations
         */
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
                AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                    getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
            Assert.notEmpty(configurations,
                    "No auto configuration classes found in META-INF/spring.factories. If you "
                            + "are using a custom packaging, make sure that file is correct.");
            return configurations;
        }
    
        /**
         * Return the class used by {@link SpringFactoriesLoader} to load configuration
         * candidates.
         * @return the factory class
         */
        protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }

      在这里又遇到我们的老熟人了--SpringFactoryiesLoader, 它会读取META-INF/spring.factories下的EnableAutoConfiguration的配置,紧接着在进行排除与过滤,进而得到需要装配的类。最后让所有配置在META-INF/spring.factories下的AutoConfigurationImportListener执行AutoConfigurationImportEvent事件,代码如下:

    private void fireAutoConfigurationImportEvents(List<String> configurations,
                Set<String> exclusions) {
            List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
            if (!listeners.isEmpty()) {
                AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
                        configurations, exclusions);
                for (AutoConfigurationImportListener listener : listeners) {
                    invokeAwareMethods(listener);
                    listener.onAutoConfigurationImportEvent(event);
                }
            }
        }
    
        protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
            return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,
                    this.beanClassLoader);
        }

    二、何时进行自动装配

      在前面的环节里只是最终要确定哪些类需要被装配,在SpringBoot时何时处理这些自动装配的类呢?下面我们简要的分析一下:

    2.1、AbstractApplicationContext的refresh方法:

      这个方法老生常谈了其中请大家关注一下这个方法:

    // Invoke factory processors registered as beans in the context.
      invokeBeanFactoryPostProcessors(beanFactory);

      在这里是处理BeanFactoryPostProcessor的,那么我们在来看一下这个接口BeanDefinitionRegistryPostProcessor:

    /*
     * Copyright 2002-2010 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.beans.factory.support;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    
    /**
     * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
     * the registration of further bean definitions <i>before</i> regular
     * BeanFactoryPostProcessor detection kicks in. In particular,
     * BeanDefinitionRegistryPostProcessor may register further bean definitions
     * which in turn define BeanFactoryPostProcessor instances.
     *
     * @author Juergen Hoeller
     * @since 3.0.1
     * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
     */
    public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    
        /**
         * Modify the application context's internal bean definition registry after its
         * standard initialization. All regular bean definitions will have been loaded,
         * but no beans will have been instantiated yet. This allows for adding further
         * bean definitions before the next post-processing phase kicks in.
         * @param registry the bean definition registry used by the application context
         * @throws org.springframework.beans.BeansException in case of errors
         */
        void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
    
    }
    View Code

      该接口继承了BeanFactoryPostProcessor。

    2.2、ConfigurationClassPostProcessor 类

      该类主要处理@Configuration注解的,它实现了BeanDefinitionRegistryPostProcessor,  那么也间接实现了BeanFactoryPostProcessor,关键代码如下:

      

    @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
            int factoryId = System.identityHashCode(beanFactory);
            if (this.factoriesPostProcessed.contains(factoryId)) {
                throw new IllegalStateException(
                        "postProcessBeanFactory already called on this post-processor against " + beanFactory);
            }
            this.factoriesPostProcessed.add(factoryId);
            if (!this.registriesPostProcessed.contains(factoryId)) {
                // BeanDefinitionRegistryPostProcessor hook apparently not supported...
                // Simply call processConfigurationClasses lazily at this point then.
                processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
            }
    
            enhanceConfigurationClasses(beanFactory);
            beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
        }
    
    /**
         * Build and validate a configuration model based on the registry of
         * {@link Configuration} classes.
         */
        public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
            //.....省略部分代码
    
            // Parse each @Configuration class
            ConfigurationClassParser parser = new ConfigurationClassParser(
                    this.metadataReaderFactory, this.problemReporter, this.environment,
                    this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
            Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
            do {
                parser.parse(candidates);
                parser.validate();
    
                Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
                configClasses.removeAll(alreadyParsed);
    
                // 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);
                alreadyParsed.addAll(configClasses);
    
                candidates.clear();
                if (registry.getBeanDefinitionCount() > candidateNames.length) {
                    String[] newCandidateNames = registry.getBeanDefinitionNames();
                    Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
                    Set<String> alreadyParsedClasses = new HashSet<String>();
                    for (ConfigurationClass configurationClass : alreadyParsed) {
                        alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }
                    for (String candidateName : newCandidateNames) {
                        if (!oldCandidateNames.contains(candidateName)) {
                            BeanDefinition bd = registry.getBeanDefinition(candidateName);
                            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                    !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                                candidates.add(new BeanDefinitionHolder(bd, candidateName));
                            }
                        }
                    }
                    candidateNames = newCandidateNames;
                }
            }
            while (!candidates.isEmpty());
        // ....省略部分代码
        }    

    其实这里注释已经很清楚了,我们可以清楚的看到解析每一个@ConfigurationClass的关键类是:ConfigurationClassParser,那么我们继续看一看这个类的parse方法:

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

      在这里大家留意一下最后一句processDeferredImportSelectors方法,在这里将会对DeferredImportSelector进行处理,这样我们就和AutoConfigurationSelectImporter结合到一起了:

    private void processDeferredImportSelectors() {
            List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            this.deferredImportSelectors = null;
            Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
    
            for (DeferredImportSelectorHolder deferredImport : deferredImports) {
                ConfigurationClass configClass = deferredImport.getConfigurationClass();
                try {
                    String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
                    processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
                }
                catch (BeanDefinitionStoreException ex) {
                    throw ex;
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to process import candidates for configuration class [" +
                            configClass.getMetadata().getClassName() + "]", ex);
                }
            }
        }

    请大家关注这句代码:String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());在这里deferredImport的类型为DeferredImportSelectorHolder:

    private static class DeferredImportSelectorHolder {
    
            private final ConfigurationClass configurationClass;
    
            private final DeferredImportSelector importSelector;
    
            public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
                this.configurationClass = configClass;
                this.importSelector = selector;
            }
    
            public ConfigurationClass getConfigurationClass() {
                return this.configurationClass;
            }
    
            public DeferredImportSelector getImportSelector() {
                return this.importSelector;
            }
        }

      在这个内部类里持有了一个DeferredImportSelector的引用,至此将会执行自动装配的所有操作

    三、总结

      1)自动装配还是利用了SpringFactoriesLoader来加载META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它会经过exclude和filter等操作,最终确定要装配的类

      2)  处理@Configuration的核心还是ConfigurationClassPostProcessor,这个类实现了BeanFactoryPostProcessor, 因此当AbstractApplicationContext执行refresh方法里的invokeBeanFactoryPostProcessors(beanFactory)方法时会执行自动装配

  • 相关阅读:
    CSS3 3D Transform
    js关闭当前页面(窗口)的几种方式总结
    JavaScript书籍阅读
    使用@media screen解决移动web开发的多分辨率问题
    ie678 兼容问题
    JS动态引入js,CSS——动态创建script/link/style标签
    扁平化设计的流行配色方案
    最简单的jquery轮播图
    ie7 z-index 失效问题
    js正则表达式手机号(邮箱)验证
  • 原文地址:https://www.cnblogs.com/niechen/p/9027804.html
Copyright © 2011-2022 走看看