深入浅析SpringBoot中的自动装配

网友投稿 174 2023-02-05


深入浅析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://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.

*

* 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://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).

*

* Application should only ever include one {@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://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).

*

* When using {@link SpringBootApplication}, the auto-configuration of the context is

* automatically enabled and adding this annotation has therefore no additional effect.

*

* 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.

*

* 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.

*

* 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://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 configurations = getCandidateConfigurations(annotationMetadata,

attributes);

configurations = removeDuplicates(configurations);

configurations = sort(configurations, autoConfigurationMetadata);

Set 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 getCandidateConfigurations(AnnotationMetadata metadata,

AnnotationAttributes attributes) {

List 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 configurations,

Set exclusions) {

List invalidExcludes = new ArrayList(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 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 getExclusions(AnnotationMetadata metadata,

AnnotationAttributes attributes) {

Set excluded = new LinkedHashSet();

excluded.addAll(asList(attributes, "exclude"));

excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));

excluded.addAll(getExcludeAutoConfigurationsProperty());

return excluded;

}

private List getExcludeAutoConfigurationsProperty() {

if (getEnvironment() instanceof ConfigurableEnvironment) {

RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(

this.environment, "spring.autoconfigure.");

Map properties = resolver.getSubProperties("exclude");

if (properties.isEmpty()) {

return Collections.emptyList();

}

List excludes = new ArrayList();

for (Map.Entry entry : properties.entrySet()) {

String name = entry.getKey();

Object value = entry.getValue();

if (name.isEmpty() || name.startsWith("[") && value != null) {

excludes.addAll(new HashSet(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 sort(List configurations,

AutoConfigurationMetadata autoConfigurationMetadata) throws IOException {

configurations = new AutoConfigurationSorter(getMetadataReaderFactory(),

autoConfigurationMetadata).getInPriorityOrder(configurations);

return configurations;

}

private List filter(List 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 result = new ArrayList(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(result);

}

protected List 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 List removeDuplicates(List list) {

return new ArrayList(new LinkedHashSet(list));

}

protected final List asList(AnnotationAttributes attributes, String name) {

String[] value = attributes.getStringArray(name);

return Arrays.asList(value == null ? new String[0] : value);

}

private void fireAutoConfigurationImportEvents(List configurations,

Set exclusions) {

List listeners = getAutoConfigurationImportListeners();

if (!listeners.isEmpty()) {

AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,

configurations, exclusions);

for (AutoConfigurationImportListener listener : listeners) {

invokeAwareMethods(listener);

listener.onAutoConfigurationImportEvent(event);

}

}

}

protected List 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://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.sprDxECZwingframework.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.

*

*

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}:

*

*

*

*

*

*

*

*

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 configurations = getCandidateConfigurations(annotationMetadata,

attributes);

configurations = removeDuplicates(configurations);

configurations = sort(configurations, autoConfigurationMetadata);

Set 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 getCandidateConfigurations(AnnotationMetadata metadata,

AnnotationAttributes attributes) {

List 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 configurations,

Set exclusions) {

List listeners = getAutoConfigurationImportListeners();

if (!listeners.isEmpty()) {

AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,

configurations, exclusions);

for (AutoConfigurationImportListener listener : listeners) {

invokeAwareMethods(listener);

listener.onAutoConfigurationImportEvent(event);

}

}

}

protected List 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://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 before 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 candidates = new LinkedHashSet(configCandidates);

Set alreadyParsed = new HashSet(configCandidates.size());

do {

parser.parse(candidates);

parser.validate();

Set configClasses = new LinkedHashSet(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 oldCandidateNames = new HashSet(Arrays.asList(candidateNames));

Set alreadyParsedClasses = new HashSet();

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 configCandidates) {

this.deferredImportSelectors = new LinkedList();

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 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中的自动装配,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,会及时回复大家的!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java 线程几种状态(java线程几种状态)
下一篇:Spring Cloud Config RSA简介及使用RSA加密配置文件的方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~