You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
source-code-hunter/docs/SpringBoot/SpringBoot-自动装配.md

17 KiB

Spring Boot 自动装配

@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 {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

}

AutoConfigurationImportSelector

  • 类图

image-20200320150642022

getAutoConfigurationMetadata()

		@Override
		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()));
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(
							// 加载配置元数据
							getAutoConfigurationMetadata(), annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}


		private AutoConfigurationMetadata getAutoConfigurationMetadata() {
			if (this.autoConfigurationMetadata == null) {
				// 加载配置信息
				this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
			}
			return this.autoConfigurationMetadata;
		}

  • org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)

    	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
    		try {
    
    		    // 获取资源路径
    			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
    					: ClassLoader.getSystemResources(path);
    			Properties properties = new Properties();
    			while (urls.hasMoreElements()) {
    				properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
    			}
    			return loadMetadata(properties);
    		}
    		catch (IOException ex) {
    			throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
    		}
    	}
    
    

    image-20200320160423991

  • protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

    注意: 这个文件在target编译后的文件夹中

    相关 Issues : https://github.com/spring-projects/spring-boot/issues/11282

  • 自动装配

    spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

    该文件内存有:

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    

image-20200320162835665

同样找一下 redis

image-20200320163001728

  • 仔细看org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

先说注解

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })

EnableConfigurationProperties

自动映射一个POJO到Spring Boot配置文件默认是application.properties文件的属性集。

  • org.springframework.boot.autoconfigure.data.redis.RedisProperties
  • 部分 redis 配置属性
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

	/**
	 * Database index used by the connection factory.
	 */
	private int database = 0;

	/**
	 * Connection URL. Overrides host, port, and password. User is ignored. Example:
	 * redis://user:password@example.com:6379
	 */
	private String url;

	/**
	 * Redis server host.
	 */
	private String host = "localhost";

	/**
	 * Login password of the redis server.
	 */
	private String password;

	/**
	 * Redis server port.
	 */
	private int port = 6379;

	/**
	 * Whether to enable SSL support.
	 */
	private boolean ssl;

	/**
	 * Connection timeout.
	 */
	private Duration timeout;

	/**
	 * Client name to be set on connections with CLIENT SETNAME.
	 */
	private String clientName;



}
  • 找到一个我们用相同方式去寻找到别的一些属性处理如org.springframework.boot.autoconfigure.jdbc.JdbcProperties 具体展开请各位读者自行了解了

AnnotationMetadata

回过头继续我们的主要流程

  • org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

    image-20200320163806852

再此之前我们看过了getAutoConfigurationMetadata()的相关操作

关注 AnnotationMetadata annotationMetadata 存储了一些什么

image-20200320164145286

这里简单理解

  1. mergedAnnotations 类相关的注解信息
  2. annotationTypes 在启动类上的注解列表

getAutoConfigurationEntry

	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// 获取注解属性值
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 获取候选配置信息
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		// 删除重复配置
		configurations = removeDuplicates(configurations);
		// 获取 exclude 属性
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		// 校验 exclude 类
		checkExcludedClasses(configurations, exclusions);
		// 配置中删除 exclude 的属性值
		configurations.removeAll(exclusions);
		// 过滤
		configurations = filter(configurations, autoConfigurationMetadata);
		// 触发自动配置事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		// 返回
		return new AutoConfigurationEntry(configurations, exclusions);
	}

getAttributes

	protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
	    // name = org.springframework.boot.autoconfigure.EnableAutoConfiguration , 这是一个固定的值
		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;
	}

image-20200320171138431

getCandidateConfigurations

  • 读取spring.factories数据
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	    // 读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 相关配置
		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;
	}

image-20200320171734270

  • 第一个是我自己写的一个测试用

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      org.sourcehot.service.HelloServiceAutoConfiguration
    

