Springboot源码分析第二弹 - 自动配置实现

149 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

文章接上文哈,请先熟读自动装配篇,不然本文比较难以理解。

预先准备

  • MVC项目的web.xml,找到本地文件扫描配置
<context-param>
    <param-name>contextConfigLocation</param-name>
    <!--指定路径下的配置文件 -->
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>

但是在springboot中是没有这种指向配置文件的入口的,那么springboot中的配置文件又是怎么加载到系统里面的呢? 今天来一探究竟吧

自动配置源码分析

  • 入口当然还是main方法,前面的就不多了,一直往下在SpringApplication.run方法里面找到这行代码,从这里开始分析
//找到这里 解析配置文件入口 自动装配之前 ###
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  • 接下来到这里
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
  DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
 // Create and configure the environment
 //获取或者创建环境 这里就无其他web mvc的依赖包 所有得到的是 sevlet
 ConfigurableEnvironment environment = getOrCreateEnvironment();
 configureEnvironment(environment, applicationArguments.getSourceArgs());
 ConfigurationPropertySources.attach(environment);
 //然后进入监听 接下来往这看 ###
 listeners.environmentPrepared(bootstrapContext, environment);
 DefaultPropertiesPropertySource.moveToEnd(environment);
 Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
   "Environment prefix cannot be set via properties.");
 bindToSpringApplication(environment);
 if (!this.isCustomEnvironment) {
  environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    deduceEnvironmentClass());
 }
 ConfigurationPropertySources.attach(environment);
 return environment;
}
  • 一直往下实现了就一个EventPublishingRunListener,看下从那里获取的
//从run方法中就可以看出listeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
 Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    //从 spring.factories 找到SpringApplicationRunListener类名的配置
 return new SpringApplicationRunListeners(logger,
    getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
//其实就这一个 注意加载该类时,会调用该类的构造方法初始化
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
  • 接下来来到EventPublishingRunListener
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
  ConfigurableEnvironment environment) {
 this.initialMulticaster.multicastEvent(
   new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}

//multicastEvent一直往下到SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
 Executor executor = getTaskExecutor();
 //找到ApplicationListeners的监听列表 接下来先看这个 才知道回调到了哪个类 ###
 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  if (executor != null) {
   executor.execute(() -> invokeListener(listener, event));
  }
  else {
   //监听的调用
   invokeListener(listener, event);
  }
 }
}
  • 接下来先看下获取监听列表getApplicationListeners,找到最后一行代码
//获取对应类型的监听
return retrieveApplicationListeners(eventType, sourceType, newRetriever)

//AbstractApplicationEventMulticaster.retrieveApplicationListeners找到这行
//这里就是返回对应的监听了 
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);

  • this.defaultRetriever.applicationListeners的值怎么来的呢? 是什么?
  • 前面spring.factories自动加载时会加载EventPublishingRunListener类,那么肯定会通过构造方法初始化,回到EventPublishingRunListener的构造方法
public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    //getListeners 获取的listener
    for (ApplicationListener<?> listener : application.getListeners()) {
        //在这里添加的 那listeners又是什么呢
        this.initialMulticaster.addApplicationListener(listener);
    }
}

//先看 application.getListeners()取的是SpringApplication里面的this.listeners
//在new SpringApplication(primarySources).run(args);能看到
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //this.listeners = new ArrayList<>(listeners); 
    //其实就是从spring.factories取的ApplicationListener类名的配置
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

//spring.factories中找到ApplicationListener类名的配置 那就是这些了
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
  • 到目前为止,已经获取到自动装配的listener类了,接下来开始分析监听的回调
  • 回到SimpleApplicationEventMulticaster.multicastEvent
//监听的调用 从这里开始
invokeListener(listener, event);

//一直往下 最终到这个
listener.onApplicationEvent(event);
  • 上面说到实现ApplicationListener监听的类比较多,我也是一个一个断点调试,最终找到EnvironmentPostProcessorApplicationListener类是分析配置文件的,其他的类就不一一展开了有兴趣的可以自己多做了解
  • EnvironmentPostProcessorApplicationListener#onApplicationEvent
//这行代码往下
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
 ConfigurableEnvironment environment = event.getEnvironment();
 SpringApplication application = event.getSpringApplication();
 //先看getEnvironmentPostProcessors 获取需要回调集合
 for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
   event.getBootstrapContext())) {
  //回调 
  postProcessor.postProcessEnvironment(environment, application);
 }
}

//获取需要回调的监听类
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
  ConfigurableBootstrapContext bootstrapContext) {
 ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
 //这里扫描对应的类 ###
 EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
 //通过newInstance初始化并返回
 return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
  • 先看下postProcessorsFactory本地变量
private final Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory;

//前面说了该类时自动装配进来的 无参的就会走这个构造方法
public EnvironmentPostProcessorApplicationListener() { 
  //postProcessorsFactory 传参classLoader 执行的是后面这段
  this((classLoader) -> EnvironmentPostProcessorsFactory.fromSpringFactories(classLoader), new DeferredLogs());
 }

//fromSpringFactories
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
  //从配置文件里面取 EnvironmentPostProcessor这个类的
  return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
    SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
 }

//查看spring.factories 发现并没有我们想找的实现类 经过断点调试才发现当前类也是它的实现类
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
  • 在回到当前类EnvironmentPostProcessorApplicationListener#onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
 ConfigurableEnvironment environment = event.getEnvironment();
 SpringApplication application = event.getSpringApplication();
 for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
   event.getBootstrapContext())) {
  //开始回调 
  postProcessor.postProcessEnvironment(environment, application);
 }
}
  • 经过断点回调到ConfigFileApplicationListener#postProcessEnvironment,它也是EnvironmentPostProcessor的实现类
