SpringBoot源码解析

SpringBoot源码解析

我们启动SpringBoot的代码:

@SpringBootApplication
public class SpringbootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }

}

我们主要从3大点着手解析:

  • @SpringBootApplication注解
  • 构建SpringApplication对象
  • SpringApplication的run方法

@SpringBootApplication解析

我们来看看SpringBootApplication的注解的代码:

@Target(ElementType.TYPE)    //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) ///表示注解的生命周期,Runtime运行时
@Documented ////表示注解可以记录在javadoc中
@Inherited   //表示可以被子类继承该注解

@SpringBootConfiguration //// 标明该类为配置类
@EnableAutoConfiguration  // 启动自动配置功能
@ComponentScan(excludeFilters = {   // 包扫描器 <context:component-scan base-package="com.xxx.xxx"/>
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication 

排除掉上面的4个元注解外,还剩下3个注解,我们一一分析。

  1. @SpringBootConfiguration

这个里面什么都没有,就只是在这个注解上标明了@Configuration,代表是一个配置类。
2. @EnableAutoConfiguration

这个注解主要是启动自动配置功能,我们点进去详细看看。

@AutoConfigurationPackage		//自动配置包 : 会把@springbootApplication注解标注的类所在包名拿到,并且对该包及其子包进行扫描,将组件添加到容器中
@Import(AutoConfigurationImportSelector.class)  //可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中
public @interface EnableAutoConfiguration {

2.1 我们先看@AutoConfigurationPackage注解

@Import(AutoConfigurationPackages.Registrar.class)  //  默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中
public @interface AutoConfigurationPackage {

}

可以看出主要是引入了Registrar这个类,继续跟踪Registrar这个类

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		// 获取的是项目主程序启动类所在的目录
		//metadata:注解标注的元数据信息
		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			//默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
			register(registry, new PackageImport(metadata).getPackageName());
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}

	}

主要目的获取SpringBoot主程序启动类的包名并注册

2.2 继续看@Import(AutoConfigurationImportSelector.class)

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		//判断 enableautoconfiguration注解有没有开启,默认开启(是否进行自动装配)
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		//1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件
		//作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。
		// SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类
		// 自动配置的类全名.条件=值
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

主要是获取并筛选出自动配置类并返回。

  1. @ComponentScan

这个注解就是包扫描的作用,类似Spring里面的:

<context:component-scan base-package="com.xxx.xxx"/>

构建SpringApplication对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

		this.sources = new LinkedHashSet();
		this.bannerMode = Mode.CONSOLE;
		this.logStartupInfo = true;
		this.addCommandLineProperties = true;
		this.addConversionService = true;
		this.headless = true;
		this.registerShutdownHook = true;
		this.additionalProfiles = new HashSet();
		this.isCustomEnvironment = false;
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");

		//项目启动类 SpringbootDemoApplication.class设置为属性存储起来
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

		//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();

		// 设置初始化器(Initializer),最后会调用这些初始化器
		//所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

		// 设置监听器(Listener)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

		// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

run方法

public ConfigurableApplicationContext run(String... args) {
	    // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置 headless 属性
		configureHeadlessProperty();


		//   (1)获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
		    // 创建  ApplicationArguments 对象 初始化默认应用参数类
			// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			//(2)项目运行环境Environment的预配置
			// 创建并配置当前SpringBoot应用将要使用的Environment
			// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

			configureIgnoreBeanInfo(environment);
			// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
			Banner printedBanner = printBanner(environment);

			// (3)创建Spring容器
			context = createApplicationContext();
			// 获得异常报告器 SpringBootExceptionReporter 数组
			//这一步的逻辑和实例化初始化器和监听器的一样,
			// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);


			// (4)Spring容器前置处理
			//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

			// (5):刷新容器
			refreshContext(context);

			// (6):Spring容器后置处理
			//扩展接口,设计模式中的模板方法,默认为空实现。
			// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
			afterRefresh(context, applicationArguments);
			// 停止 StopWatch 统计时长
			stopWatch.stop();
			// 打印 Spring Boot 启动的时长日志。
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// (7)发出结束执行的事件通知
			listeners.started(context);

			// (8):执行Runners
			//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
			//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
			//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
		    // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

        //   (9)发布应用上下文就绪事件
		//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
		// 这样整个Spring Boot项目就正式启动完成了。
		try {
			listeners.running(context);
		} catch (Throwable ex) {
            // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
            handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		 //返回容器
		return context;
	}
书山有路勤为径,学海无涯苦作舟
原文地址:https://www.cnblogs.com/javammc/p/15659153.html