深入淺析SpringBoot中的自動裝配
SpringBoot的自動裝配是拆箱即用的基礎,也是微服務化的前提。這次主要的議題是,來看看它是怎么樣實現(xiàn)的,我們透過源代碼來把握自動裝配的來龍去脈。
一、自動裝配過程分析
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
@Configuration
public @interface SpringBootConfiguration {
}由此我們可以推斷出@SpringBootApplication等同于@Configuration @ComponentScan @EnableAutoConfiguration
1.2、@EnableAutoConfiguration
一旦加上此注解,那么將會開啟自動裝配功能,簡單點講,Spring會試圖在你的classpath下找到所有配置的Bean然后進行裝配。當然裝配Bean時,會根據(jù)若干個(Conditional)定制規(guī)則來進行初始化。我們看一下它的源碼:
/*
* 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 {};
}雖然根據(jù)文檔注釋的說明它指點我們?nèi)タ碋nableAutoConfigurationImportSelector。但是該類在SpringBoot1.5.X版本已經(jīng)過時了,因此我們看一下它的父類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("\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);
}
}
}
@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;
}
}首先該類實現(xiàn)了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都處理過后才會執(zhí)行。
回過頭來我們看一下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讀取元數(shù)據(jù)與元數(shù)據(jù)的相關屬性,緊接著會調(diào)用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執(zhí)行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);
}二、何時進行自動裝配
在前面的環(huán)節(jié)里只是最終要確定哪些類需要被裝配,在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注解的,它實現(xiàn)了BeanDefinitionRegistryPostProcessor, 那么也間接實現(xiàn)了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());
// ....省略部分代碼
} 其實這里注釋已經(jīng)很清楚了,我們可以清楚的看到解析每一個@ConfigurationClass的關鍵類是:ConfigurationClassParser,那么我們繼續(xù)看一看這個類的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;
}
}在這個內(nèi)部類里持有了一個DeferredImportSelector的引用,至此將會執(zhí)行自動裝配的所有操作
三、總結
1)自動裝配還是利用了SpringFactoriesLoader來加載META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它會經(jīng)過exclude和filter等操作,最終確定要裝配的類
2) 處理@Configuration的核心還是ConfigurationClassPostProcessor,這個類實現(xiàn)了BeanFactoryPostProcessor, 因此當AbstractApplicationContext執(zhí)行refresh方法里的invokeBeanFactoryPostProcessors(beanFactory)方法時會執(zhí)行自動裝配
以上所述是小編給大家介紹的SpringBoot中的自動裝配,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!
相關文章
java 定義長度為0的數(shù)組/空數(shù)組案例
這篇文章主要介紹了java 定義長度為0的數(shù)組/空數(shù)組案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
java中String、StringBuffer與StringBuilder的區(qū)別
這篇文章主要介紹了java 中String和StringBuffer與StringBuilder的區(qū)別,在開發(fā)過程中經(jīng)常會用到String這個類進行操作。需要的朋友可以收藏下,方便下次瀏覽觀看2021-12-12
SpringBoot整合Elasticsearch實現(xiàn)索引和文檔的操作方法
Elasticsearch 基于 Apache Lucene 構建,采用 Java 編寫,并使用 Lucene 構建索引、提供搜索功能,本文分步驟通過綜合案例給大家分享SpringBoot整合Elasticsearch的相關知識,感興趣的朋友跟隨小編一起看看吧2021-05-05
SpringBoot 項目如何在tomcat容器中運行的實現(xiàn)方法
這篇文章主要介紹了SpringBoot 項目如何在tomcat容器中運行的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
Spring中的SpringApplicationRunListener詳細解析
這篇文章主要介紹了Spring中的SpringApplicationRunListener詳細解析,SpringApplicationRunListener是一個監(jiān)聽SpringApplication中run方法的接口,在項目啟動過程的各個階段進行事件的發(fā)布,需要的朋友可以參考下2023-11-11
SpringBoot全局異常處理之多個處理器匹配順序(最新推薦)
這篇文章主要介紹了SpringBoot全局異常處理之多個處理器匹配順序(最新推薦),調(diào)試源碼可見匹配順序為:異常層級高者優(yōu)先,再清楚點,子類異常處理器優(yōu)先,本文給大家介紹的非常詳細,感興趣的朋友一起看看吧2024-03-03
java圖的深度優(yōu)先遍歷實現(xiàn)隨機生成迷宮
這篇文章主要為大家詳細介紹了java圖的深度優(yōu)先遍歷實現(xiàn)隨機生成迷宮,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01

