配置文件自动注入源码分析

Spring约定的目录

在这里插入图片描述

DataSource自动配置过程

  • API 是sun公司定义的规则META-INF/servises/xx
  • spring同样支持自动配置,但规则略有不同 META-INF/spring.factories – EnableAutoConfiguration
META-INF/spring.factories -> EnableAutoConfiguration -> org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ //***
-------------------------------------------------------------------------------
//配置类,托管@Bean
@Configuration(proxyBeanMethods = false)
//条件注解:一定要出现DataSource与EmbeddedDatabaseType字节码
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
//条件注解:一定不能有这样的类被托管
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
//条件注解引申:@ConditionalOnBean @ConditionalOnMissingBean...............
//因为条件注解,扫描EnableAutoConfiguration配置项下133个jar时不会全部加载,满足条件才会加载

//自动配置前做该操作(用SQL语句完成初始化自动配置 ***)
@AutoConfigureBefore(SqlInitializationAutoConfiguration.class)
//启动配置属性DataSourceProperties(将配置文件中的内容存到一个类中) ***
@EnableConfigurationProperties(DataSourceProperties.class)//一旦配置完成就连接数据库成功
//Import托管系列配置类
//数据源池
//数据源的初始化配置类  ***
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
		DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class,
		DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
	
	//不同数据源的加载
	
	@Configuration(proxyBeanMethods = false)
	//满足EmbeddedDatabaseCondition条件就会加载内置数据源
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {   }
	
	@Configuration(proxyBeanMethods = false)
	//基于连接池的池化数据源
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	//托管一系列配置类
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
			DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration {  }
}
-------------------------------------------------------------------------------
	//读取配置文件spring.datasource,批量完成注入
	//写在配置文件中的配置项实际上都对应一个属性文件类,该类包含@ConfigurationProperties注解
	@ConfigurationProperties(prefix = "spring.datasource")
	public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {.....................}
-------------------------------------------------------------------------------
	@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
	@org.springframework.context.annotation.Conditional(DifferentCredentialsCondition.class)
	@org.springframework.context.annotation.Import(DatabaseInitializationDependencyConfigurer.class)
	@ConditionalOnSingleCandidate(DataSource.class)
	@ConditionalOnMissingBean(DataSourceScriptDatabaseInitializer.class)
	//满足以上条件,该类起作用
	static class InitializationSpecificCredentialsDataSourceInitializationConfiguration {
		
		//初始化数据库   每次都找到该脚本执行,可以重写建表XX,但注意库要自己提前创建
		
		//ddl数据定义语言(建表语句)
		//ObjectProvider<DataSource> dataSource:数据源,知道连接哪个数据库
		//DataSourceProperties properties:数据源属性,就是自己在配置文件中的配置项创建的对应的类
		@Bean
		SqlDataSourceScriptDatabaseInitializer ddlOnlyScriptDataSourceInitializer(ObjectProvider<DataSource> dataSource,
				DataSourceProperties properties) {
			//创建一个对象
			DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
			//数据库脚本位置---->  scriptLocations查看数据库脚本默认位置 ***
			//properties.getPlatform()   --->  返回字符串all
			//数据库脚本默认位置:classpath*: schema-all.sql或schema.sql
			settings.setSchemaLocations(scriptLocations(properties.getSchema(), "schema", properties.getPlatform()));
			settings.setContinueOnError(properties.isContinueOnError());
			settings.setSeparator(properties.getSeparator());
			settings.setEncoding(properties.getSqlScriptEncoding());
			settings.setMode(mapMode(properties.getInitializationMode()));
			DataSource initializationDataSource = determineDataSource(dataSource::getObject,
					properties.getSchemaUsername(), properties.getSchemaPassword());
			return new SqlDataSourceScriptDatabaseInitializer(initializationDataSource, settings);
		}

		//dml:增删改查
		@Bean
		@DependsOn("ddlOnlyScriptDataSourceInitializer")
		SqlDataSourceScriptDatabaseInitializer dmlOnlyScriptDataSourceInitializer(ObjectProvider<DataSource> dataSource,
				DataSourceProperties properties) {
			DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
			//数据库脚本默认位置:classpath*: data-all.sql或data.sql
			settings.setDataLocations(scriptLocations(properties.getData(), "data", properties.getPlatform()));
			settings.setContinueOnError(properties.isContinueOnError());
			settings.setSeparator(properties.getSeparator());
			settings.setEncoding(properties.getSqlScriptEncoding());
			settings.setMode(mapMode(properties.getInitializationMode()));
			DataSource initializationDataSource = determineDataSource(dataSource::getObject,
					properties.getDataUsername(), properties.getDataPassword());
			return new SqlDataSourceScriptDatabaseInitializer(initializationDataSource, settings);
		}
	}
	
	//启动数据库脚本默认配置文件的配置项在@AutoConfigureBefore(SqlInitializationAutoConfiguration.class)中定义
