Spring 源码解析十二(SpringBoot 的加载机制)

Spring 源码解析十二:SpringBoot 的加载机制 1. spring-boot 包含的模块 在解析 SpringBoot 的加载机制之前,先来看看官方 spring-boot 包含有哪些模块,各有什么用。
spring-boot 官方仓库

  • spring-boot:SpringBoot 的核心包,包括对 spring-framework 的封装与扩展、application.yaml 加载机制、注解启动机制等
  • spring-boot-autoconfigureapplication.yaml 的配置项定义
  • spring-boot-actuator:对 Spring 的健康检查、审计、统计和监控
  • spring-boot-actuator-autoconfigure:对 Spring 健康检查、审计、统计和监控的 application.yaml 配置项支持
  • spring-boot-cli:命令行执行支持
  • spring-boot-devtools:开发时监听 class 文件变动自动刷新应用程序
  • spring-boot-starters/*:SpringBoot starter,下面的项目全部是依赖定义,没有代码
  • spring-boot-tools/*:SpringBoot 配套工具
2. 应用入口 一个打包好的应用有如下的基本目录结构
- / - BOOT-INF - classes# 存放应用的class文件与Resources资源文件 - lib# 依赖的jar - META-INF - MANIFEST.MF# 应用的启动配置 - org/springframework/boot/loader/# spring-boot-loader 代码

我们一般可以使用 java -jar app.jar 来启动应用,MANIFEST.MF 就是来配置应用的启动
# MANIFEST.MFManifest-Version: 1.0 Start-Class: com.example.demo.App Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Spring-Boot-Version: 2.1.4.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher

对 Java 来说,这里面的配置,只有 Main-Class 是有效的,就是指定入口类
3. JarLauncher JarLauncher
的主要功能是通过 jar 来启动应用
public class JarLauncher extends ExecutableArchiveLauncher { // 入口启动方法 public static void main(String[] args) throws Exception { new JarLauncher().launch(args); } }

现在来仔细分解这个类,先来看看继承关系
- Launcher - ExecutableArchiveLauncher - JarLauncher

3.1. Launcher
Launcher
的主要功能是实现了基本的启动机制
public abstract class Launcher { // 启动应用 protected void launch(String[] args) throws Exception { // 获取类加载器 ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator()); // 获取启动类 String launchClass = getMainClass(); // 启动 launch(args, launchClass, classLoader); }// 启动 protected void launch(String[] args, String launchClass, ClassLoader classLoader) throws Exception { Thread.currentThread().setContextClassLoader(classLoader); // 创建runner,run createMainMethodRunner(launchClass, args, classLoader).run(); }// 创建runner protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) { return new MainMethodRunner(mainClass, args); }// 获取启动类,由子类实现 protected abstract String getMainClass() throws Exception; }

来看看 MainMethodRunner
是怎么运行的
public class MainMethodRunner { private final String mainClassName; private final String[] args; public MainMethodRunner(String mainClass, String[] args) { this.mainClassName = mainClass; this.args = (args != null) ? args.clone() : null; }public void run() throws Exception { Class mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader()); // 获取mainClass的main方法 Method mainMethod = mainClass.getDeclaredMethod("main", String[].class); mainMethod.setAccessible(true); // 调用main方法 mainMethod.invoke(null, new Object[] { this.args }); }}

3.2. ExecutableArchiveLauncher
ExecutableArchiveLauncher
的主要功能是执行归档文件(jar, war)
public abstract class ExecutableArchiveLauncher extends Launcher { // 启动类 private static final String START_CLASS_ATTRIBUTE = "Start-Class"; // 获取启动类 @Override protected String getMainClass() throws Exception { // 获取归档文件(jar, war)中的`MANIFEST.MF`文件 Manifest manifest = this.archive.getManifest(); String mainClass = null; if (manifest != null) { // 获取Start-Class指定的类作为启动类 mainClass = manifest.getMainAttributes().getValue(START_CLASS_ATTRIBUTE); } if (mainClass == null) { // 启动类不能为空 throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this); } return mainClass; } }

3.3. JarLauncher
JarLauncher
的主要功能是执行 jar 文件,并指定 classpath
public class JarLauncher extends ExecutableArchiveLauncher { // 是否可以加载这下面的文件 static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry) -> { if (entry.isDirectory()) { return entry.getName().equals("BOOT-INF/classes/"); } return entry.getName().startsWith("BOOT-INF/lib/"); }; // "BOOT-INF/classes/"与"BOOT-INF/lib/*.jar"当做classpath加载 @Override protected boolean isNestedArchive(Archive.Entry entry) { return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry); } }

4. 启动应用 我们来看一个最基本的启动
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }

SpringApplication
run 方法由于启动一个 SpringBoot 应用
public class SpringApplication { public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { // 指定启动主源,运行参数 return new SpringApplication(primarySources).run(args); } }

5. SpringApplication.run
public class SpringApplication { public SpringApplication(Class... primarySources) { this(null, primarySources); }public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { // 资源加载器 this.resourceLoader = resourceLoader; // 启动主源 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 获取 spring.factories 文件中定义的,实现Bootstrapper接口的组件 this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); // 获取 spring.factories 文件中定义的,实现ApplicationContextInitializer接口的组件 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 获取 spring.factories 文件中定义的,实现ApplicationListener接口的组件 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 获取入口类文件,如com.example.demo.App.class this.mainApplicationClass = deduceMainApplicationClass(); }// 运行项目 public ConfigurableApplicationContext run(String... args) { // 创建启动上下文 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 应用上下文 ConfigurableApplicationContext context = null; // ... 代码省略// 获取 spring.factories 文件中定义的,实现SpringApplicationRunListener接口的组件 SpringApplicationRunListeners listeners = getRunListeners(args); // 启动监听器 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 应用参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 准备环境对象 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 初始化spring.beaninfo.ignore配置 configureIgnoreBeanInfo(environment); // 创建应用上下文,默认使用 AnnotationConfigServletWebServerApplicationContext context = createApplicationContext(); // 准备应用上下文 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 调用context的refresh方法,刷新上下文数据 refreshContext(context); // ... 代码省略// 调用应用开始的监听函数 listeners.started(context); // 运行应用 callRunners(context, applicationArguments); } catch (Throwable ex) { // ... 代码省略 }try { // 调用应用运行的监听函数 listeners.running(context); } catch (Throwable ex) { // ... 代码省略 } return context; }// 创建启动上下文 private DefaultBootstrapContext createBootstrapContext() { // 默认使用DefaultBootstrapContext,调用bootstrappers中每个组件的intitialize方法初始化 DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext)); return bootstrapContext; } }

public class SpringApplication { // 准备环境对象 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 创建一个spring-web的标准环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 配置环境 configureEnvironment(environment, applicationArguments.getSourceArgs()); // ... 代码省略// 配置 additionalProfiles configureAdditionalProfiles(environment); // 绑定环境与应用 bindToSpringApplication(environment); // ... 代码省略return environment; }// 配置环境 protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { // 添加数据转换与格式化服务 if (this.addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } // 配置命令行的属性来源 propertySources configurePropertySources(environment, args); } }

public class SpringApplication { // 准备应用上下文 private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置环境 context.setEnvironment(environment); // 添加bean名字生成器、资源加载器、数据转换服务 postProcessApplicationContext(context); // 调用initializers中组件的initialize方法 applyInitializers(context); // ... 代码省略// 获取bean工厂 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 注册springApplicationArguments单例 beanFactory.registerSingleton("springApplicationArguments", applicationArguments); // ... 代码省略// 获取所有的源 Set sources = getAllSources(); // 加载这些源下的资源 load(context, sources.toArray(new Object[0])); // ... 代码省略 }// 加载这些源下的资源 protected void load(ApplicationContext context, Object[] sources) { // 创建BeanDefinitionLoader BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); // ... 代码省略// 加载资源 loader.load(); } }
public class SpringApplication { // 运行应用 private void callRunners(ApplicationContext context, ApplicationArguments args) { List runners = new ArrayList<>(); // 应用运行器ApplicationRunner runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 命令行运行器CommandLineRunner runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { // 调用ApplicationRunner.run方法 callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { // 调用CommandLineRunner.run方法 callRunner((CommandLineRunner) runner, args); } } } }
这一小节中,有几点需要接下来慢慢解析
  • AnnotationConfigServletWebServerApplicationContext 如何管理应用与数据
  • BeanDefinitionLoader.load 如何加载资源
6. AnnotationConfigServletWebServerApplicationContext AnnotationConfigServletWebServerApplicationContext
是 SpringBoot 默认使用应用上下文
先看看继承关系
- GenericWebApplicationContext - ServletWebServerApplicationContext - AnnotationConfigServletWebServerApplicationContext

GenericWebApplicationContext
在 Spring 源码解析二:上下文组件(WebApplicationContext) 中已经讲过了,先来看看 ServletWebServerApplicationContext
6.1. ServletWebServerApplicationContext
ServletWebServerApplicationContext
的主要功能是可以启动自身为一个应用服务
public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { // 刷新应用 @Override protected void onRefresh() { super.onRefresh(); // 刷新的时候,重新创建一个应用服务 createWebServer(); }// 创建一个应用服务 private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); // 应用服务与上下文都为空 if (webServer == null && servletContext == null) { // 获取 ServletWebServerFactory 的bean,内置实现有tomcat、jetty、undertow、netty ServletWebServerFactory factory = getWebServerFactory(); // 当应用服务初始化完成后,调用自身的selfInitialize this.webServer = factory.getWebServer(getSelfInitializer()); // ... 代码省略 } // 如果有上下文对象,调用自身的selfInitialize来初始化 else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { // ... 代码省略 } }// ... 代码省略 } }

public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { // 初始化应用 private void selfInitialize(ServletContext servletContext) throws ServletException { // ... 代码省略// 获取所有ServletContextInitializer的bean,调用这些组件的onStartup方法 for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } } }

6.2. AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext
的主要功能是自动扫描注解定义的 bean
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { // 注解bean定义读取器 private final AnnotatedBeanDefinitionReader reader; // 类路径bean定义扫描器 private final ClassPathBeanDefinitionScanner scanner; // 已注册的注解类组件 private final Set> annotatedClasses = new LinkedHashSet<>(); // 待扫描的包 private String[] basePackages; public AnnotationConfigServletWebServerApplicationContext() { // 初始化读取器与扫描器 this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); // 初始化读取器与扫描器 this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }public AnnotationConfigServletWebServerApplicationContext(Class... annotatedClasses) { this(); // 注册bean,并刷新应用上下文数据 register(annotatedClasses); refresh(); }public AnnotationConfigServletWebServerApplicationContext(String... basePackages) { this(); // 扫描包,并刷新应用上下文数据 scan(basePackages); refresh(); } }

public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { // 注册bean @Override public final void register(Class... annotatedClasses) { this.annotatedClasses.addAll(Arrays.asList(annotatedClasses)); }// 扫描包 @Override public final void scan(String... basePackages) { this.basePackages = basePackages; }// 应用上下文刷新完之后,自动加载bean与扫描包 @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); if (this.basePackages != null && this.basePackages.length > 0) { this.scanner.scan(this.basePackages); } if (!this.annotatedClasses.isEmpty()) { this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); } } }

7. BeanDefinitionLoader.load 在上面的代码中,资源加载是类 BeanDefinitionLoader
完成的
class BeanDefinitionLoader { // 加载资源 void load() { // 遍历加载 for (Object source : this.sources) { load(source); } }// 加载单个资源 private void load(Object source) { // 加载类下的资源 if (source instanceof Class) { loadClass((Class) source); return; } // 加载Resource的资源 if (source instanceof Resource) { loadResource((Resource) source); return; } // 加载包下的资源 if (source instanceof Package) { loadPackage((Package) source); return; } // 加载字符代表的资源 if (source instanceof CharSequence) { loadCharSequence((CharSequence) source); return; } // 其他不能加载 throw new IllegalArgumentException("Invalid source type " + source.getClass()); } }

class BeanDefinitionLoader { // 加载类下的资源 private void loadClass(Class source) { // 加载类下的注解 this.annotatedReader.register(source); }// 加载Resource的资源 private void loadResource(Resource source) { // 加载Resource的xml this.xmlReader.loadBeanDefinitions(source); }// 加载包下的资源 private void loadPackage(Package source) { // 扫描包下的注解 this.scanner.scan(source.getName()); }// 加载字符代表的资源 private void loadCharSequence(CharSequence source) { // 替换占位符${} String resolvedSource = this.scanner.getEnvironment().resolvePlaceholders(source.toString()); // 当做类加载,如果成功,返回 try { load(ClassUtils.forName(resolvedSource, null)); return; } catch (IllegalArgumentException | ClassNotFoundException ex) {} // 当做Resource加载,如果成功,返回 if (loadAsResources(resolvedSource)) { return; } // 当做包加载,如果成功,返回 Package packageResource = findPackage(resolvedSource); if (packageResource != null) { load(packageResource); return; } // 其他不能加载 throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'"); } }

后续 更多博客,查看 https://github.com/senntyou/blogs
作者:深予之 (@senntyou)
【Spring 源码解析十二(SpringBoot 的加载机制)】版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)

    推荐阅读