springboot实战-自动配置

目录

前言

自定义配置实现

自定义组件测试

源码分析

一、启动类

二、SpringApplication的prepareContext

三、SpringApplication的refreshContext


前言

我们在springboot项目开发过程中,可能存在多个项目需要用到同一个功能,这时候不同的项目组实现这个功能都采用了不同的方式,同时后期需要各自项目组进行代码维护升级,这样会导致代码质量不一致、重复造轮子、浪费人力等问题。

遇到上面的问题,需要思考下此功能是否具备通用性,如第三方的接入、中间件的接入、工具类等,如果具备,那么我们可以把这一块功能抽离出来,做成公共组件,在springboot的项目中,我们就可以采用自定义starter组件的方式实现公共组件。

自定义配置实现

1、创建自定义配置starter项目,目录如下:

2、pom.xml引入maven包spring-boot-autoconfigure

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sam-spring-boot</artifactId>
        <groupId>com.sam.project</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>my-spring-boot-starter</artifactId>

    <name>my-spring-boot-starter</name>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
    </dependencies>

</project>

这里要注意,如果要做成组件可以maven打包出来供其他应用引入使用,packaging要设置成jar

3、创建一个User类,因为是例子,这里就只设置一个属性name

package com.sam.project.my.domain;

public class User {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4、创建属性类:MyProperty,负责从application.properties文件中获取配置的属性值

package com.sam.project.my.config;

import org.springframework.boot.context.properties.ConfigurationProperties;


@ConfigurationProperties(prefix = "my.user")
public class MyProperty {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

@ConfigurationProperties(prefix = "my.user")注解就是标识要匹配前缀是my.user的值,name对应的是my.user.name=xxxx的值

5、编写配置类:MyAutoConfiguration,这是关键类

package com.sam.project.my;

import com.sam.project.my.config.MyProperty;
import com.sam.project.my.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(MyProperty.class)
public class MyAutoConfiguration {

    @Autowired
    private MyProperty myProperty;

    @Bean
    @ConditionalOnMissingBean(User.class)
    public User user(){
        User user = new User();
        user.setName(myProperty.getName());
        return user;
    }

}
  • @Configuration 标识这个类是配置类,类似于spring的xml配置文件的作用;
  • @EnableConfigurationProperties(MyProperty.class) 看到Enable的前缀就猜出,只有加上这个配置,才能让MyProperty的类初始化自动填充属性,下面就可以用@Autowired获取这个bean来使用
  • @Bean 就是表示这个方法创建的User作为bean注入到IOC容器;
  • @ConditionalOnMissingBean 表示当容器中没有User这个Bean时才执行这个方法新建一个User的Bean;

6、在resources资源文件夹下面,创建文件夹META-INF,然后创建文件spring.factories(路径是/resources/META-INF/spring.factories)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sam.project.my.MyAutoConfiguration

到此,我们就已经创建好组件了,然后使用maven执行install命令打包到本地仓库中就可以;

自定义组件测试

1、创建一个springboot的web项目,然后pom中引入上面的my-spring-boot-starter组件maven包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>sam-spring-boot</artifactId>
        <groupId>com.sam.project</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>web-test-spring-boot-start</artifactId>
    <packaging>war</packaging>

    <name>web-test-spring-boot-start</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.sam.project</groupId>
            <artifactId>my-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <finalName>sam-project-web</finalName>
    </build>

</project>

2、编写application.properties文件

server.port=8080
server.servlet.context-path=/sam/web

my.user.name=sam

3、编写测试controller类

package com.sam.project.web.controller;

import com.sam.project.my.domain.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping(value = "/test", produces = "application/json; charset=utf-8")
public class TestController {


    @Resource
    private User user;

    @GetMapping(value = "/name/get")
    public String getUserName() {
        return "hello " + user.getName();
    }

}

这里就可以获取User里面的值为sam

4、编写启动类SamWebApplication

package com.sam.project.web;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SamWebApplication {
    public static void main(String[] args) {
        SpringApplication.run(SamWebApplication.class, args);
    }
}

5、测试

启动运行后,在浏览器输入http://localhost:8080/sam/web/test/name/get

 

源码分析

一、启动类

先从启动类来分析如何加载组件

@SpringBootApplication
public class MyApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApiApplication.class, args);
    }
}

通过jar命令启动的时候,执行run方法

    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}


	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

new SpringApplication(primarySources)执行初始化,这里进去,这里的primarySource是class com.sam.project.web.SamWebApplication,后面就会根据这个primarySource进行解析。

    public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}


	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		......
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		......
	}

我们关注setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))里面的方法getSpringFactoriesInstances(ApplicationContextInitializer.class)

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

进入方法SpringFactoriesLoader.loadFactoryNames(type, classLoader)

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