removeDuplicates

  • new 两个对象直接做数据转换,去重
	protected final <T> List<T> removeDuplicates(List<T> list) {
		return new ArrayList<>(new LinkedHashSet<>(list));
	}

getExclusions

	protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		Set<String> excluded = new LinkedHashSet<>();
		// 获取属性 exclude 值转换成list
		excluded.addAll(asList(attributes, "exclude"));
      // 获取属性 excludeName 值转换成list
		excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
		// 获取 SpringBoot 本身的忽略配置属性
		excluded.addAll(getExcludeAutoConfigurationsProperty());
		return excluded;
	}

getExcludeAutoConfigurationsProperty

	private List<String> getExcludeAutoConfigurationsProperty() {
		if (getEnvironment() instanceof ConfigurableEnvironment) {
			Binder binder = Binder.get(getEnvironment());
			// 取出 "spring.autoconfigure.exclude" 转换成list
			return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
					.orElse(Collections.emptyList());
		}
		String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
		return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
	}

image-20200323080611527

  • 修改启动类

    @SpringBootApplication(excludeName =  { "org.sourcehot.service.HelloServiceAutoConfiguration" })
    
    

    image-20200323081009823

checkExcludedClasses

	private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
		List<String> 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);
		}
	}

  • configurations.removeAll(exclusions)

    移除忽略的类

filter

	private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
		long startTime = System.nanoTime();
		String[] candidates = StringUtils.toStringArray(configurations);
		boolean[] skip = new boolean[candidates.length];
		boolean skipped = false;
		// 获取 AutoConfigurationImportFilter 相关配置
		for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
		    // 执行 aware 相关接口
			invokeAwareMethods(filter);
			// 比较
			boolean[] match = filter.match(candidates, autoConfigurationMetadata);
			for (int i = 0; i < match.length; i++) {
				if (!match[i]) {
					skip[i] = true;
					candidates[i] = null;
					skipped = true;
				}
			}
		}
		if (!skipped) {
			return configurations;
		}
		List<String> 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);
	}

  • getAutoConfigurationImportFilters()spring.factories 获取 AutoConfigurationImportFilter的接口

image-20200323081903145

  • 循环内执行Aware系列接口

match方法: org.springframework.boot.autoconfigure.AutoConfigurationImportFilter#match

  • filter.match(candidates, autoConfigurationMetadata) 比较判断哪些是需要自动注入的类

image-20200323082553595

fireAutoConfigurationImportEvents

	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) {
				// 执行 Aware 相关接口
				invokeAwareMethods(listener);
				// 监听器执行自动配置事件
				listener.onAutoConfigurationImportEvent(event);
			}
		}
	}

image-20200323083149737

  • AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);

image-20200323083247061

  • org.springframework.boot.autoconfigure.AutoConfigurationImportListener#onAutoConfigurationImportEvent 在执行自动配置时触发 , 实现类只有 ConditionEvaluationReportAutoConfigurationImportListener

    	@Override
    	public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
    		if (this.beanFactory != null) {
    			ConditionEvaluationReport report = ConditionEvaluationReport.get(this.beanFactory);
    			// 记录需要加载的配置
    			report.recordEvaluationCandidates(event.getCandidateConfigurations());
    			// 记录不需要加载的配置
    			report.recordExclusions(event.getExclusions());
    		}
    	}
    
    

image-20200323083656670

  • 初始化完

process

  • org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process

image-20200323084922159

  • 后续的一些行为相对简单,直接放个源码了.
@Override
		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()));
			// 自动装配信息
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(
							// 加载配置元数据
							getAutoConfigurationMetadata(), annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			// 循环需要自动注入的类
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				// 继续放入k,v
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}

selectImports

		@Override
		public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			// 获取忽略的类
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());

			// 获取需要注入的类
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));

			// 把不需要自动注入的类从需要注入的类中移除
			processedConfigurations.removeAll(allExclusions);

			// 排序
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

后续由 spring 进行不再继续跟踪