-------------------------------------------------------------------------------
	@Configuration(proxyBeanMethods = false)
	@AutoConfigureAfter({ R2dbcAutoConfiguration.class, DataSourceAutoConfiguration.class })
	//配置属性 SqlInitializationProperties中的属性就是配置文件中配置的配置项   ----->   学会自己查配置项的配置方法
	@EnableConfigurationProperties(SqlInitializationProperties.class)
	@Import({ DatabaseInitializationDependencyConfigurer.class, R2dbcInitializationConfiguration.class,
			DataSourceInitializationConfiguration.class })
	@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true)
	@Conditional(SqlInitializationModeCondition.class)
	public class SqlInitializationAutoConfiguration {
		static class SqlInitializationModeCondition extends NoneNestedConditions {
			SqlInitializationModeCondition() {
				super(ConfigurationPhase.PARSE_CONFIGURATION);
			}

			@ConditionalOnProperty(prefix = "spring.sql.init", name = "mode", havingValue = "never")
			static class ModeIsNever {
			}
		}
	}

JdbcTemplate自动配置过程

META-INF/spring.factories -> EnableAutoConfiguration -> org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ //***
-------------------------------------------------------------------------------
//配置类,托管@Bean
@Configuration(proxyBeanMethods = false)
//条件注解:一定要出现DataSource与EmbeddedDatabaseType字节码
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
//条件注解:DataSource只有一个实例
@ConditionalOnSingleCandidate(DataSource.class)
//自动配置完后执行
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
//配置属性文件 ****
@EnableConfigurationProperties(JdbcProperties.class)
//jdbc模板类配置文件 JdbcTemplateConfiguration ****
@Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class,
		NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {

}
------------------------------------------------------------------------
配置:
spring:
 jdbc: 
  fetch-size: 5
  ......
------------------------------------------------------------------------
@ConfigurationProperties(prefix = "spring.jdbc")
public class JdbcProperties {

	private final Template template = new Template();

	public Template getTemplate() {
		return this.template;
	}

	/**
	 * {@code JdbcTemplate} settings.
	 */
	public static class Template {

		/**
		 * Number of rows that should be fetched from the database when more rows are
		 * needed. Use -1 to use the JDBC driver's default configuration.
		 */
		private int fetchSize = -1;

		/**
		 * Maximum number of rows. Use -1 to use the JDBC driver's default configuration.
		 */
		private int maxRows = -1;

		/**
		 * Query timeout. Default is to use the JDBC driver's default configuration. If a
		 * duration suffix is not specified, seconds will be used.
		 */
		@DurationUnit(ChronoUnit.SECONDS)
		private Duration queryTimeout;

		public int getFetchSize() {
			return this.fetchSize;
		}

		public void setFetchSize(int fetchSize) {
			this.fetchSize = fetchSize;
		}

		public int getMaxRows() {
			return this.maxRows;
		}

		public void setMaxRows(int maxRows) {
			this.maxRows = maxRows;
		}

		public Duration getQueryTimeout() {
			return this.queryTimeout;
		}

		public void setQueryTimeout(Duration queryTimeout) {
			this.queryTimeout = queryTimeout;
		}

	}

}
--------------------------------------------------------------------------
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
//条件满足开始托管bean
class JdbcTemplateConfiguration {
	
	//DataSource dataSource:数据源
	//JdbcProperties properties:配置项对应的类
	@Bean
	@Primary
	JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		JdbcProperties.Template template = properties.getTemplate();
		jdbcTemplate.setFetchSize(template.getFetchSize());
		jdbcTemplate.setMaxRows(template.getMaxRows());
		if (template.getQueryTimeout() != null) {
			jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
		}
		return jdbcTemplate;
	}
}

自动配置原理

1.过程

1.双亲委派加载所有有META-INF/spring.factories路径的jar,同时解析其中的接口名,以及接口下的实现类
以(类加载器,Map(接口Class,List实现类))的形式保存到catch缓存中
2.通过指定类加载器获取指定接口下所有的实现类,再根据条件注解排除不需要的

2.准备阶段

  • 利用类加载器加载META-INF/spring.factories中配置的接口及类,形成map,并存到缓存cache
SpringFactoriesLoader
------------------------------------------------------------------------
//传入的参数factoryType与想象的有些不同,原因是启动时spring内部有很多东西要加载
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoader == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}

	String factoryTypeName = factoryType.getName();
	//loadSpringFactories返回指定目录下配置文件中的jar包 ***
	return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