我们可以看到调用的第二个loadFactoryNames方法里面的代码classLoader.getResources("META-INF/spring.factories")会加载项目里面所有resources资源文件夹下的META-INF/spring.factories文件,然后解析spring.factories,获取里面的keyValue格式的Properties值,最后保存到Map<ClassLoader, MultiValueMap<String, String>> cache中,其中ClassLoader是用来区分加载器,MultiValueMap里面的内容我们可以看一下:

其中EnableAutoConfiguration的map里面value就有我们后续自己实现在spring.factories设置的自定义组件的初始化配置类的全路径类名称。

(注意,这里只是全量预缓存,还没有真正用到EnableAutoConfiguration里面的value列表进行实例化,目前还是执行者其中的获取ApplicationContextInitializer的value里面的类的实例化)

二、SpringApplication的prepareContext

接着就是执行new SpringApplication(primarySources).run(args)的方法中执行的prepareContext进行容器准备,一直跟到最后的load方法:

SpringApplication {
	run(...) {
		// 准备context
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
	}

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		......
		// 这里获取到了之前primarySource的值class com.sam.project.web.SamWebApplication,然后执行load加载
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		......
	}
	
	protected void load(ApplicationContext context, Object[] sources) {
		......
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		loader.load();
	}
}

进去到BeanDefinitionLoader这个加载BeanDefinition的类方法中,这里主要就是对SamWebApplication类型的Source进行加载


BeanDefinitionLoader {
	int load() {
		int count = 0;
		for (Object source : this.sources) {
		//class com.sam.project.web.SamWebApplication,执行load加载
			count += load(source);
		}
		return count;
	}
	
	private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		......
	}
	private int load(Class<?> source) {
		......
		if (isEligible(source)) {
		// 调用AnnotatedBeanDefinitionReader的注册
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}
}

this.annotatedReader.register(source)注册调用的就是AnnotatedBeanDefinitionReader类的注册方法,将SamWebApplication和BeanDefinition的映射注册进AnnotationConfigServletWebServerApplicationContext容器里面

AnnotatedBeanDefinitionReader {
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}
	public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null, null);
	}
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {
		// 这里解析com.sam.project.web.SamWebApplication的class获取metaData基础数据,得到BeanDefinition
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		......
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		// 将SamWebApplication和BeanDefinition的映射注册进AnnotationConfigServletWebServerApplicationContext里面
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
}

三、SpringApplication的refreshContext

执行完prepareContext准备工作后,接着会执行

1、refreshContext(context)刷新容器方法

SpringApplication {
	run(...) {
		// 1、刷新context
		refreshContext(context);
	}
	private void refreshContext(ConfigurableApplicationContext context) {
		refresh((ApplicationContext) context);
		......
	}
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
		refresh((ConfigurableApplicationContext) applicationContext);
	}
	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}
}

2、applicationContext的refresh方法使用的是抽象类AbstractApplicationContext类中的refresh方法;

3、实例化和调用所有 BeanFactoryPostProcessor, BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它

4、getBeanFactoryPostProcessors()获取当前已加载的三个Processors:

  • org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
  • org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
  • org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

AbstractApplicationContext {
	// 2、调用到抽象类的刷新方法
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
				......
				// 3、实例化和调用所有 BeanFactoryPostProcessor, BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它
				invokeBeanFactoryPostProcessors(beanFactory);
				......
	}
	
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		// 4、获取当前已加载的三个Processors:
		//[org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor, //org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor, //org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor]
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
		......
	}

}

5、PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors负责按照优先级处理BeanDefinitionRegistryPostProcessors,先处理org.springframework.context.annotation.internalConfigurationAnnotationProcessor。这里第一个优先处理是currentRegistryProcessors=ConfigurationClassPostProcessor这个用于处理配置类的Processor。

6、调用ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法注册;

PostProcessorRegistrationDelegate {
	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		// 5、按照优先级处理BeanDefinitionRegistryPostProcessors,先处理org.springframework.context.annotation.internalConfigurationAnnotationProcessor
		// 这里第一次处理是currentRegistryProcessors=ConfigurationClassPostProcessor
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		......
	}
	private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
		for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
		// 6、调用ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法注册;
			postProcessor.postProcessBeanDefinitionRegistry(registry);
		}
	}
}

7、遍历candidateNames,如果是属于自定义的@Configuration注解修饰的配置类的configCandidates(这里只有我们的启动类samWebApplication符合,其他都是系统的),就加到configCandidates中;接下来执行的就是解析每一个@Configuration注解的类,这里只有一个samWebApplication需要解析,执行方法parser.parse(candidates);

