SpringBoot2.5原理讲解

SpringBoot原理讲解

alt

一、SpringBoot是什么?

Spring Boot是Spring公司的一个顶级项目,和Spring Framework是一个级别的。

Spring Boot实际上是利用Spring Framework 自动配置特性完成。编写项目时不需要编写xml文件。发展到现在,Spring Boot已经具有很很大的生态圈,各种主流技术已经都提供了Spring Boot的启动器

​ Spring Boot 利用Spring Framework 自动配置特性完成,多数xml不需要配置,整合其他技术,使用起来更加方便,组装完整环境的一个开发平台

Spring Boot的出现使我们能够将侧重点放在开发上。

Spring Boot启动器帮我们解决了繁琐的导jar包的时间,以及jar包之间存在的冲突问题

在Spring Boot 还没出现前,我们开发时需要对项目环境搭建配置繁琐的xml文件。SpringBoot的自动装配恰好解决了这问题。

SpringBoot的出现让部署web项目变得相对简单。

二、SpringBoot自动装配原理?

我们都知道要启动Springboot应用,在SpringBoot启动类上添加**@SpringBootApplication注解**

那么@SpringBootApplication注解跟SpringBoot的自动装配密切相关。

@SpringBootApplication注解下有几个重要注解:

  • @SpringBootConfiguration 实际就是上封装了@Configuration注解

  • @EnableAutoConfiguration 开启自动装配

  • @ComponentScan 组件扫秒

这里重点关注@EnableAutoConfiguration 注解,那么@EnableAutoConfiguration 下有注解

有个注解@Import 注解上声明类AutoConfigurationImportSelector.class

我们点进去 找到selectImports(AnnotationMetadata annotationMetadata) 方法

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //加载jar中/META-INF/spring.factores配置类
		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);
	}
//加载Factories
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

  	result = new HashMap<>();
try {
	Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
            //加载Factories
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
                        ....
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			....
		}
		catch (IOException ex) {
			....
		}
		return result;
	}

三、SpringBoot配置文件加载顺序?

关于配置文件加载顺序具体可看实现类:

  • org.springframework.boot.context.config.ConfigFileApplicationListener弃用

  • org.springframework.boot.context.config.ConfigDataEnvironment

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

四、SpringBoot是如何解析yaml、properties文件?以及如何自定义解析配置文件?

解析yaml、properties文件

首先SpringBoot启动时候会加载 SpringBoot包下 META-INF/spring.factories,其中跟加载解析文件相关的类

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader

然后通过springboot 监听器监听事件EnvironmentPostProcessorApplicationListener

调用***ConfigDataLoaders.load()方法一层一层的调用方法, 最终调用到PropertySourceLoader.load()***方法

调用过程

  • ConfigDataImporter.resolveAndLoad(...)

  • ConfigDataLoaders.load()

  • YamlPropertySourceLoader.load(....)PropertiesPropertySourceLoader.load(...)

自定义解析文件

  1. 创建类实现接口 PropertySourceLoader
package com.any;
public class JSONPropertySourceLoader implements PropertySourceLoader {
    @Override
    public String[] getFileExtensions() {
        return new String[]{"json"};
    }

    @Override
    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
        if(resource == null || !resource.exists()){
            return Collections.emptyList();
        }

        Map<String, Object> configs = JSON.parseObject(resource.getInputStream(), Map.class);

        return Collections.singletonList(
                new MapPropertySource(name, configs)
        );
    }
  1. 创建spring.factories文件

alt

org.springframework.boot.env.PropertySourceLoader=\
com.any.JSONPropertySourceLoader #指定SourceLoader类
  1. 编写配置文件application.json

    {
      "spring.application.name": "anywhere"
    }
    

五、SpringBoot启动顺序?

  • 开启计时器 stopWatch.start()

  • 开启监听器 listeners.starting

  • 初始化配置文件 prepareEnvironment

  • 打印Banner printBanner(environment)

  • 创建上下文 createApplicationContext()

  • 刷新上下文 refreshContext(context)

  • 停止计时器 stopWatch.stop()

  • 启动完成

    • listeners.started(context);

    • listeners.running(context)

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch(); //创建计时器,记录一些启动信息
		stopWatch.start();//开始计时
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args); 
		listeners.starting(bootstrapContext, this.mainApplicationClass); //开启监听器
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);//初始化配置并绑定到环境
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);//打印Banner
			context = createApplicationContext();//创建上下文
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);//为刷新上下前做准备
			refreshContext(context);//刷新上下文
			afterRefresh(context, applicationArguments);
			stopWatch.stop();//停止计时
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context); 
			....
		}
		....
		try {
			listeners.running(context); //启动完成通知
		}
		....
		return context;
	}

刷新上下文

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
			// 为刷新上下文做准备
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// 配置BeanFactory
			prepareBeanFactory(beanFactory);
			try {
				......
				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// 实例化和调用所有已注册的Bean
				invokeBeanFactoryPostProcessors(beanFactory);
				//实例化和注册所有Bean
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();
				// 初始化此上下文的消息源。
				initMessageSource();
				// 初始化此上下文的事件多播程序
				initApplicationEventMulticaster();
				// 初始化一些与上下文有特别关系的bean对象(如创建tomcat)
				onRefresh();
				// 检查监听器bean并注册
				registerListeners();
				// 完成此上下文bean工厂的初始化,初始化所有剩余的非懒汉式单例bean
				finishBeanFactoryInitialization(beanFactory);
				// 通知完成此上下文的刷新
				finishRefresh();
			}
			catch (BeansException ex) {
				....
			}

			finally {
				....
			}
		}
	}

全部评论

相关推荐

头像
不愿透露姓名的神秘牛友
03-24 16:20
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务