------------------------------------------------------------------------
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	//先去缓存中取
	Map<String, List<String>> result = (Map)cache.get(classLoader);
	if (result != null) {
		return result;
	} else {
		HashMap result = new HashMap();

		try {
			//获取有指定路径的jar包
			Enumeration urls = classLoader.getResources("META-INF/spring.factories");

			//循环获取所有jar包
			while(urls.hasMoreElements()) {
				//取出jar包   jar:file:/D:/MavenRepository/com/baomidou/mybatis-plus-boot-starter/3.4.2/mybatis-plus-boot-starter-3.4.2.jar!/META-INF/spring.factories
				URL url = (URL)urls.nextElement();
				//URL [jar:file:/D:/MavenRepository/com/baomidou/mybatis-plus-boot-starter/3.4.2/mybatis-plus-boot-starter-3.4.2.jar!/META-INF/spring.factories]
				UrlResource resource = new UrlResource(url);
				//取出属性(接口名) size = 2(org.springframework.boot.env.EnvironmentPostProcessor+org.springframework.boot.autoconfigure.EnableAutoConfiguration)
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				//将属性取成迭代器
				Iterator var6 = properties.entrySet().iterator();
				
				//循环属性
				while(var6.hasNext()) {
					Entry<?, ?> entry = (Entry)var6.next();
					//接口名  org.springframework.boot.env.EnvironmentPostProcessor
					String factoryTypeName = ((String)entry.getKey()).trim();
					//实现类  com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor
					String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
					String[] var10 = factoryImplementationNames;
					int var11 = factoryImplementationNames.length;

					for(int var12 = 0; var12 < var11; ++var12) {
						String factoryImplementationName = var10[var12];
						((List)result.computeIfAbsent(factoryTypeName, (key) -> {
							return new ArrayList();
						})).add(factoryImplementationName.trim());
					}
				}
			}
			//做一些处理存到result中   47个(配置不同个数不同)
			result.replaceAll((factoryType, implementations) -> {
				return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
			});
			//保存到缓存中   (类加载器,Map(接口Class,List实现类))
			cache.put(classLoader, result);
			return result;
		} catch (IOException var14) {
			throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
		}
	}
}

3.获取指定接口下所有的实现类,再根据条件注解排除不需要的

@SpringBootApplication  -->  @EnableAutoConfiguration --> AutoConfigurationImportSelector
------------------------------------------------------------------------
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	//getAutoConfigurationEntry ***
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
------------------------------------------------------------------------
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	//getCandidateConfigurations返回对应接口实现类List(所有) ***
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	
	//前面获取到所有实现类,这里开始筛选,去除不需要的(不满足条件注解的)
	configurations = removeDuplicates(configurations);//去重
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	//筛选过后的被返回
	return new AutoConfigurationEntry(configurations, exclusions);
}
------------------------------------------------------------------------
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	//getSpringFactoriesLoaderFactoryClass==EnableAutoConfiguration.class;
	//根据类加载器寻找EnableAutoConfiguration接口对应的实现类对应的list列表
	//loadFactoryNames ***
	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;
}
------------------------------------------------------------------------
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	ClassLoader classLoaderToUse = classLoader;
	if (classLoader == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}

	String factoryTypeName = factoryType.getName();
	//根据类加载器获取接口,根据接口获取实现类List  loadSpringFactories ***
	return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
------------------------------------------------------------------------
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	//第一步已经加载过一次到缓存中
	Map<String, List<String>> result = (Map)cache.get(classLoader);
	if (result != null) {
		//直接返回
		return result;
	} else {
		HashMap result = new HashMap();

		try {
			Enumeration urls = classLoader.getResources("META-INF/spring.factories");

			while(urls.hasMoreElements()) {
				URL url = (URL)urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				Iterator var6 = properties.entrySet().iterator();

				while(var6.hasNext()) {
					Entry<?, ?> entry = (Entry)var6.next();
					String factoryTypeName = ((String)entry.getKey()).trim();
					String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
					String[] var10 = factoryImplementationNames;
					int var11 = factoryImplementationNames.length;

					for(int var12 = 0; var12 < var11; ++var12) {
						String factoryImplementationName = var10[var12];
						((List)result.computeIfAbsent(factoryTypeName, (key) -> {
							return new ArrayList();
						})).add(factoryImplementationName.trim());
					}
				}
			}

			result.replaceAll((factoryType, implementations) -> {
				return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
			});
			cache.put(classLoader, result);
			return result;
		} catch (IOException var14) {
			throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值