
1 深入理解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 {};}
这里面包含了@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@Configurationpublic @interface SpringBootConfiguration {}
由此我们可以推断出@SpringBootApplication等同于@Configuration @ComponentScan @EnableAutoConfiguration。更多相关内容:SpringBoot内容聚合
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 {};}
虽然根据文档注释的说明它指点我们去看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 AutoConfigurationImportSelectorimplements 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;@Overridepublic 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("\t- ").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);}}}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}protected final ConfigurableListableBeanFactory getBeanFactory() {return this.beanFactory;}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.beanClassLoader = classLoader;}protected ClassLoader getBeanClassLoader() {return this.beanClassLoader;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}protected final Environment getEnvironment() {return this.environment;}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}protected final ResourceLoader getResourceLoader() {return this.resourceLoader;}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE - 1;}}
首先该类实现了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);}
该接口主要是为了导入@Configuration的配置项,而DeferredImportSelector是延期导入,当所有的@Configuration都处理过后才会执行。更多相关内容:SpringBoot内容聚合
回过头来我们看一下AutoConfigurationImportSelector的selectImport方法:
@Overridepublic 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;}
该接口继承了BeanFactoryPostProcessor。更多相关内容:SpringBoot内容聚合
2.2、ConfigurationClassPostProcessor 类
该类主要处理@Configuration注解的,它实现了BeanDefinitionRegistryPostProcessor, 那么也间接实现了BeanFactoryPostProcessor,关键代码如下:
@Overridepublic 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 classConfigurationClassParser 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 contentif (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)方法时会执行自动装配
如有收获请划至底部
点击“在看”支持,谢谢!
关注马士兵
每天分享技术干货
点赞是最大的支持








