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 | |
*/ | |
public SpringBootApplication { | |
/** | |
* 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 {}; | |
/** | |
* 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 | |
*/ | |
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 | |
*/ | |
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 | |
*/ | |
public SpringBootConfiguration { | |
} |
由此我们可以推断出@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 | |
*/ | |
public 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 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; | |
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("\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[] : 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 = ; 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 = ; 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[] : 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); | |
} | |
} | |
} | |
public void setBeanFactory(BeanFactory beanFactory) throws BeansException { | |
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory); | |
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; | |
} | |
protected final ConfigurableListableBeanFactory getBeanFactory() { | |
return this.beanFactory; | |
} | |
public void setBeanClassLoader(ClassLoader classLoader) { | |
this.beanClassLoader = classLoader; | |
} | |
protected ClassLoader getBeanClassLoader() { | |
return this.beanClassLoader; | |
} | |
public void setEnvironment(Environment environment) { | |
this.environment = environment; | |
} | |
protected final Environment getEnvironment() { | |
return this.environment; | |
} | |
public void setResourceLoader(ResourceLoader resourceLoader) { | |
this.resourceLoader = resourceLoader; | |
} | |
protected final ResourceLoader getResourceLoader() { | |
return this.resourceLoader; | |
} | |
public int getOrder() { | |
return Ordered.LOWEST_PRECEDENCE - ; | |
} | |
} |
首先该类实现了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
都处理过后才会执行。
回过头来我们看一下AutoConfigurationImportSelector的selectImport方法:
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; | |
} |
该接口继承了BeanFactoryPostProcessor。
2.2、ConfigurationClassPostProcessor 类
该类主要处理@Configuration
注解的,它实现了BeanDefinitionRegistryPostProcessor, 那么也间接实现了BeanFactoryPostProcessor,关键代码如下:
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)
方法时会执行自动装配