🦉 SpringBoot

吞佛童子2022年10月10日
  • frame
  • SpringBoot
大约 8 分钟

🦉 SpringBoot

1. 概述

  1. SpringBoot 基于 Spring 开发,SpringBoot 本身并不提供 Spring 框架的核心特性 & 扩展功能
  2. SpringBoot 只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序
  3. SpringBoot 并不是用于替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具

优点

  • SpringBoot 遵循 约定大于配置 的核心思想,与 Spring 相比,它有以下优势:
  1. 起步依赖
  2. 自动装配
    • 将原有的 xml 配置改为 Java 配置,将 bean 注入改为 注解注入,并将多个 xml、properties 配置集成在一个 application.yml
  3. 端点监控
    • 基于 actuator 实现
  4. SpringBoot 可 快速创建 独立的 Spring 应用程序
  5. SpringBoot 内嵌 Tomcat 等容器,因此可以 直接启动,简化部署
  6. SpringBoot 提供了一些现有的功能,例如表单数据验证等第三方功能
  7. SpringBoot 可快速整合常用依赖,提供 pom 简化了 maven 的配置

2. 自动装配

1) 原理

  1. 启动类
    • 依赖于 @SpringBootApplication 注解
  2. @SpringBootApplication
    • @SpringBootApplication 是个 复合注解
    • 实现 自动装配 重点依赖于其中一个注解 @EnableAutoConfiguration
  3. @EnableAutoConfiguration
    • 核心是 @Import({AutoConfigurationImportSelector.class}) 注解
    • 通过导入 AutoConfigurationImportSelector 类实现 自动装配 逻辑
  4. AutoConfigurationImportSelector
    • META-INF/spring.factories 中获取 所有满足条件的自动装配类 的路径

2) 自定义 SpringBoot Starter

  1. 创建一个项目,作为自定义的 SpringBoot Starter 类
    • 引入 SpringBoot 相关依赖
    • 编写配置文件类
    • 创建自动配置类
    • 配置自动类
      • /resources/META-INF/spring.factories 文件中添加 自动配置类 路径
  2. 测试
    • 创建一个新项目,导入 自定义 SpringBoot Starter 依赖
    • 在配置文件中添加配置
    • 编写测试类进行测试

3. 启动原理

基于 SpringBoot 2.x

1) ManageApplication 启动类

package com.manage;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ManageApplication {

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

}

2) SpringApplication.run(args)

public class SpringApplication {
    // ...
    // 属性
    private Set<Class<?>> primarySources;
    private Set<String> sources;
    private Class<?> mainApplicationClass;
    private Mode bannerMode;
    private boolean logStartupInfo;
    private boolean addCommandLineProperties;
    private boolean addConversionService;
    private Banner banner;
    private ResourceLoader resourceLoader;
    private BeanNameGenerator beanNameGenerator;
    private ConfigurableEnvironment environment;
    private WebApplicationType webApplicationType;
    private boolean headless;
    private boolean registerShutdownHook;
    private List<ApplicationContextInitializer<?>> initializers;
    private List<ApplicationListener<?>> listeners;
    private Map<String, Object> defaultProperties;
    private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers;
    private Set<String> additionalProfiles;
    private boolean allowBeanDefinitionOverriding;
    private boolean isCustomEnvironment;
    private boolean lazyInitialization;
    private String environmentPrefix;
    private ApplicationContextFactory applicationContextFactory;
    private ApplicationStartup applicationStartup;

    // 构造函数
    public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }
    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 = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 判断应用程序类型,分为 Servlet, Reactive, None 三种枚举类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 从 SpringFactories 加载 初始化器
        this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
        // 设置 应用上下文初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 设置 应用上下文监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // 找到 main 方法,设置为 运行的主类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    
    // 从 SpringFactories 加载 初始化器
    private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
        ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList();
        this.getSpringFactoriesInstances(Bootstrapper.class).stream().map((bootstrapper) -> {
            return bootstrapper::initialize;
        }).forEach(initializers::add);
        initializers.addAll(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        return initializers;
    }
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    // 创建 SpringFactory 集合实例
    private <T> List<T> createSpringFactoriesInstances(Class<T> type, 
                                                       Class<?>[] parameterTypes, 
                                                       ClassLoader classLoader, 
                                                       Object[] args, 
                                                       Set<String> names) {
        List<T> instances = new ArrayList(names.size());
        Iterator var7 = names.iterator();

        while(var7.hasNext()) {
            String name = (String)var7.next();

            try {
                // 通过 类名 & 类加载器 获取 类
                Class<?> instanceClass = ClassUtils.forName(name, classLoader); 
                Assert.isAssignable(type, instanceClass);
                // 获取 类构造器
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                // 内部调用 反射 生成该 SpringFactory
                T instance = BeanUtils.instantiateClass(constructor, args);
                // 添加到结果集中
                instances.add(instance);
            } catch (Throwable var12) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
            }
        }

        return instances;
    }
    
    // 找到 main 方法,设置为 运行的主类
    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }
    
    public ConfigurableApplicationContext run(String... args) {
        // 1. 启动计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 2. 系统属性设置
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        // 3. 初始化监视器并启动,发布 ApplicationStartedEvent 事件
        // 1) 获取所有 监视器
        SpringApplicationRunListeners listeners = this.getRunListeners(args); 
        // 2) starting 函数 - 执行 multicastEvent 方法,唤醒所有的 事件监听器
        //    multicastEvent 方法 会执行 invokeListener 方法
        //    会执行 invokeListener 方法 会 调用 listener.onApplicationEvent(event),只要事件监听器重写了该方法,那么就会执行 具体事件
        listeners.starting(bootstrapContext, this.mainApplicationClass); 

        try {
            // 4. 环境配置,发布 ApplicationEnvironmentPreparedEvent 事件
            // 在这一步中,会触发相关监视器 ConfigFileApplicationListener,加载 properties & yml 文件
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 5. 打印 banner
            Banner printedBanner = this.printBanner(environment);
            // 6. 创建 ApplicationContext
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // 7. 上下文 前置处理,装配 Context 的环境变量等
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            // 8. 刷新 上下文 - 继续进入
            this.refreshContext(context);
            // 9. 上下文刷新的后置处理
            this.afterRefresh(context, applicationArguments);
            // 10. 计时器 结束
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
            // 11. 监视器启动
            listeners.started(context);
            // 12. 执行 runner 运行器运行
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            // 13. 监视器运行
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
}

4. .properties VS yml

1) 加载时间

  • run() 中的 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); 方法会进行加载

2) .properties VS yml

.propertiesyml
语法格式spring.datasource.username=rootspring: datasource: username: root
数据类型基础数据类型对象数组 均可
语言SpringBootJava PHP Python JavaScript Golang
  • yml 文件复杂数据结构的表示
# 数组 & 对象 应用举例
pili:
   figures:
      - liuxianzhishou
      - tunfo
      - xiao
      - fojian
   favoritefigure:
      name: xiao
      location: AoFeng
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou