Spring Boot源码解读——SpringApplication

本文深入解析Spring Boot的启动过程,重点分析@SpringBootApplication注解的组成及SpringApplication.run()方法中的6个关键步骤:获取运行监听器、准备环境、创建应用上下文、准备上下文、刷新上下文和调用运行器。通过这些步骤,理解Spring Boot如何初始化并启动应用程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


自从 Spring Boot问世以来,已经有越来越多的项目完成了从 SpringMVC迁移到 Spring Boot的迁移过程。本系列将从源码(spring boot 2.1.3.RELEASE)的角度来聊聊 Spring Boot是如何让我们的开发体验变得如此效率。

主函数

package com.boot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


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

以上这段代码,就已经完成了一个最简单的Spring Boot应用(虽然没有任何功能,但已经能够正常启动,并可以通过Web浏览器访问了)。越是简洁的代码,其背后往往隐藏的是越复杂的实现。

在这段代码中,我们可以很清晰的看到,用Spring Boot构建Web应用,只需要2个步骤

  1. 创建主类,并构建主函数,用SpringApplication提供的静态方法run()运行项目。
  2. 在主类上,使用**@SpringBootApplication**注解

接下来,我们来看看,这两个类的特别之处吧。

@SpringBootApplication

@SpringBootApplication.java

package org.springframework.boot.autoconfigure;

//import ...

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	//...
}

从源码中,我们可以看到,该注解的作用实际上就是耦合**@SpringBootConfiguration**,@EnableAutoConfiguration@ComponentScan这几个注解的功能。

@SpringBootConfiguration.java

package org.springframework.boot;

//import ...

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

该注解的作用同**@Configuration**。

@EnableAutoConfiguration.java

package org.springframework.boot.autoconfigure;

//import ...

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	Class<?>[] exclude() default {};

	String[] excludeName() default {};
  
}

该注解是用来标识项目中使用Spring的自动装载功能。这也是Spring Boot项目开发简单的关键注解。

@ComponentScan.java

该注解,实际上也没啥可提的,其实现的功能就是自动扫描。值得提一下的就是,可以通过配置basePackages或者basePackageClasses来设置需要扫描的包目录**(在Spring Boot中,默认扫描主类所在的包下的同级类以及子包)**。

好了,到这里,注解这块,我们就聊得差不多了,下面聊聊SpringApplication吧,它是整个项目启动的核心,可以说没有它,Spring Boot也就无从谈起了。

SpringApplication

上面已经提到,Spring Boot项目启动,实际上是托管于SpringApplication.java这个类的静态方法run(),下面我们来看看**run()**方法的实现吧

实现代码

public class SpringApplication {
	  public ConfigurableApplicationContext run(String... args) {
      StopWatch stopWatch = new StopWatch();	//计时工具
      stopWatch.start();	//开始计时
      
      ConfigurableApplicationContext context = null;
      Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
      
      //设置java.awt.headless,默认为true(没有图形化界面)
      configureHeadlessProperty();
      
      //Key 1 : 获取启动过程监听器
      SpringApplicationRunListeners listeners = getRunListeners(args);
      listeners.starting();	//执行开始启动事件
      
      try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        
        //KEY 2: 初始化Environment
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
        
        //设置spring.beaninfo.ignore,默认为true
        configureIgnoreBeanInfo(environment);
        
        //打印Banner图
        Banner printedBanner = printBanner(environment);
        
        //KEY 3: 创建ApplicationContext
        context = createApplicationContext();
        
        //获取异常记录器
        exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
        
        //KEY 4 : 初始化ApplicationContext
        prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
        
        //KEY 5 : 刷新ApplicationContext
        refreshContext(context);
        
        //刷新ApplicationContext后,回调事件(默认为空方法,可以自定义实现)
        afterRefresh(context, applicationArguments);
        stopWatch.stop();	//停止计时
        if (this.logStartupInfo) {
          new StartupInfoLogger(this.mainApplicationClass)
              .logStarted(getApplicationLog(), stopWatch);
        }
        
        //执行启动后事件
        listeners.started(context);
        
        //Key 6 : 启动后置处理
        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;
	}
	
  /**
  	* 静态方法run入口
  	* @param primarySource 主类(main方法所在的类)
  	* @param args 启动参数
  	*/
	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
    //调用重载方法run()
		return run(new Class<?>[] { primarySource }, args);
	}

 	/**
  	* @param primarySource 主类(main方法所在的类)
  	* @param args 启动参数
  	*/
	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
    //创建SpringApplication实例,并调用run()方法
		return new SpringApplication(primarySources).run(args);
	}
}

从以上源码,可以看到SpringApplication.java在启动项目的过程中,主要有6个核心操作,下面将针对每一点做扩展解读

KEY 1 : getRunListeners()

实现代码

public class SpringApplication {
  private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}
  
  private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
    //获取ClassLoader
		ClassLoader classLoader = getClassLoader();
    
    //获取指定类型的实现类名(不能通过注册为Spring Bean,需通过META-INF/spring.factories配置)
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //创建指定类型的实现类实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
    
    //对实例排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
}

从源码中,可以看出该方法的作用就是返回SpringApplicationRunListeners的实例,而SpringApplicationRunListeners里存储的是SpringApplicationRunListener的所有实例

SpringApplicationRunListeners.java

class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	public void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

	public void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}

	private void callFailedListener(SpringApplicationRunListener listener,
			ConfigurableApplicationContext context, Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

SpringApplicationRunnerListener.java

public interface SpringApplicationRunListener {
	
   //启动事件
   void starting();

   //Environment初始化完成回调事件
   void environmentPrepared(ConfigurableEnvironment environment);

   //ApplicationContext初始化完成回调事件
   void contextPrepared(ConfigurableApplicationContext context);

   //ApplicationContext加载后回调事件
   void contextLoaded(ConfigurableApplicationContext context);

   //启动完成事件
   void started(ConfigurableApplicationContext context);

   //运行中事件
   void running(ConfigurableApplicationContext context);

   //启动失败事件
   void failed(ConfigurableApplicationContext context, Throwable exception);

}

从以上源码中,可以看出SpringApplicationRunnerListener监听的是整个Spring Boot启动过程。至于实际运用场景,则需要具体问题,具体分析了。而SpringApplicationRunnerListeners的作用,则相当于一个拦截器,将所有的监听请求截获,并转发给所有以注册的监听器里。

KEY 2 :prepareEnvironment()

实现代码

public class SpringApplication {
  private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
  
  private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
    
    //根据web应用类型不同,创建不同的Environment对象
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}
  
  protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		if (this.addConversionService) {
			ConversionService conversionService = ApplicationConversionService
					.getSharedInstance();
			environment.setConversionService(
					(ConfigurableConversionService) conversionService);
		}
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}
}

可以看出,这里初始化Environment对象,实际上,包括四个步骤:

  1. 根据Web应用类型,创建不同Environment实例(正常来说,创建的是**StandardServletEnvironment实例)。
  2. 配置Property Sources
  3. 配置Profiles
  4. 执行Environment对象初始化完成回调事件

这里源码必将直观,就不做展开讲述了。

KEY 3 :createApplicationContext()

实现代码

public class SpringApplication {
  protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
}

这里的话,代码也比较直观,就是根据Web应用类型,创建不同的ApplicationContext实例(正常来说,创建的是AnnotationConfigServletWebServerApplicationContext实例)。

KEY 4 :prepareContext()

实现代码

public class SpringApplication {
  private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
    
    //关联ApplicationContext和Environment实例
		context.setEnvironment(environment);
    
    //在ApplicationContext中设置
    //Bean名称生成器(如果存在),资源加载器(如果存在)以及类型转换服务
		postProcessApplicationContext(context);
    
    //调用所有初始化器
		applyInitializers(context);
    
    //执行ApplicationContext初始化完成事件
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
    
    //添加Spring Boot中的特殊单例Bean:springApplicationArguments 以及 springBootBanner
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
    
    //设置是否允许Bean重载,默认true
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}

    //加载Sources,对于该项目而言,只包含BootApplication这一个类
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
    
    //加载动作 - 构造BeanDefinitionLoader并完成所有sources集合中类的Bean定义的加载
		load(context, sources.toArray(new Object[0]));
    
    //执行ApplicationContext加载后回调事件
		listeners.contextLoaded(context);
	}
  
  /**
  	* 在ApplicationContext中设置
  	* Bean名称生成器(如果存在),资源加载器(如果存在)以及类型转换服务
  	*/
  protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context)
						.setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context)
						.setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		if (this.addConversionService) {
			context.getBeanFactory().setConversionService(
					ApplicationConversionService.getSharedInstance());
		}
	}
  
  protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
					initializer.getClass(), ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}
}

可以看出,这段代码的功能主要还是集中在为后续加载Spring容器做的准备工作上。

KEY 5 :refreshContext()

实现代码

public class SpringApplication {
  private void refreshContext(ConfigurableApplicationContext context) {
    
    //刷新ApplicationContext
		refresh(context);
    
    // 注册一个关闭容器时的钩子函数
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
  
  protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}
}

这段代码主要就是完成对ApplicationContext实例的刷新操作,这里暂时不做展开,先了解即可。

KEY 6 :callRunners()

实现代码

public class SpringApplication {
  private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

	private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}

	private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args.getSourceArgs());
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
		}
	}
}

这里的功能,就是依次调用各种Runner来完成初始化过程中的一些后置处理,这里的处理可以包括但不全包括:Redis键值清理,读取配置文件,数据库连接等操作。

然后,自定义Runner仅能通过实现ApplicationRunnerCommandLineRunner其中之一来实现。这两个接口的区别不大,只是入参不同而已,因此根据需要选择相应的接口实现即可。

ApplicationRunner.java

@FunctionalInterface
public interface ApplicationRunner {

	/**
	 * Callback used to run the bean.
	 * @param args incoming application arguments
	 * @throws Exception on error
	 */
	void run(ApplicationArguments args) throws Exception;

}

CommandLineRunner.java

@FunctionalInterface
public interface CommandLineRunner {

	/**
	 * Callback used to run the bean.
	 * @param args incoming main method arguments
	 * @throws Exception on error
	 */
	void run(String... args) throws Exception;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值