//一直往下到这
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
 RandomValuePropertySource.addToEnvironment(environment);
 //用loader封装后 在加载
 new Loader(environment, resourceLoader).load();
}

//先看构造方法
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
 this.environment = environment;
 this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
 this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
 //加载PropertySourceLoader配置的类
 this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
   this.resourceLoader.getClassLoader());
}

//那就是这两个类
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
  • 接下来继续分析ConfigFileApplicationListener#load()方法
void load() {
 FilteredPropertySource.apply(this.environment, DefaultPropertiesPropertySource.NAME, LOAD_FILTERED_PROPERTY,
   this::loadWithFilteredProperties);
}

private void loadWithFilteredProperties(PropertySource<?> defaultProperties) {
 this.profiles = new LinkedList<>();
 this.processedProfiles = new LinkedList<>();
 this.activatedProfiles = false;
 this.loaded = new LinkedHashMap<>();
 //这里就是加载坏境的 spring.profiles.active 和 spring.config.name
 initializeProfiles();
 while (!this.profiles.isEmpty()) {
  Profile profile = this.profiles.poll();
  if (isDefaultProfile(profile)) {
   addProfileToEnvironment(profile.getName());
  }
   //接下来load 一直往下 外层的load也是一样的功能 ###
  load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
  this.processedProfiles.add(profile);
 }
 load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
 addLoadedPropertySources();
 applyActiveProfiles(defaultProperties);
}


private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
 //先看下getSearchLocations() 我猜应该是对应路径遍历
 getSearchLocations().forEach((location) -> {
  String nonOptionalLocation = ConfigDataLocation.of(location).getValue();
  boolean isDirectory = location.endsWith("/");
  //getSearchNames() 获取需要加载的配置名称
        //进去可以看到默认名称 String DEFAULT_NAMES = "application";
  Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
  //接下来遍历名称去加载 ### 
  names.forEach((name) -> load(nonOptionalLocation, name, profile, filterFactory, consumer));
 });
}

//果不其然 拿到配置所在目录 以后面试问配置加载顺序 直接背下这个类这一行就完事了
private Set<String> getSearchLocations() {
    if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
        return getSearchLocations(CONFIG_LOCATION_PROPERTY);
    }
    Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
    //private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
    locations.addAll(
        asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
   return locations;
  }
  • 再看下一个load
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
  DocumentConsumer consumer) {
 //省略部分代码
 ......
 
 Set<String> processed = new HashSet<>();
 for (PropertySourceLoader loader : this.propertySourceLoaders) {
         //getFileExtensions这里获取文件后缀
  for (String fileExtension : loader.getFileExtensions()) {
   if (processed.add(fileExtension)) {
                //location/路径 name/配置名称 fileExtension/后缀
    loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
      consumer);
   }
  }
 }
}

//前面说了 loader就两个类 先直接看YamlPropertySourceLoader类里的
loader.getFileExtensions()
@Override
public String[] getFileExtensions() {
    return new String[] { "yml", "yaml" };
}
//接下来到 loadForFileExtension
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);
 if (profile != null) {
  // Try profile-specific file & profile section in profile file (gh-340)
  //如果环境为test 那这里就是"classpath:/application + "-" + "test" + .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(loader, previouslyLoaded, profile, profileFilter, consumer);
   }
  }
 }
 // Also try the profile-specific section (if any) of the normal file
 //如果环境为null 那就是"classpath:/application.yml
 load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
  • 再继续load到loadDocuments
//找到这行代码往下
List<Document> documents = loadDocuments(loader, name, resource);

private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
  throws IOException {
 DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
 List<Document> documents = this.loadDocumentsCache.get(cacheKey);
 if (documents == null) {
  //接下来看这个 实现类就是上PropertySourceLoader配置的了
        //我们这里是yml那么直接到YamlPropertySourceLoader类吧
  List<PropertySource<?>> loaded = loader.load(name, resource);
  documents = asDocuments(loaded);
  this.loadDocumentsCache.put(cacheKey, documents);
 }
 return documents;
}
  • 接下来到YamlPropertySourceLoader#load
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
 if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", getClass().getClassLoader())) {
  throw new IllegalStateException(
    "Attempted to load " + name + " but snakeyaml was not found on the classpath");
 }
 //这个地方就是解析yaml文件了 得到的果然是一个list map 断点图在下面
 List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
 if (loaded.isEmpty()) {
  return Collections.emptyList();
 }
 List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
 for (int i = 0; i < loaded.size(); i++) {
  String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
  //最后再进行包装
  propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
    Collections.unmodifiableMap(loaded.get(i)), true));
 }
 return propertySources;
}

读取配置断点图

读取配置断点图

概括

  1. 通过SpringApplication构造方法时会手动初始化listeners,即spring.factories中可以为org.springframework.context.ApplicationListener的值,最终保存到本地变量List<ApplicationListener<?>> listeners
  2. 初始化事件监听类EventPublishingRunListener
  3. EventPublishingRunListener类中获取到构造方法保存的监听,然后进行回调
  4. 后面回调到文件处理的监听ConfigFileApplicationListener类,然后通过加载配置中PropertySourceLoader类名的值得到PropertiesPropertySourceLoader,YamlPropertySourceLoader两个配置解析类
  5. 最终交由这两个类分别解析properties后缀和yaml后缀的配置文件

自动配置实现分析就到这里了,这个比自动装配简单多了,主要是要了解spring.factories其中的Listener配置

以上就是本章的全部内容了。

上一篇:Springboot源码分析第一弹 - 自动装配实现 下一篇:Springboot源码分析第三弹 - 自动装配扩展,手动实现一个starter

熟读唐诗三百首,不会作诗也会吟