Spring源码之Environment源码分析
Environment类体系
-
PropertyResolver接口:提供属性访问功能,解析${…}
- containsProperty(String key):返回给定属性键是否可用于解析,即给定键的值是否不是{@code null}。
- getProperty(String key):返回与给定键关联的属性值,如果无法解析该键,则返回{@code null}
- getProperty(String key, String defaultValue):如果无法解析该键,则返回默认值
- T getProperty(String key, Class targetType):返回执行类型
- T getProperty(String key, Class targetType, T defaultValue):提供默认值
- String getRequiredProperty(String key) throws IllegalStateException:解析错误抛出异常
- T getRequiredProperty(String key, Class targetType) throws IllegalStateException:解析错误抛出异常
- String resolvePlaceholders(String text):解决${…}给定文本中的占位符,用{@link#getProperty}解析的相应属性值替换它们。不带默认值的不可解析占位符将被忽略,并以不变的方式传递
- String resolveRequiredPlaceholders(String text) throws IllegalArgumentException:抛出异常
-
ConfigurablePropertyResolver:继承自PropertyResolver,主要提供属性类型转换(基于org.springframework.core.convert.ConversionService)功能
-
getConversionService:返回对属性执行类型转换时使用的{@link ConfigurableConversionService}。返回的转换服务的可配置性质允许方便地添加和删除单个{@code Converter}实例:
* ConfigurableConversionService cs = env.getConversionService(); * cs.addConverter(new FooConverter());
-
setConversionService(ConfigurableConversionService conversionService):设置对属性执行类型转换时要使用的{@link ConfigurableConversionService};
- 作为完全替换{@code ConversionService}的替代方法,可以考虑通过深入{@link#getConversionService()}并调用{@code#addConverter}等方法来添加或删除单个{@code Converter}实例
-
setPlaceholderPrefix(String placeholderPrefix):设置由此解析器解决程序替换的占位符必须开头的前缀。
-
setPlaceholderSuffix(String placeholderSuffix):设置结束符
-
setValueSeparator(@Nullable String valueSeparator):指定由此替换的占位符之间的分隔字符
解析程序及其关联的默认值,或者{@code null},如果不应将此类特殊字符作为值分隔符处理。
-
setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders):
- 设置遇到嵌套在给定属性值中的不可解析占位符时是否引发异常。
- {@code false}值表示严格解析,即将引发异常。
- {@code true}值表示不可解析的嵌套占位符应该在其未解析的${…}中传递类型
- {@link#getProperty(String)}及其变体的实现必须检查此处设置的值,以确定当属性值包含无法解析的占位符时的正确行为。
-
setRequiredProperties(String… requiredProperties):指定必须存在哪些属性,由{@link#validateRequiredProperties()}验证。
-
validateRequiredProperties() throws MissingRequiredPropertiesException:验证{@link#setRequiredProperties}存在并解析为非{@code null}值。@throws-MissingRequiredPropertiesException如果需要属性是不可解析的。
-
-
Environment接口:继承自PropertyResolver,提供访问和判断profiles的功能。
- getDefaultProfiles:当没有明确设置活动profile时,返回默认激活的profile
- acceptsProfiles:返回{@linkplain#getActiveProfiles()active profiles}是否与给定的{@link profiles}谓词匹配
- getActiveProfiles:如果没有明确指定为活动的Profile,则{@linkplain#getDefaultProfiles()默认配置文件}将自动激活;返回为此环境显式激活的配置文件集。概要文件用于创建要有条件注册的bean定义的逻辑分组,例如基于部署环境;通过将{@linkplain AbstractEnvironment#ACTIVE_Profiles_PROPERTY_NAME“spring.Profiles.ACTIVE”}设置为系统属性或调用
-
ConfigurableEnvironment接口:继承自ConfigurablePropertyResolver和Environment,并且提供设置激活的profile和默认的profile的功能
- setActiveProfiles(String… profiles)
- 指定此{@code Environment}的活动Profile。配置文件在容器引导期间进行评估,以确定是否应该向容器注册bean定义
- 任何现有的活动配置文件都将替换为给定的参数;使用零参数调用以清除当前的活动配置文件集。
- 使用{@link#addActiveProfile}添加配置文件,同时保留现有集
- addActiveProfile(String profile)
- 将配置文件添加到当前激活的配置文件集中。
- setDefaultProfiles(String… profiles)
- 如果没有其他配置文件通过{@link#setActiveProfiles}显式激活,则指定默认情况下激活的配置文件集。
- MutablePropertySources getPropertySources()
- 以可变形式返回此{@code-Environment}的{@link-PropertySources},允许在解析针对此{@code-Environment}对象的属性时,操作应搜索的{@link-PropertySources}对象集
- 各种{@link MutablePropertySources}方法,例如
- {@link MutablePropertySources#addFirst addFirst},
- {@link MutablePropertySources#addLast addLast},\
- {@link MutablePropertySources#addBefore addBefore}和
- {@link MutablePropertySources#addAfter addAfter}
- 允许对属性源排序进行细粒度控制。例如,这有助于确保某些用户定义的特性源的搜索优先级高于默认特性源,例如系统特性集或系统环境变量集。
- setActiveProfiles(String… profiles)
-
ConfigurableWebEnvironment接口:继承自ConfigurableEnvironment,并且提供配置Servlet上下文和Servlet参数的功能
-
initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig)
-
将任何充当占位符的{@linkplain org.springframework.core.env.PropertySource.StubPropertySource StubPropertySource}实例替换为真正的servlet上下文/配置属性源使用给定的参数。
-
@param servletContext{@link servletContext}(不能是{@code null})
@param servletConfig{@link servletConfig}({@code null}如果不可用)
-
-
-
AbstractEnvironment抽象类:实现了ConfigurableEnvironment接口,默认属性和存储容器的定义,并且实现了ConfigurableEnvironment中的方法,并且为子类预留可覆盖了扩展方法。
-
StandardEnvironment:继承自AbstractEnvironment,非Servlet(Web)环境下的标准Environment实现。
-
StandardServletEnvironment:继承自StandardEnvironment,Servlet(Web)环境下的标准Environment实现
-
StandardEnvironment#customizePropertySources
-
ConfigurableWebEnvironment#initPropertySources
-
Environment的存储容器
Environment的静态属性和存储容器都是在AbstractEnvironment中定义的,ConfigurableWebEnvironment接口提供的getPropertySources()方法可以获取到返回的MutablePropertySources实例,然后添加额外的PropertySource。实际上,Environment的存储容器就是org.springframework.core.env.PropertySource的子类集合,AbstractEnvironment中使用的实例是org.springframework.core.env.MutablePropertySources,下面看下PropertySource的源码:
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name;
protected final T source;
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
public String getName() {
return this.name;
}
public T getSource() {
return this.source;
}
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
@Nullable
public abstract Object getProperty(String name);
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof PropertySource &&
ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.name);
}
//省略其他方法和内部类的源码
}
- 源码相对简单,预留了一个getProperty抽象方法给子类实现,重点需要关注的是覆写了的equals和hashCode方法,实际上只和name属性相关,这一点很重要,说明一个PropertySource实例绑定到一个唯一的name,这个name有点像HashMap里面的key,部分移除、判断方法都是基于name属性
- PropertySource的最常用子类
- MapPropertySource:source指定为Map实例的PropertySource实现。
- PropertiesPropertySource:source指定为Map实例的PropertySource实现,内部的Map实例由Properties实例转换而来。
- ResourcePropertySource:继承自PropertiesPropertySource,source指定为通过Resource实例转化为Properties再转换为Map实例。
- StubPropertySource:PropertySource的一个内部类,source设置为null,实际上就是空实现。
- ComparisonPropertySource:继承自ComparisonPropertySource,所有属性访问方法强制抛出异常,作用就是一个不可访问属性的空实现。
AbstractEnvironment中的属性定义
public static final String IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore";
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
private final Set<String> activeProfiles = new LinkedHashSet<>();
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
- 上面的propertySources(MutablePropertySources类型)属性就是用来存放PropertySource列表的,PropertySourcesPropertyResolver是ConfigurablePropertyResolver的实现,默认的profile就是字符串default
- MutablePropertySources是PropertySources的子类,它提供了get(String name)、addFirst、addLast、addBefore、addAfter、remove、replace等便捷方法,方便操作propertySourceList集合的元素
重要的三个方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//事件监控
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//获取spring,facories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
//1.获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//包装了一个DefaultApplicationArguments把我们启动的参数放入类中
//这个类包装了我们启动的main函数上面的String[] args,在我们启动jar包的时候我们可以给args赋值,这个值我们同样传到后面的run方法中并做了
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2.准备了一个Environment:构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//3.configureIgnoreBeanInfo放入需要ignore的bean
configureIgnoreBeanInfo(environment);
//printBanner打印Banner(SpringBoot启动时打印的logo)
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
//4.实例化SpringBootExceptionReporter.class,用来支持关于启动的错误
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//5.启动容器
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//6.刷新容器
refreshContext(context);
//7.刷新容器后的扩展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
#DefaultApplicationArguments
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
//Source 类是DefaultApplicationArguments的内部类继承了 SimpleCommandLinePropertySource 类构造方法直接调用了super(args);所以我们只需要看 SimpleCommandLinePropertySource 类的处理流程就行了:
private static class Source extends SimpleCommandLinePropertySource {
Source(String[] args) {
super(args);
}
@Override
public List<String> getNonOptionArgs() {
return super.getNonOptionArgs();
}
@Override
public List<String> getOptionValues(String name) {
return super.getOptionValues(name);
}
}
//super 类的构造方法同样调用了父类 CommandLinePropertySource 的构造方法,并传入处理之后的对象,我们来看下new SimpleCommandLineArgsParser().parse(args)是如何处理args的:
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
//spring有关的配置: --spring.profiles.active=dev
//代码非常简单,如果已--开头则进入处理拿到name和value,并放入commandLineArgs中并放回 CommandLineArgs 对象.之后调用父类
//CommandLinePropertySource 的构造方法:
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
//如果已--开头则进入处理拿到name和value
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
//处理拿到name和value
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf('='));
optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
}
else {
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
//放入commandLineArgs中并返回 CommandLineArgs 对象
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
//调用父类CommandLinePropertySource 的构造方法,其中COMMAND_LINE_PROPERTY_SOURCE_NAME是上面写死的成员变量 public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
public CommandLinePropertySource(T source) {
super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
}
//然后还是调用父类的构造方法 EnumerablePropertySource,然后直接调用父类PropertySource 的构造方法:给name和source赋值
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//根据应用程序的类型创建对应的环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//获取到启动参数,通过参数配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
//这一步往environment中添加configurationProperties为前缀的source
ConfigurationPropertySources.attach(environment);
//发布环境已准备的事件(重点方法):这是第二次发布事件,启动相应的监听器,其中一个重要的监听器ConfigApplicationListener就是加载项目配置文件的监听器
listeners.environmentPrepared(environment);
//将环境绑定到应用程序当中
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
#getOrCreateEnvironment
public enum WebApplicationType{
//不需要再web容器的环境下运行
NONE;
//基于servlet的web项目
SERVLET;
//spring5开始的新特性
REACTIVE;
}
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
由于ClassLoader都是空,所以直接返回一个WebApplicationType.SERVLET,然后由getOrCreateEnvironment方法创建一个 StandardServletEnvironment 对象
//environment已经被设置了servlet类型,所以这里创建StandardServletEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
//如果有environment则返回,如果没有就创建
if (this.environment != null) {
return this.environment;
}
//根据应用的类型选择环境,这里会创建一个 StandardServletEnvironment 对象
//webApplicationType的赋值是在创建SpringApplication对象中调用 WebApplicationType.deduceFromClasspath() 方法实现的
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
//在创建 StandardServletEnvironment 时,因为没有构造函数,所以直接调用父类构造方法,而他的父类 StandardEnvironment 也没有构造方法,继续调用父类AbstractEnvironment的构造方法,AbstractEnvironment的构造方法会去执行customizePropertySources
//customizePropertySources:这个方法在 StandardEnvironment 和 StandardServletEnvironment 中都有实现
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
StandardServletEnvironment:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
StandardEnvironment:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
//最后的效果是里面有四个值分别是:propertySources里面有四个key:servletConfigInitParams,servletContextInitParams,
//systemEnvironment,systemProperties,前两个key的value是一个object();systemProperties 的value是System.getProperties();加载了一堆系统变量
- 基于上面的这个思想我们可以自定义环境
下面这个自定义Environment主要是加载自定义配置文件。注意这里没有任何的实际的意义 仅仅演示。因为Springboot为我们提供了更简单的方式去加载自定义配置文件,仅仅是为了熟悉流程
在项目resources目录下面新建一个customize.yml
coledy:
account: xxxxxx
自定义Environment我们只需要继承StandardEnvironment或者其子类即可。
由于我们现在是web环境所以我选择继承StandardServletEnvironment。
@Slf4j
public class CustomEnvironment extends StandardServletEnvironment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
//这行代码不能少 父类也需要加载一些环境信息 例如StandardServletEnvironment就需要去加载
//servletContext参数和servletConfig参数
super.customizePropertySources(propertySources);
//这里我们使用默认的ResourceLoad去加载类路径下的配置文件
Resource resource = new DefaultResourceLoader().getResource("classpath:customize.yml");
try {
//这里使用yml解析器解析 加载的Resource
List<PropertySource<?>> customYml = new YamlPropertySourceLoader().load("customize_yml", resource);
for (PropertySource<?> p : customYml){
propertySources.addLast(p);
}
}catch (IOException e){
log.error("加載custom.yml配置文件異常!!!");
}
}
}
由于是自定义Environment所以我们启动类需要修改一下
@SpringBootApplication
public class SpringbootStudyApplication{
public static void main(String[] args) {
SpringApplication application = new SpringApplicationBuilder(SpringbootStudyApplication.class).application();
//这里需要我们手动设置一下自定义变量的实例
application.setEnvironment(new CustomEnvironment());
ApplicationContext context = application.run(args);
ConfigurableEnvironment environment = context.getBean(ConfigurableEnvironment.class);
System.out.println(environment.getProperty("coledy.account"));
}
}
我们看到自定义一个Environment流程很简单。
可是问题是刚刚我们提到的 web环境变量 系统环境变量 命令行参数 默认的application.yml 文件是什么时候加载的?我们先不着急回答这个问题 先看一下刚刚的 propertySources.addLast 这一行代码。我们把自定义的 customize.yml手动添加到了一个集合中 结果我们就可以在容器中拿到配置的信息
走一下原来的流程
SpringApplication#getOrCreateEnvironment会返回一个StandardServletEnvironment
因为StandardServletEnvironment继承StandardEnvironment而StandardEnvironment继承了AbstractEnvironment,肯定要先加载AbstractEnvironment的构造方法,而AbstractEnvironment的构造方法里面又会调用customizePropertySources方法,回去直接调用StandardServletEnvironment的customizePropertySources
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
//继续去调用StandardEnvironment的实现方法加载该类里面的属性
super.customizePropertySources(propertySources);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aXttIkhZ-1657260592404)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1652568169197.png)]
#configureEnvironment()
配置环境的一些配置信息,设置转换器用作参数的转换
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
//args为:--spring.profiles.active=prod
//his.addConversionService == true,所以会调用 ApplicationConversionService.getSharedInstance();方法:
if (this.addConversionService) {
//new 一个ApplicationConversionService当做sharedInstance并返回,
//所以上面的 ConversionService 的实际的对象是 ApplicationConversionService
//然后把conversionService强转成 ConfigurableConversionService 并赋值给environment的 //setConversionService方法.因为 ConfigurableConversionService是一个继承
//ApplicationConversionService 的空接口所以可以直接强转,之后调用configurePropertySources方法继续处理
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
//将args封装成了SimpleCommandLinePropertySource并加入到了environment中
configurePropertySources(environment, args);
//根据启动参数激活了相应的配置文件(例如在Program arguments中配置 )
configureProfiles(environment, args);
}
#configurePropertySources:
//参数args是通过上面传进来的参数
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
//environment的propertySources在构造StandardServletEnvironment时赋值,
//里面有四个值分别是:servletConfigInitParams,servletContextInitParams,
//systemEnvironment,systemProperties,因为我们启动没有加参数,所以这个方法做任何处理,
//如果加入了参数会走下面的if把 COMMAND_LINE_PROPERTY_SOURCE_NAME为key,args为value的 //SimpleCommandLinePropertySource 放入composite并把composite放入sources中
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
#configureProfiles
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
//profile为空
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
//调用了environment.getActiveProfiles()方法:此时profiles为prod
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
//设置environment的activeProfiles为prod
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
@Override
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
protected Set<String> doGetActiveProfiles() {
synchronized (this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
//调用getProperty方法获取profiles
@Override
@Nullable
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
#propertyResolver
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
在成员变量中直接创建了PropertySourcesPropertyResolver 的对象,传入propertySources,这个propertySources就是我们之前创建
severlet对象时赋值的propertySources,里面有四个值,但是没有ACTIVE_PROFILES_PROPERTY_NAME,所以获取的是空.
这里有个问题,ACTIVE_PROFILES_PROPERTY_NAME==spring.profiles.active,我们在spring的配置文件中会使用这个key配置读取得环境但是现在并没有读取出来,这个配置是在后面读取的(ConfigFileApplicationListener).
- 走完configurePropertySources(environment, args)和configureProfiles(ConfigurableEnvironment environment, String[] args)
- 走完configureProfiles
#environmentPrepared()
- 发布环境配置的事件,根据环境读取到对应的配置文件
//通过事件广播器,发布应用环境准备的事件
public void environmentPrepared(ConfigurableEnvironment environment) {
//创建了 ApplicationEnvironmentPreparedEvent 对象
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
public ApplicationContextInitializedEvent(SpringApplication application, String[] args,
ConfigurableApplicationContext context) {
//先调用了父类(SpringApplicationEvent)构造方法,然后把environment赋值给成员变量
super(application, args);
this.context = context;
}
#EventObject的构造方法
//赋值了source,同样的source是 SpringApplication 对象,同样的调用multicastEvent方法,同样获取Executor,同样invoke,就不在赘述了值得注意的是,这里执行的listener是为environment赋值使用的,例如前面我们提到的读取spring.profiles.active配置,就是在ConfigFileApplicationListener 中实现的
public EventObject(Object source) {
if (source == null) {
throw new IllegalArgumentException("null source");
} else {
this.source = source;
}
}
#multicastEvent
public void multicastEvent(ApplicationEvent event) {
//第一个参数是事件对象,第二个参数,根据事件解析出对应的事件类型对象(ResolvableType)
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event)方法
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//获取到对应的监听器,其中使用了缓存的方式将对应的事件类型跟监听器缓存起来,便于后续获取;其中使用了 GenericApplicationListenerAdapter 适配器的方式来提供给不同的监听器用来进行适配
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
//如果设置了线程池,就使用异步的方式进行执行
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//执行监听器执行的监听器为:ConfigFileApplicationListener
this.invokeListener(listener, event);
}
}
}
- 主要来看一下
ConfigFileApplicationListener
,该监听器非常核心,主要用来处理项目配置。项目中的 properties 和yml文件都是其内部类所加载
#ConfigFileApplicationListener
@Override
public void onApplicationEvent(ApplicationEvent event) {
显然event就是ApplicationEnvironmentPreparedEvent,所以会进入下面这个if
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
#onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
//方法首先调用loadPostProcessors方法做一些处理:加载 EnvironmentPostProcessor 的实现类
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
//将 ConfigFileApplicationListener 添加到处理链当中。配置文件的读取也是由 ConfigFileApplicationListener进行处理
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
- 目前加载出来的对象如下图
#ConfigFileApplicationListener:postProcessEnvironment
//当前方法调用了内部方法 addPropertySources(environment, application.getResourceLoader());
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
//其中创建了 Loder对象,,并且调用了load() 方法
//从字面上看第一步加入一个什么东西到environment中,然后在load个什么东西具体加入和load的内容是什么呢?
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
#addToEnvironment
//这个方法特别简单,创建了个RandomValuePropertySource的类然后放在environment中的propertySources里SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
//key的后面,至于RandomValuePropertySource对象的创建,不用看了特别简单就是获取一个随机数.
//经过这样的处理environment中的source里加了一个对象:RandomValuePropertySource {name='random',value=随机数}
public static void addToEnvironment(ConfigurableEnvironment environment) { environment.getPropertySources().addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
logger.trace("RandomValuePropertySource add to Environment");
}
#loader
//把environment中的属性赋值一份放在Loader里,resourceLoader就是 DefaultResourceLoader ,获取的是当前线程的classLoader
//然后propertySourceLoaders还是调用loadFactories方法获取对象,获取完的对象 有两个:
//PropertiesPropertySourceLoader和YamlPropertySourceLoader
//一个负责处理.properties一个负责处理.yml
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
//初始化环境,会将环境中的默认环境添加到this.profiles当中
//初始会有两个元素一个是空的,一个"default"
initializeProfiles();
//取出环境进行处理
while (!this.profiles.isEmpty()) {
//第一次循环profiles是null不会走第一个if
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
#initializeProfiles
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesFromProperty(ACTIVE_PROFILES_PROPERTY);
Set<Profile> includedViaProperty = getProfilesFromProperty(INCLUDE_PROFILES_PROPERTY);
List<Profile> otherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
this.profiles.addAll(otherActiveProfiles);
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
this.profiles.addAll(includedViaProperty);
addActiveProfiles(activatedViaProperty);
//判断profiles的size是否等于1,进入for循环处理
if (this.profiles.size() == 1) { // only has null profile
//然后for循环会创建Profile对象
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
//这段代码首先在profiles中添加一个null,然后调用 getProfilesFromProperty
//从environment中的resouce获取 ACTIVE_PROFILES_PROPERTY(spring.profiles.active)和INCLUDE_PROFILES_PROPERTY(spring.profiles.include)
//因为environment中没有所以直接返回一个空的set集合
private Set<Profile> getProfilesFromProperty(String profilesProperty) {
if (!this.environment.containsProperty(profilesProperty)) {
return Collections.emptySet();
}
Binder binder = Binder.get(this.environment);
Set<Profile> profiles = getProfiles(binder, profilesProperty);
return new LinkedHashSet<>(profiles);
}
//调用了environment.getActiveProfiles()方法获取,这个方法在之前研究过,获取不到任何东西,所以返回依旧是null,这些操作之后profiles内的对象依然只有一个null
private List<Profile> getOtherActiveProfiles(Set<Profile> activatedViaProperty,
Set<Profile> includedViaProperty) {
return Arrays.stream(this.environment.getActiveProfiles()).map(Profile::new).filter(
(profile) -> !activatedViaProperty.contains(profile) && !includedViaProperty.contains(profile))
.collect(Collectors.toList());
}
//addActiveProfiles因为profiles是empty的,所以直接返回,,没有记日志
void addActiveProfiles(Set<Profile> profiles) {
if (profiles.isEmpty()) {
return;
}
if (this.activatedProfiles) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
}
return;
}
this.profiles.addAll(profiles);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
}
this.activatedProfiles = true;
removeUnprocessedDefaultProfiles();
}
protected Set<String> doGetDefaultProfiles() {
synchronized (this.defaultProfiles) {
//doGetDefaultProfiles首先判断了this.defaultProfiles.equals(getReservedDefaultProfiles(),this.defaultProfiles在成员变量里直接赋值了:
//private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
//所以肯定是equals的
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
//,然后调用getProperty方法直接返回了environment中的propertyResolver中的spring.profiles.default为key的value值,返回的依旧是空,然后方法直接返回defaultProfiles即default,然后for循环会创建Profile对象
String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.defaultProfiles;
}
}
protected Set<String> getReservedDefaultProfiles() {
return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}
#加载配置文件中的信息
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//获取到所有的配置文件的路径:初始化时有4个文件的路径
//file:./config/
//file:./
//classpath:/config/
//classpath:/
getSearchLocations().forEach((location) -> {
//判断格式是否正确以/结果
boolean isFolder = location.endsWith("/");
//获取项目名
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
//根据项目名+文件前缀+环境遍历循环,后面会进行拼接
//
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
//首先getSearchLocations获取所有前缀
//一串代码下来获取的是DEFAULT_SEARCH_LOCATIONS("classpath:/,classpath:/config/,file:./,file:./config/")
//在我们之后读取配置文件时这些前缀会帮助我们拼接地址
private Set<String> getSearchLocations() {
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
Set<String> locations = getSearchLocations(
CONFIG_ADDITIONAL_LOCATION_PROPERTY);
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
DEFAULT_SEARCH_LOCATIONS));
return locations;
}
private Set<String> getSearchLocations(String propertyName) {
Set<String> locations = new LinkedHashSet<>();
if (this.environment.containsProperty(propertyName)) {
for (String path : asResolvedSet(
this.environment.getProperty(propertyName), null)) {
if (!path.contains("$")) {
path = StringUtils.cleanPath(path);
if (!ResourceUtils.isUrl(path)) {
path = ResourceUtils.FILE_URL_PREFIX + path;
}
}
locations.add(path);
}
}
return locations;
}
- 返回结果
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
//因为StringUtils.hasText(name)返回true,不会走下面的if
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
throw new IllegalStateException("File extension of config file location '" + location
+ "' is not known to any PropertySourceLoader. If the location is meant to reference "
+ "a directory, it must end in '/'");
}
Set<String> processed = new HashSet<>();
//获取propertySourceLoaders一个个处理,propertySourceLoaders就是之前获取的yml和properties的loader
for (PropertySourceLoader loader : this.propertySourceLoaders) {
//for循环中获取loader.getFileExtensions()返回{ "properties", "xml" }和{ "yml", "yaml" }
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
//加载对应路径的配置文件信息
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,consumer);
}
}
}
}
- this.propertySourceLoaders
- loader.getFileExtensions()
loadForFileExtension
loader:PropertiesPropertySourceLoader
perfix:"file:/config/application"
fileExtension:".properties"
Profile:null
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
//第一次profile为null
if (profile != null) {
// classpath:/config/application-default.yml
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
//和下面的load调用的是同一个方法
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
//加载其他application-test,application-prod...没有用到的配置文件
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
#load
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
DocumentConsumer consumer) {
try {
//获取根据classpath:/config/application.yml得到resouce
Resource resource = this.resourceLoader.getResource(location);
if (resource == null || !resource.exists()) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped missing config ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
//resource.getFilename():application.yml
//StringUtils.getFilenameExtension(resource.getFilename()):yml
if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped empty config extension ", location,
resource, profile);
this.logger.trace(description);
}
return;
}
//name:applicationConfig: [classpath:/config/application.yml]
//只有在拼接成classpath:application.properties时才能真正加载上面的代码:
String name = "applicationConfig: [" + location + "]";
//调用loadDocuments方法加载配置文件
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
List<Document> loaded = new ArrayList<>();
//直接使用PropertiesLoaderUtils获取了properties中所有的配置信息然后转换成document对象返回
//返回到load方法继续执行
for (Document document : documents) {
if (filter.match(document)) {
//直接循环调用addActiveProfiles和addIncludedProfiles赋值了profiles和 include
//返回最初的load:
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
loaded.forEach((document) -> consumer.accept(profile, document));
if (this.logger.isDebugEnabled()) {
StringBuilder description = getDescription("Loaded config file ", location, resource, profile);
this.logger.debug(description);
}
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
}
}
#loadDocuments
//loader:properties或者yml
//name:applicationConfig: [classpath:/application.properties]
//resource:class path resource [application.properties]
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
throws IOException {
//先从cache中获取documents,如果没有就loader.load(name, resource)读取,这是初始化肯定在cache中没有数据
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
//则调用loader.load
List<PropertySource<?>> loaded = loader.load(name, resource);
//将读取到的List<PropertySource<?>>转为documents
documents = asDocuments(loaded);
//加入到缓存里面
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
//PropertiesPropertySourceLoader
//name:applicationConfig: [classpath:/application.properties]
//resource:class path resource [application.properties]
@Override
public List<PropertySource<?>> load(String name, Resource resource)
throws IOException {
Map<String, ?> properties = loadProperties(resource);
if (properties.isEmpty()) {
return Collections.emptyList();
}
return Collections
.singletonList(new OriginTrackedMapPropertySource(name, properties));
}
#loadProperties
private Map<String, ?> loadProperties(Resource resource) throws IOException {
//application.properties
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
return (Map) PropertiesLoaderUtils.loadProperties(resource);
}
return new OriginTrackedPropertiesLoader(resource).load();
}
//构建OriginTrackedPropertiesLoader然后执行其load方法 new OriginTrackedPropertiesLoader(resource).load()
Map<String, OriginTrackedValue> load(boolean expandLists) throws IOException {
//将resource传入构建CharacterReader
//从源资源中读取字符,负责跳过注释、处理多行值和跟踪{@code'\\'}转义。
try (CharacterReader reader = new CharacterReader(this.resource)) {
Map<String, OriginTrackedValue> result = new LinkedHashMap<>();
StringBuilder buffer = new StringBuilder();
//while循环,读取配置文件中的值
while (reader.read()) {
String key = loadKey(buffer, reader).trim();
if (expandLists && key.endsWith("[]")) {
key = key.substring(0, key.length() - 2);
int index = 0;
do {
OriginTrackedValue value = loadValue(buffer, reader, true);
put(result, key + "[" + (index++) + "]", value);
if (!reader.isEndOfLine()) {
reader.read();
}
}
while (!reader.isEndOfLine());
}
else {
OriginTrackedValue value = loadValue(buffer, reader, false);
put(result, key, value);
}
}
//返回读取的数据
return result;
}
}
//YamlPropertySourceLoader
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, ?> loadProperties(Resource resource) throws IOException {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
return (Map) PropertiesLoaderUtils.loadProperties(resource);
}
return new OriginTrackedPropertiesLoader(resource).load();
}
- resource:
#addProfileToEnvironment
//因为现在environment中还没有activeProfile,所以调用this.environment.addActiveProfile(profile);方法把profiles放入environment中
private void addProfileToEnvironment(String profile) {
for (String activeProfile : this.environment.getActiveProfiles()) {
if (activeProfile.equals(profile)) {
return;
}
}
this.environment.addActiveProfile(profile);
}
//调用 doGetActiveProfiles 方法,前面知道返回空,然后把profile加入profiles中,
@Override
public void addActiveProfile(String profile) {
if (logger.isDebugEnabled()) {
logger.debug("Activating profile '" + profile + "'");
}
validateProfile(profile);
doGetActiveProfiles();
synchronized (this.activeProfiles) {
this.activeProfiles.add(profile);
}
}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
这一次传入的profile不为空,所以会走loadForFileExtension的
if (profile != null) {
// Try profile-specific file & profile section in profile file (gh-340)
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// Also try the profile-specific section (if any) of the normal file
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
//这段代码把loaded中的变量放入environment.getPropertySources()中,放入了两个:application-dvep.properties和 application.properties
private void addLoadedPropertySources() {
MutablePropertySources destination = this.environment.getPropertySources();
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = new HashSet<>();
//遍历loaded
for (MutablePropertySources sources : loaded) {
//遍历sources
for (PropertySource<?> source : sources) {
if (added.add(source.getName())) {
//添加到destination中
addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}
//代码执行到这里,整个 ConfigFileApplicationListener 类已经全部完成加载,现在我们总结一下这个监听器都做了什么:
//总的来说就是加载配置文件.
//但是健在的流程是获取所有的loader包括properties和yml的loader,然后在加载default的配置文件名获得application和default的//配置文件路径获得
//classpath:/,classpath:/config/,file:./,file:./config/ 文件路径,遍历拼装配置文件路径并获取配置文件里的内容,把他们放入environment中
版本2.4.0-3.0.0读取配置文件使用的是ConfigDataEnvironmentPostProcessor 实现了EnvironmentPostProcessor
#postProcessEnvironment
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles());
}
void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection<String> additionalProfiles) {
try {
this.logger.trace("Post-processing environment to add config data");
resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
}
catch (UseLegacyConfigProcessingException ex) {
this.logger.debug(LogMessage.format("Switching to legacy config file processing [%s]",
ex.getConfigurationProperty()));
configureAdditionalProfiles(environment, additionalProfiles);
postProcessUsingLegacyApplicationListener(environment, resourceLoader);
}
}
ConfigDataEnvironment#processAndApply
ConfigDataEnvironment
包装{@link ConfigurableEnvironment},可用于导入和应用{@link ConfigData}。
通过包装Spring{@link Environment}中的属性源并添加初始位置集,配置{@link ConfigDataEnvironmentContributors}的初始集。
初始位置可以通过{@link#LOCATION_PROPERTY}、{@value#ADDITIONAL_LOCATION_PROPERTY}和{@value#IMPORT_PROPERTY}属性来影响。
如果没有设置显式属性,将使用{@link#DEFAULT _SEARCH _LOCATIONS}。
/**
*使用的属性将覆盖导入的位置。
*/
static final String LOCATION_PROPERTY = "spring.config.location";
/**
* 属性,用于提供要导入的其他位置。
*/
static final String ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
/**
* 属性,用于提供要导入的其他位置。
*/
static final String IMPORT_PROPERTY = "spring.config.import";
如果未找到{@link#LOCATION_PROPERTY},则使用默认搜索位置。
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
static {
List<ConfigDataLocation> locations = new ArrayList<>();
locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
}
#processAndApply
void processAndApply() {
ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
this.loaders);
registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
ConfigDataActivationContext activationContext = createActivationContext(
contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
contributors = processWithoutProfiles(contributors, importer, activationContext);
activationContext = withProfiles(contributors, activationContext);
contributors = processWithProfiles(contributors, importer, activationContext);
applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
importer.getOptionalLocations());
}
this.contributors(在ConfigDataEnvironment的构造方法里面会赋值成下面这个样子)
ROOT null null []
EXISTING null null []
EXISTING null null []
EXISTING null null []
EXISTING null null []
EXISTING null null []
EXISTING null null []
INITIAL_IMPORT null null []
INITIAL_IMPORT null null []
#processInitial
private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer) {
this.logger.trace("Processing initial config data environment contributors without activation context");
contributors = contributors.withProcessedImports(importer, null);
registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
return contributors;
}
处理从所有活动贡献者的导入,并返回一个新的{@link configDataEnvironmentContractors}实例。
#withProcessedImports
ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
ConfigDataActivationContext activationContext) {
ImportPhase importPhase = ImportPhase.get(activationContext);
this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
(activationContext != null) ? activationContext : "no activation context"));
ConfigDataEnvironmentContributors result = this;
int processed = 0;
while (true) {
//第一个contributor是INITIAL_IMPORT null null []
ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
if (contributor == null) {
this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processed));
return result;
}
//第一个contributor的king是INITIAL_IMPORT
//第一个contributor的king是INITIAL_IMPORT
//经过第一二此分别给INITIAL_IMPORT添加了子节点,第三个contributor是
//UNBOUND_IMPORT optional:classpath:/;optional:classpath:/config/ class path resource [config/application.yml] []
if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
Iterable<ConfigurationPropertySource> sources = Collections
.singleton(contributor.getConfigurationPropertySource());
PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
result, activationContext, true);
Binder binder = new Binder(sources, placeholdersResolver, null, null, null);
ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(binder);
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, bound));
continue;
}
ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
result, contributor, activationContext);
ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
//第一次的imports为optional:file:./;optional:file:./config/;optional:file:./config/*/
//第一次的imports为optional:classpath:/;optional:classpath:/config/
List<ConfigDataLocation> imports = contributor.getImports();
this.logger.trace(LogMessage.format("Processing imports %s", imports));
//判断处理optional:classpath:/;optional:classpath:/config/有相关文件的路径
//OriginTrackedMapPropertySource {name='Config resource 'class path resource [application.properties]' via location 'optional:classpath:/''}
//OriginTrackedMapPropertySource {name='Config resource 'class path resource [config/application.yml]' via location 'optional:classpath:/config/''}
Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
locationResolverContext, loaderContext, imports);
this.logger.trace(LogMessage.of(() -> getImportedMessage(imported.keySet())));
ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
asContributors(imported));
//返回的result给第一个INITIAL_IMPORT添加了空的孩子节点
//INITIAL_IMPORT null null []
// EMPTY_LOCATION optional:file:./;optional:file:./config/;optional:file:./config/*/ null // [IGNORE_IMPORTS]
//返回的result给第二个INITIAL_IMPORT添加了空的孩子节点
//INITIAL_IMPORT null null []
// kind为UNBOUND_IMPORT 文件路径为config/application.yml
// UNBOUND_IMPORT optional:classpath:/;optional:classpath:/config/ class path resource [config/application.yml] []
// kind为UNBOUND_IMPORT 文件路径为application.properties
// UNBOUND_IMPORT optional:classpath:/;optional:classpath:/config/ class path resource [application.properties] []
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, contributorAndChildren));
processed++;
}
}
#ConfigDataLocationResolverContext
/**
* 上下文提供给{@link ConfigDataLocationResolver}方法。
*/
public interface ConfigDataLocationResolverContext {
/**
* 提供对活页夹的访问,该活页夹可用于获取以前提供的值。
* @return a binder instance
*/
Binder getBinder();
/**
*提供对触发解析的父项{@link ConfigDataResource}的访问,如果没有可用的父项,则提供对{@code null}的访问。
*/
ConfigDataResource getParent();
/**
*提供对所有{@link EnvironmentPostProcessor EnvironmentPostProcessors}共享的{@link ConfigurableBootstrapContext}的访问。
*/
ConfigurableBootstrapContext getBootstrapContext();
}
#Binder
绑定一个或多个{@link ConfigurationPropertySource ConfigurationPropertySources}对象的容器对象。
#resolveAndLoad
//解析并加载给定的位置列表,过滤之前加载的任何位置。
//locations===>optional:file:./;optional:file:./config/;optional:file:./config/*/
Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
List<ConfigDataLocation> locations) {
try {
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
//ConfigData加载对应文件下的属性
return load(loaderContext, resolved);
}
catch (IOException ex) {
throw new IllegalStateException("IO error on loading imports from " + locations, ex);
}
}
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext locationResolverContext,
Profiles profiles, List<ConfigDataLocation> locations) {
List<ConfigDataResolutionResult> resolved = new ArrayList<>(locations.size());
for (ConfigDataLocation location : locations) {
resolved.addAll(resolve(locationResolverContext, profiles, location));
}
return Collections.unmodifiableList(resolved);
}
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext locationResolverContext,
Profiles profiles, ConfigDataLocation location) {
try {
return this.resolvers.resolve(locationResolverContext, location, profiles);
}
catch (ConfigDataNotFoundException ex) {
handle(ex, location, null);
return Collections.emptyList();
}
}
List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location,
Profiles profiles) {
if (location == null) {
return Collections.emptyList();
}
for (ConfigDataLocationResolver<?> resolver : getResolvers()) {
if (resolver.isResolvable(context, location)) {
return resolve(resolver, context, location, profiles);
}
}
throw new UnsupportedConfigDataLocationException(location);
}
resolver==>StandardConfigDataLocationResolver
location==>optional:classpath:/;optional:classpath:/config/
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolver<?> resolver,
ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
List<ConfigDataResolutionResult> resolved = resolve(location, false, () -> resolver.resolve(context, location));
if (profiles == null) {
return resolved;
}
List<ConfigDataResolutionResult> profileSpecific = resolve(location, true,
() -> resolver.resolveProfileSpecific(context, location, profiles));
return merge(resolved, profileSpecific);
}
private List<ConfigDataResolutionResult> resolve(ConfigDataLocation location, boolean profileSpecific,
Supplier<List<? extends ConfigDataResource>> resolveAction) {
List<ConfigDataResource> resources = nonNullList(resolveAction.get());
//
List<ConfigDataResolutionResult> resolved = new ArrayList<>(resources.size());
for (ConfigDataResource resource : resources) {
resolved.add(new ConfigDataResolutionResult(location, resource, profileSpecific));
}
return resolved;
}
@Override
public List<StandardConfigDataResource> resolve(ConfigDataLocationResolverContext context,
ConfigDataLocation location) throws ConfigDataNotFoundException {
return resolve(getReferences(context, location.split()));
}
//configDataLocations:
// optional:classpath:/
// optional:classpath:/config/
private Set<StandardConfigDataReference> getReferences(ConfigDataLocationResolverContext context,
ConfigDataLocation[] configDataLocations) {
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
for (ConfigDataLocation configDataLocation : configDataLocations) {
references.addAll(getReferences(context, configDataLocation));
}
return references;
}
private Set<StandardConfigDataReference> getReferences(ConfigDataLocationResolverContext context,
ConfigDataLocation configDataLocation) {
//得到文件的路径
String resourceLocation = getResourceLocation(context, configDataLocation);
try {
//判断是否是文件夹(判断是否是以/结尾的)
if (isDirectory(resourceLocation)) {
return getReferencesForDirectory(configDataLocation, resourceLocation, NO_PROFILE);
}
return getReferencesForFile(configDataLocation, resourceLocation, NO_PROFILE);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Unable to load config data from '" + configDataLocation + "'", ex);
}
}
private String getResourceLocation(ConfigDataLocationResolverContext context,
ConfigDataLocation configDataLocation) {
//prefix:resource
String resourceLocation = configDataLocation.getNonPrefixedValue(PREFIX);
//判断是否是决定地址
boolean isAbsolute = resourceLocation.startsWith("/") || URL_PREFIX.matcher(resourceLocation).matches();
if (isAbsolute) {
return resourceLocation;
}
ConfigDataResource parent = context.getParent();
if (parent instanceof StandardConfigDataResource) {
String parentResourceLocation = ((StandardConfigDataResource) parent).getReference().getResourceLocation();
String parentDirectory = parentResourceLocation.substring(0, parentResourceLocation.lastIndexOf("/") + 1);
return parentDirectory + resourceLocation;
}
return resourceLocation;
}
//
directory====>classpath:/
profile====>null
configDataLocation===>optional:classpath:/
private Set<StandardConfigDataReference> getReferencesForDirectory(ConfigDataLocation configDataLocation,
String directory, String profile) {
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
//this.configNames: application
for (String name : this.configNames) {
Deque<StandardConfigDataReference> referencesForName = getReferencesForConfigName(name, configDataLocation,
directory, profile);
references.addAll(referencesForName);
}
return references;
}
#name===>application
#directory===>classpath:/
#profile===> null
private Deque<StandardConfigDataReference> getReferencesForConfigName(String name,
ConfigDataLocation configDataLocation, String directory, String profile) {
Deque<StandardConfigDataReference> references = new ArrayDeque<>();
//yaml和properties的解析器
for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
//首先是properties的解析器 propertySourceLoader.getFileExtensions()===>"properties", "xml"
for (String extension : propertySourceLoader.getFileExtensions()) {
StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation, directory,
directory + name, profile, extension, propertySourceLoader);
if (!references.contains(reference)) {
references.addFirst(reference);
}
}
}
return references;
}
private List<StandardConfigDataResource> resolve(StandardConfigDataReference reference) {
//判断路径是否合法
if (!this.resourceLoader.isPattern(reference.getResourceLocation())) {
return resolveNonPattern(reference);
}
return
resolvePattern(reference);
}
private List<StandardConfigDataResource> resolveNonPattern(StandardConfigDataReference reference) {
//resource
// path:application.properties
// classloader:类加载器
Resource resource = this.resourceLoader.getResource(reference.getResourceLocation());
if (!resource.exists() && reference.isSkippable()) {
logSkippingResource(reference);
return Collections.emptyList();
}
return Collections.singletonList(createConfigResourceLocation(reference, resource));
}
#getReferences的最终返回结果
#reference
#referencesForName
#getResolvers()的返回值
#ConfigDataResolutionResult类介绍
class ConfigDataResolutionResult {
//被解析文件的位置
private final ConfigDataLocation location;
//被解析文件的属性key-value
private final ConfigDataResource resource;
private final boolean profileSpecific;
ConfigDataResolutionResult(ConfigDataLocation location, ConfigDataResource resource, boolean profileSpecific) {
this.location = location;
this.resource = resource;
this.profileSpecific = profileSpecific;
}
ConfigDataLocation getLocation() {
return this.location;
}
ConfigDataResource getResource() {
return this.resource;
}
boolean isProfileSpecific() {
return this.profileSpecific;
}
}
#load
private Map<ConfigDataResolutionResult, ConfigData> load(ConfigDataLoaderContext loaderContext,
List<ConfigDataResolutionResult> candidates) throws IOException {
Map<ConfigDataResolutionResult, ConfigData> result = new LinkedHashMap<>();
//candidates:封装文件路径的集合类
for (int i = candidates.size() - 1; i >= 0; i--) {
ConfigDataResolutionResult candidate = candidates.get(i);
//得到默认默认路径:optional:classpath:/;optional:classpath:/config/
ConfigDataLocation location = candidate.getLocation();
//class path resource [application.properties]
ConfigDataResource resource = candidate.getResource();
if (resource.isOptional()) {
this.optionalLocations.add(location);
}
if (this.loaded.contains(resource)) {
this.loadedLocations.add(location);
}
else {
try {
ConfigData loaded = this.loaders.load(loaderContext, resource);
if (loaded != null) {
this.loaded.add(resource);
this.loadedLocations.add(location);
result.put(candidate, loaded);
}
}
catch (ConfigDataNotFoundException ex) {
handle(ex, location, resource);
}
}
}
return Collections.unmodifiableMap(result);
}
<R extends ConfigDataResource> ConfigData load(ConfigDataLoaderContext context, R resource) throws IOException {
ConfigDataLoader<R> loader = getLoader(context, resource);
this.logger.trace(LogMessage.of(() -> "Loading " + resource + " using loader " + loader.getClass().getName()));
return loader.load(context, resource);
}
#真正加载文件属性的方法
@Override
public ConfigData load(ConfigDataLoaderContext context, StandardConfigDataResource resource)
throws IOException, ConfigDataNotFoundException {
if (resource.isEmptyDirectory()) {
return ConfigData.EMPTY;
}
ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, resource.getResource());
//reference包含的属性
// configDataLocation optional:classpath:/
// resourceLocation classpath:/application.properties
// directory classpath:/
// profile null
// propertySourceLoader properties的加载器
StandardConfigDataReference reference = resource.getReference();
//构架新的Resouce
Resource originTrackedResource = OriginTrackedResource.of(resource.getResource(),
Origin.from(reference.getConfigDataLocation()));
//name的值 ==> Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'
String name = String.format("Config resource '%s' via location '%s'", resource,
reference.getConfigDataLocation());
List<PropertySource<?>> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource);
PropertySourceOptions options = (resource.getProfile() != null) ? PROFILE_SPECIFIC : NON_PROFILE_SPECIFIC;
return new ConfigData(propertySources, options);
}
name:不在往下追加,读取文件内容
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
List<Map<String, ?>> properties = loadProperties(resource);
if (properties.isEmpty()) {
return Collections.emptyList();
}
List<PropertySource<?>> propertySources = new ArrayList<>(properties.size());
for (int i = 0; i < properties.size(); i++) {
String documentNumber = (properties.size() != 1) ? " (document #" + i + ")" : "";
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
Collections.unmodifiableMap(properties.get(i)), true));
}
return propertySources;
}
private List<Map<String, ?>> loadProperties(Resource resource) throws IOException {
//application.properties
String filename = resource.getFilename();
List<Map<String, ?>> result = new ArrayList<>();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
result.add((Map) PropertiesLoaderUtils.loadProperties(resource));
}
else {
List<Document> documents = new OriginTrackedPropertiesLoader(resource).load();
documents.forEach((document) -> result.add(document.asMap()));
}
return result;
}
经过上述步骤就已经将yml或者properties中的配置信息读取到了contributor中
SpringAware的调用时间
ApplicationContextAwareProcessor处理
private void invokeAwareInterfaces(Object bean) {
//感知当前的环境在Bean生命周期中的初始化阶段被ApplicationContextAwareProcessor调用postProcessBeforeInitialization方法执行
//Environment: 具有判断当前环境和提供properties属性访问的能力
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
//
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
//感知资源访问器
//在Bean生命周期中的初始化阶段被ApplicationContextAwareProcessor调用postProcessBeforeInitialization方法执行
//ResourceLoader: 具有访问本地、classpath、WEB-INF目录下资源的能力
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
//感知应用的事件发布器
//在Bean生命周期中的初始化阶段被ApplicationContextAwareProcessor调用postProcessBeforeInitialization方法执行
//ApplicationEventPublisher: 具有发布事件的能力
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
//感知支持国际化消息解析器
* 在Bean生命周期中的初始化阶段被ApplicationContextAwareProcessor调用postProcessBeforeInitialization方法执行
* MessageSource: 具有支持国际化的消息解析的能力
*/
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
感知Spring应用上下文
* 在Bean生命周期中的初始化阶段被ApplicationContextAwareProcessor调用postProcessBeforeInitialization方法执行
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
initializeBean#invokeAwareMethods(beanName, bean)
if (bean instanceof Aware) {
//感知Bean实例自身的BeanName,在Bean生命周期中的初始化阶段执行
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
//感知Bean实例自身的ClassLoader,在Bean生命周期中的初始化阶段执行
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
//感知Bean实例自身所在的BeanFactory,在Bean生命周期中的初始化阶段执行
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
使用@MapperScan配置多数据源
@Configuration
@MapperScan(basePackages = "com.xxx", annotationClass = DatasourceRead.class, sqlSessionFactoryRef = DatasourceNewsReadConfig.SQL_SESSION_FACTORY_NAME)
public class DatasourceNewsReadConfig
{
public static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryNewsRead";
public static final String TX_MANAGER = "txManagerNewsRead";
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Autowired
private MybatisProperties properties;
@Bean(name = "datasourceNewsRead")
@ConfigurationProperties(prefix = "spring.datasource.newsread")
@Primary
public DataSource datasource() {
return DataSourceBuilder.create().build();
}
@Bean(name = TX_MANAGER)
@Primary
public PlatformTransactionManager txManagerUser() {
return new DataSourceTransactionManager(datasource());
}
@Bean(name = DatasourceNewsReadConfig.SQL_SESSION_FACTORY_NAME)
@Primary
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(datasource());
sqlSessionFactoryBean.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
sqlSessionFactoryBean.setMapperLocations(this.properties.getMapperLocations());
return sqlSessionFactoryBean.getObject();
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
使用@MapperScan配置多数据源
```java
@Configuration
@MapperScan(basePackages = "com.xxx", annotationClass = DatasourceRead.class, sqlSessionFactoryRef = DatasourceNewsReadConfig.SQL_SESSION_FACTORY_NAME)
public class DatasourceNewsReadConfig
{
public static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryNewsRead";
public static final String TX_MANAGER = "txManagerNewsRead";
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Autowired
private MybatisProperties properties;
@Bean(name = "datasourceNewsRead")
@ConfigurationProperties(prefix = "spring.datasource.newsread")
@Primary
public DataSource datasource() {
return DataSourceBuilder.create().build();
}
@Bean(name = TX_MANAGER)
@Primary
public PlatformTransactionManager txManagerUser() {
return new DataSourceTransactionManager(datasource());
}
@Bean(name = DatasourceNewsReadConfig.SQL_SESSION_FACTORY_NAME)
@Primary
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(datasource());
sqlSessionFactoryBean.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
sqlSessionFactoryBean.setMapperLocations(this.properties.getMapperLocations());
return sqlSessionFactoryBean.getObject();
}
}