ConfigurationClassPostProcessor {
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		......
		processConfigBeanDefinitions(registry);
	}
	
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();
		// 7、下面的省略代码是遍历candidateNames,如果是属于自定义的@Configuration注解修饰的配置类的configCandidates(这里只有我们的启动类samWebApplication符合,其他都是系统的),就加到configCandidates中
		......
		// 解析每一个@Configuration注解的类,这里只有一个samWebApplication需要解析
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> 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());
			}
			// 18、这个方法主要是把前面解析出来的配置类中的@Bean注解的方法解析成beanDefinition都注册到容器中
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> 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;
			}
		}
		......
	}
}

8、第7步得到的com.sam.project.web.SamWebApplication的BeanDefinitionHolder属于AnnotatedBeanDefinition,调用这里解析;

9、处理校验SamWebApplication的注解,并组装这个的SourceClass资源类

10、循环递归处理

11、doProcessConfigurationClass方法执行:如果是有注解@Compnent内部类,并且是配置类,就执行processConfigurationClass方法递归;

12、处理有注解@PropertySource的属性

13、处理有注解@ComponentScan

14、处理有@Import的注解

  • getImports(sourceClass)遍历SamWebApplication启动资源类的所有注解,获取Import注解的资源类AutoConfigurationPackages和AutoConfigurationImportSelector
  • AutoConfigurationPackages添加到SamWebApplication的ConfigurationClass类的importBeanDefinitionRegistrars中
  • AutoConfigurationImportSelector实例化成ImportSelector后添加到当前的List<DeferredImportSelectorHolder>中,等待后续的处理解析;

ConfigurationClassParser {
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
				// 8、属于AnnotatedBeanDefinition,调用这里解析,com.sam.project.web.SamWebApplication的BeanDefinitionHolder
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				......
			}
		}
		// 17、这里是处理下面AutoConfigurationImportSelector实例化的ImportSelector,得到的就是EnableAutoConfiguration映射的配置列表
		this.deferredImportSelectorHandler.process();
	}
	
	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
	}
	
	protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		......
		// 9、处理校验SamWebApplication的注解,并组装sourceClass
		SourceClass sourceClass = asSourceClass(configClass, filter);
		// 10、循环递归处理
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}
	
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
		//11、如果是有注解@Compnent内部类,判断如果是配置类,就执行processConfigurationClass方法递归
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		//12、处理有注解@PropertySource的属性
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}
		//13、处理有注解@ComponentScan
		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}
		//14、处理有@Import的注解
		// getImports(sourceClass)遍历SamWebApplication启动资源类的所有注解,获取Import注解的资源类AutoConfigurationPackages和AutoConfigurationImportSelector
		// AutoConfigurationPackages添加到SamWebApplication的ConfigurationClass类的importBeanDefinitionRegistrars中
		// AutoConfigurationImportSelector实例化成ImportSelector后添加到当前的List<DeferredImportSelectorHolder>中
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		......
		return null;
	}
	
	
}

15、ConfigurationClassParser的processImports()方法会调用getImports执行process方法,这里的group.process就是调用AutoConfigurationImportSelector的process方法

		public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}

16、AutoConfigurationImportSelector.java类的getAutoConfigurationEntry方法里面的方法getCandidateConfigurations获取候选配置类名称;这里loadFactoryNames方法才是从前面说的Map<ClassLoader, MultiValueMap<String, String>> cache里面获取EnableAutoConfiguration.class对应的配置列表;

	public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
		Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
				() -> String.format("Only %s implementations are supported, got %s",
						AutoConfigurationImportSelector.class.getSimpleName(),
						deferredImportSelector.getClass().getName()));
        // 16、调用getCandidateConfigurations方法获取配置类名称列表
		AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
				.getAutoConfigurationEntry(annotationMetadata);
		this.autoConfigurationEntries.add(autoConfigurationEntry);
		for (String importClassName : autoConfigurationEntry.getConfigurations()) {
			this.entries.putIfAbsent(importClassName, annotationMetadata);
		}
	}

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

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

17、执行完上面后,继续执行ConfigurationClassParser类parse()方法的this.deferredImportSelectorHandler.process(),这里是处理上面AutoConfigurationImportSelector实例化的ImportSelector,得到的就是EnableAutoConfiguration映射的配置列表,包含了自定义的MyAutoConfiguration;

18、执行完ConfigurationClassParser.parse(...)方法后,ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法继续执行到this.reader.loadBeanDefinitions(configClasses),这个方法的作用就是把前面解析出来的配置类中的@Bean注解的方法解析成beanDefinition都注册到容器中。

结尾:到这里,已经把自定义Configuration的类进行解析,对@Bean等配置类里面的资源都解析成BeanDefinition注册到容器中;然后等到执行finishBeanFactoryInitialization(beanFactory)方法的时候就进行bean的初始化。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值