深入浅析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
@Configuration
public @interface 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
 */
@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 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;
 }
}

  首先该类实现了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方法:

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

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

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

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

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

二、何时进行自动装配

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

2.1、AbstractApplicationContext的refresh方法:

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

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

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

/*
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.beans.factory.support;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
/**
 * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
 /**
 * Modify the application context's internal bean definition registry after its
 * standard initialization. All regular bean definitions will have been loaded,
 * but no beans will have been instantiated yet. This allows for adding further
 * bean definitions before the next post-processing phase kicks in.
 * @param registry the bean definition registry used by the application context
 * @throws org.springframework.beans.BeansException in case of errors
 */
 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

  该接口继承了BeanFactoryPostProcessor。

2.2、ConfigurationClassPostProcessor 类

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

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

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

public void parse(Set<BeanDefinitionHolder> configCandidates) {
 this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
 for (BeanDefinitionHolder holder : configCandidates) {
  BeanDefinition bd = holder.getBeanDefinition();
  try {
  if (bd instanceof AnnotatedBeanDefinition) {
   parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  }
  else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
   parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
  }
  else {
   parse(bd.getBeanClassName(), holder.getBeanName());
  }
  }
  catch (BeanDefinitionStoreException ex) {
  throw ex;
  }
  catch (Throwable ex) {
  throw new BeanDefinitionStoreException(
   "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  }
 }
 processDeferredImportSelectors();
 }

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

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

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

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

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

三、总结

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

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

以上所述是小编给大家介绍的SpringBoot中的自动装配,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • Java注解机制之Spring自动装配实现原理详解

    Java中使用注解的情况主要在SpringMVC(Spring Boot等),注解实际上相当于一种标记语言,它允许你在运行时动态地对拥有该标记的成员进行操作.注意:spring框架默认不支持自动装配的,要想使用自动装配需要修改spring配置文件中<bean>标签的autowire属性. 自动装配属性有6个值可选,分别代表不同的含义: byName ->从Spring环境中获取目标对象时,目标对象中的属性会根据名称在整个Spring环境中查找<bean>标签的id属性值.如果

  • Spring 自动装配的二义性实例解析

    这篇文章主要介绍了Spring 自动装配的二义性实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.我们知道可以用Spring的自动装配(@Autowired)将Bean应用注入到构造参数和属性中,但是,注意了,仅有一个bean匹配需要的结果时,自动装配才可以生效.如果有多个bean匹配同一个结果,这种歧义性会阻碍Spring自动装配属性,构造参数或方法参数. 大白话说一下,就如我们有一个甜片接口(Dessert)里面有一个好吃的方法(

  • 基于XML配置Spring的自动装配过程解析

    一.了解Spring自动装配的方式 采用传统的XML方式配置Bean组件的关键代码如下所示 <bean id="userMapper" class="edu.cn.dao.UserMapperImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <bean id="userSer

  • Spring的自动装配Bean的三种方式

    spring的自动装配功能的定义:无须在Spring配置文件中描述javaBean之间的依赖关系(如配置<property>.<constructor-arg>).IOC容器会自动建立javabean之间的关联关系. 如果没有采用自动装配的话,手动装配我们通常在配置文件中进行实现:一下代码就是手动装配: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="ht

  • 彻底搞明白Spring中的自动装配和Autowired注解的使用

    一.自动装配 当Spring装配Bean属性时,有时候非常明确,就是需要将某个Bean的引用装配给指定属性.比如,如果我们的应用上下文中只有一个org.mybatis.spring.SqlSessionFactoryBean类型的Bean,那么任意一个依赖SqlSessionFactoryBean的其他Bean就是需要这个Bean.毕竟这里只有一个SqlSessionFactoryBean的Bean. 为了应对这种明确的装配场景,Spring提供了自动装配(autowiring).与其显式的装配

  • 详解Spring Boot自动装配的方法步骤

    在<Spring Boot Hello World>中介绍了一个简单的spring boot例子,体验了spring boot中的诸多特性,其中的自动配置特性极大的简化了程序开发中的工作(不用写一行XML).本文我们就来看一下spring boot是如何做到自动配置的. 首先阐明,spring boot的自动配置是基于spring framework提供的特性实现的,所以在本文中,我们先介绍spring framework的相关特性,在了解了这些基础知识后,我们再来看spring boot的自

  • 基于Spring@Autowired注解与自动装配详谈

    1 配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss 拥有 Office 和 Car 类型的两个属性: 清单 3. Boss.java package com.baobaotao; public class Boss { private Car car; private Office office; // 省略 get/setter @Override p

  • 深入浅析SpringBoot中的自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉. 一.自动装配过程分析 1.1.关于@SpringBootApplication 我们在编写SpringBoot项目时,@SpringBootApplication是最常见的注解了,我们可以看一下源代码: /* * Copyright 2012-2017 the original author or authors. * * Licensed un

  • 浅析SpringBoot中使用thymeleaf找不到.HTML文件的原因

    thymeleaf是目前最新的模板引擎,它和springboot完美搭配,让前后端不在疏远 首先先介绍一下市面上出现过的模板引擎 1, Verlocity 距今有10多年之久,是由Apache推出的 Struts2 底层使用2,Freemarker,距今有10多年之久, 第三方小公司推出,后来得到了Apache的认可,在Struts2这个框架中, 底层使用Freemarker. SpringBoot 默认使用Freemarker是以.ftl结尾的文件. 3,Thymeleaf是以.html结尾的

  • springboot如何实现自动装配源码解读

    Spring Boot 自动装配 最重要的注解@SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, c

  • SpringBoot详细分析自动装配原理并实现starter

    目录 约定优于配置 自动装配 手写一个starter组件 约定优于配置 SpringBoot的预定优于配置主要体现在以下几个方面: maven的目录结构: 配置文件默认存放在resources目录下 项目编译后的文件存放在target目录下 项目默认打包成jar格式 配置文件默认为application.yml或application.yaml或application.properties 默认通过 spring.profiles.active 属性来决定运行环境时的配置文件. 自动装配 相对于

  • 深入浅析springboot中static和templates区别

    静态页面的return默认是跳转到/static/目录下,当在pom.xml中引入了thymeleaf组件,动态跳转会覆盖默认的静态跳转,默认就会跳转到/templates/下,注意看两者return代码也有区别,动态没有html后缀. 1.1 在static下新建hello1.html 运行程序,浏览器输入http://localhost:8080/hello1.html so,可以在根目录下访问hello1.html,static目录类似于传统Java web中的webroot或webcon

  • Springboot框架实现自动装配详解

    目录 序言 从程序的使用去入手分析 序言 springboot框架价值,可以简单快速的构建独立的spring生产级别应用.springboot主要有以下的特性: 1.创建独立的Spring应用 2.直接嵌入Tomcat等Web容器(不需要部署WAR文件) 3.提供固化的“starter”依赖,简化构建配置 4.当条码满足时自动装配Spring货第三方类库 5.提供运维的特性,如指标信息,健康检查和外部配置 6.不需要XML配置. 下面就以springboot启动的时候,是如何实现mybatis自

  • springboot中关于自动建表,无法更新字段的问题

    目录 关于自动建表,无法更新字段问题 Springboot中的配置 除了create配置,还有以下几种 springbootjpa未自动建表问题记录 检查pom是否正确引入对应模块 检查application文件配置是否存在错误 关于自动建表,无法更新字段问题 Springboot中的配置 jpa:     database-platform: org.hibernate.dialect.MySQL5InnoDBDialect     database: MYSQL     show-sql:

  • Springboot中如何自动转JSON输出

    目录 Springboot是如此的暴力 原因 Springboot自动转json的出现问题 问题复现 问题分析及解决办法 Springboot是如此的暴力 在Controller中注解为@RestController类.@GetMapping或其他Mapping注解方法后 会自动转成JSON格式… 后台打印 {msg=已查询到163条数据, data_size=163, code=0, data=[Tersga{aname='万家铺村', sum=10, run=10, down=0, req=

  • SpringBoot启动及自动装配原理过程详解

    一.servlet2(老spring-mvc) 配置文件: web.xml:主要配置项目启动项 application-context.xml:主要配置项目包扫描.各种bean.事务管理 springMVC.xml:主要配置controller包扫描.视图解析器.参数解析器 启动过程: 每一个spring项目启动时都需要初始化spring-context,对于非web项目可以在程序main方法中触发这个context的初始化过程. 由于web项目的启动入口在容器,所以开发者不能直接触发sprin

随机推荐