首页 > 其他分享 >SpringBoot学习笔记

SpringBoot学习笔记

时间:2023-06-20 19:55:20浏览次数:47  
标签:java SpringBoot boot springframework 学习 笔记 context SpringApplication org

SpringBoot学习笔记

学习资料分享,一定要点!!!

示例代码跳转链接无效,查看完整笔记点击:
https://gitee.com/pingWurth/study-notes/blob/master/springboot/spring-boot-demo/SpringBoot学习笔记.md


官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/index.html

application.properties 详解:https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html

时序图-SpringBoot启动流程

一、初始化器解析

Demo - Spring 自定义初始化器

  • 加载
# 解析 META-INF/spring.factories 文件中的配置
org.springframework.boot.SpringApplication.setInitializers(SpringApplication.java:1188)
org.springframework.boot.SpringApplication.<init>(SpringApplication.java:268)
org.springframework.boot.SpringApplication.<init>(SpringApplication.java:249)
org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
  • 触发
# DelegatingApplicationContextInitializer Order 为零先执行,它能读取 context.initializer.classes 属性配置,独立完成初始化器的调用
org.springframework.boot.context.config.DelegatingApplicationContextInitializer.initialize(DelegatingApplicationContextInitializer.java:52)
org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:649)
org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:373)
org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)

二、监听器解析

监听器模式代码示例

Demo - Spring 自定义监听器

了解 Spring 中的事件

SpringApplicationEvent 类图

Spring事件发送顺序

获取监听器列表

Spring获取监听器列表

# 调用 supportsEvent 判断监听器是否支持给定事件
org.springframework.context.event.AbstractApplicationEventMulticaster.supportsEvent(AbstractApplicationEventMulticaster.java:304)
org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:240)
org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:196)
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:133)
org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:402)
org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359)
  • supportsEvent 详解

supportsEvent判断监听器是否支持给定事件

三、bean 解析

xml方式启动Spring

annotation方式启动Spring(含多种 bean 的注册方式)

四、启动加载器解析

Demo - Spring自定义启动加载器

ApplicationRunner 和 CommandLineRunner 在 Spring 启动完成后调用

可以捕获启动参数信息

具体方法如下:


public class SpringApplication {
    // ...

    public ConfigurableApplicationContext run(String... args) {
        // ...
        context = createApplicationContext();
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        listeners.started(context, timeTakenToStartup);
        /** ------------------------------------------------ 调用点 */
        callRunners(context, applicationArguments);
        // ...
        return context;
    }

    /**
     * 按 @Order 顺序执行,顺序相同 ApplicationRunner 优先于 CommandLineRunner
     * 
     * @param context  Spring 上下文
     * @param args     启动参数
     */
    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);
            }
        }
    }

    // ...
}

五、属性配置解析

Demo - Spring 属性配置

Environment 的创建和属性的配置

  • 追踪 Environment 实例的创建 - getOrCreateEnvironment

Environment 实例创建出来后,
org.springframework.core.env.AbstractEnvironment.propertySources 字段中就包含了

  • servletConfigInitParams 属性集
  • servletContextInitParams 属性集
  • Jndi 属性集
  • systemProperties 属性集
  • systemEnvironment 属性集
org.springframework.core.env.StandardEnvironment
#customizePropertySources(StandardEnvironment.java:99)

org.springframework.web.context.support.StandardServletEnvironment
#customizePropertySources(StandardServletEnvironment.java:113)

org.springframework.core.env.AbstractEnvironment.<init>(AbstractEnvironment.java:140)
org.springframework.core.env.AbstractEnvironment.<init>(AbstractEnvironment.java:124)
org.springframework.core.env.StandardEnvironment.<init>(StandardEnvironment.java:68)
org.springframework.web.context.support.StandardServletEnvironment.<init>(StandardServletEnvironment.java:67)
org.springframework.boot.ApplicationServletEnvironment.<init>(ApplicationServletEnvironment.java:30)

org.springframework.boot.SpringApplication
#getOrCreateEnvironment(SpringApplication.java:468)

org.springframework.boot.SpringApplication
#prepareEnvironment(SpringApplication.java:336)

org.springframework.boot.SpringApplication
#run
  • 追踪 PropertySource 的配置过程 - configurePropertySources
# 添加合并 defaultProperties 和 命令行参数
org.springframework.boot.SpringApplication.configurePropertySources
org.springframework.boot.SpringApplication.configureEnvironment
org.springframework.boot.SpringApplication.prepareEnvironment
org.springframework.boot.SpringApplication.run

使用 Profile

  • 使用示例

配置属性参数的三种方式

  • 追踪 Profile 处理源码 - org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment
    • since 2.4.0
    • 旧版 org.springframework.boot.context.config.ConfigFileApplicationListener.postProcessEnvironment
org.springframework.boot.context.config.ConfigDataEnvironment.withProfiles(ConfigDataEnvironment.java:275)
org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:231)
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:102)
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:94)

# 由 ApplicationEnvironmentPreparedEvent 事件监听调用
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)

# 发布 environmentPrepared 事件
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85)
org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66)
java.base/java.util.ArrayList.forEach(ArrayList.java:1541)

# 触发监听器
org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120)
org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114)

# environment 准备完成
org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:65)
org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:339)
org.springframework.boot.SpringApplication.run

六、配置类解析

处理 @Configuration 类的核心方法

  • org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass
    • @PropertySource
    • @ComponentScan
    • @Import
    • @ImportResource
    • @Bean
    • 接口默认方法
    • 父类
    • 内部类

执行流程解析

时序图-ConfigurationClassPostProcessor解析

七、Servlet 容器启动解析

启动入口

时序图-Servlet容器启动入口

八、使用 starter

创建一个 starter 的步骤

第 1 步:创建一个 AutoConfiguration 类,用于 starter 项目的初始化工作

@Configuration
@EnableConfigurationProperties(ExampleProperties.class)
public class ExampleAutoConfiguration {

    /**
     * {@link ConditionalOnProperty} 当 spring.studydemo.enabled 值为 true 时才会调用该方法.
     * {@link Bean} 生成由 Spring 容器管理的 Bean
     * <p>
     *
     * @param exampleProperties
     * @return
     */
    @ConditionalOnProperty(prefix = "spring.studydemo", value = "enabled", havingValue = "true")
    @Bean
    public ExampleClient exampleClient(ExampleProperties exampleProperties) {
        return new ExampleClient(exampleProperties);
    }
}

第 2 步:添加属性配置类,用于获取 application.properties 中的配置

/**
 * 配置类,用于读取 application.properties 中的配置信息.
 * <p>
 *
 * @author Ping Wurth
 * @date 2020/1/5 3:23
 */
@Data
@ConfigurationProperties(prefix = "spring.studydemo")
public class ExampleProperties {
    private String name;
}

第 3 步:配置 spring.factories 文件或自定义 Enable 注解

  • resources/META-INF/spring.factories 文件中添加如下配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pingwurth.studydemo.ExampleAutoConfiguration

引入该 starter 包后,项目启动时 spring.factories 会被 Spring 扫描到, ExampleAutoConfiguration 就会被加载。

  • 也可以使用自定义的 Enable 注解,实现开关功能
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({ExampleAutoConfiguration.class})
public @interface EnableExampleClient {

}

在 Spring Boot 启动类上使用该注解, 就可以在启动时激活 ExampleAutoConfig

第 4 步:更细粒度的开关控制

  • @Conditional
  • @ConditionalOnBean
  • @ConditionalOnClass
  • @ConditionalOnMissingBean
  • @ConditionalOnMissingClass
  • ...

本质上都是 @Conditional,可以自定义新的 @ConditionalXXX 注解。

自定义的注解需要用 @Conditional 标记,并设置 value 属性

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	Class<? extends Condition>[] value();
}

需要自己定义一个 Condition 类,实现 matches 方法

九、日志系统解析

日志发展历程

  • JDK1.3 之前(含1.3),通过 System.(out|err).println 打印,存在巨大缺陷
  • 解决系统打印缺陷问题,出现 log4j, 2015 年 8 月停止更新
  • 收到 log4j 影响,SUN 公司推出 java.util.logging, 即 JUL
  • 由于存在两个系统实现,为了解决兼容性问题,推出 commons-logging, 即 JCL, 但存在一定缺陷

  • log4j 作者推出 slf4j, 功能完善兼容性好,成为业界主流
  • log4j 作者在退出 log4j 后进行新的改进思考,退出 logback
  • log4j2 对 log4j 进行重大征集,修复已知缺陷,极大提升性能
  • 最佳组合:slf4j + logback(springboot 使用)、slf4j + log4j2

日志实现寻址

日志寻址

org.slf4j.LoggerFactory.findPossibleStaticLoggerBinderPathSet(LoggerFactory.java:317)
org.slf4j.LoggerFactory.bind(LoggerFactory.java:146)
org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:124)
org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:417)
org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:362)
org.apache.commons.logging.LogAdapter$Slf4jAdapter.createLocationAwareLog(LogAdapter.java:130)
org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:91)
org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:174)

日志配置说明

配置详解 & 参考配置

<logger><root> 说明

<logger name="com.example.controller" level="error" additivity="false">
  <appender-ref ref="APPLICATION" />
</logger>

<root level="warn">
  <appender-ref ref="APPLICATION" />
  <appender-ref ref="STDOUT" />
</root>
  • root 是全局的日志输出配置
  • logger 是局部的(细粒度)配置:
    • additivity=false 表示 <logger> 处理过 <root> 就不需要处理了
    • additivity=true 表示 <logger> 处理过还会让 <root> 处理

<configuration> 说明

<configuration scan="true" scanPeriod="60 seconds" debug="false" />
  • scan: 设置为 true 时,配置文件若发生改变将会重新加载
  • scanPeriod: 扫描时间间隔(监听配置文件变化),不指定时间单位时,默认为毫秒
  • debug: 设置为 true 将打印出 logback 内部日志信息

configuration 子节点说明

  • contextName: 上下文名称
  • property: 属性配置
  • appender: 格式化日志输出
  • root: 全局日志输出配置
  • logger: 具体包或类输出配置

configuration 上下文及属性配置

<contextName>demo</contextName>
<!-- 用来区分不同应用程序的记录,默认为 default -->
<!-- name: 变量名, value: 变量值 -->
<property name="LOG_PATH" value="/tmp/logs" />
<!-- 引入属性资源文件 -->
<property resource="application.properties" />

<!-- 后续配置可以引用 property 定义的变量和资源文件中定义的变量 -->

<property name="LOG_PATH" value="${logging.path}:-${user.home}/${spring.application.name}/logs" />

日志使用说明

  • 损耗字符串拼接性能
logger.debug("xyz " + i + " is " + j);
  • 损耗 if 判断性能
if (logger.isDebugEnabled()) {
    logger.debug("xyz " + i + " is " + j);
}
  • 推荐方式
logger.debug("xyz {} is {}", i, j);

配置实战

按业务类型将日志输出到不同文件

<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
  <!-- 代码中可以通过 MDC.put("bizType", "goods") 来控制日志输出到不同文件 -->
  <discriminator>
    <key>bizType</key>
    <defaultalue>OTHER</defaultalue>
  </discriminator>
  
  <sift>
    <property name="BIZ_FILE" value="${LOG_PATH}/application-${bizType}.log" />
    <appender name="APPLICATION-${bizType}" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>${BIZ_FILE}</file>
      <encoder>
        <pattern>%date{HH:mm:ss} %contextName [%t] %p $logger{36} - %msg%n</pattern>
      </encoder>
      <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${BIZ_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxHistory>7</maxHistory>
        <maxFileSize>50MB</maxFileSize>
        <totalSizeCap>20GB</totalSizeCap>
      </rollingPolicy>
    </appender>
  </sift>
</appender>

<root level="info">
  <appender-ref ref="SIFT" />
</root>
  • 总结

    • 使用 SiftingAppender
    • <discriminator> 中定义好使用的 key
    • <sift> 中给每个业务类型配置 <appender>
    • 在程序上下文中通过 MDC 注入业务信息
  • 扩展 —— MDC 其他用法

/**
 * MDC 线程上下文映射 Thread Context Map.
 * <p>
 * 自定义日志打印格式的时候,如果设置了 %X{example},意味着 example 变量的值需要从 MDC 中取
 * 我们需要向 MDC 中添加 key 为 “example” 的值,否则 %X{example} 取不到值
 *
 * @author ship
 * @date 2021/8/15 0015 9:08
 */
@Component
public class InputMDC implements EnvironmentAware {

    private static Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        InputMDC.environment = environment;
    }

    public static void putMDC() {
        MDC.put("hostname", NetUtils.getLocalHostName());
        MDC.put("ip", NetUtils.getLocalIp());
        MDC.put("applicationName", environment.getProperty("spring.application.name"));
    }

}

标签:java,SpringBoot,boot,springframework,学习,笔记,context,SpringApplication,org
From: https://www.cnblogs.com/pingWurth/p/17494498.html

相关文章

  • NumPy学习12
    今天学习了22,NumPy矩阵乘法23,NumPyIO操作numpy_test12.py:importnumpyasnp'''22,NumPy矩阵乘法矩阵乘法是将两个矩阵作为输入值,并将A矩阵的行与B矩阵的列对应位置相乘再相加,从而生成一个新矩阵。注意:必须确保第一个矩阵中的行数等于第二个矩阵中的列数,否则......
  • Springboot web,三层架构, IOC&DI 使用总结2023
    Springbootweb,三层架构,IOC&DI使用总结2023一.spring.io全家桶springbootspringframework基础框架,配置繁琐,入门难度大--》springbootspringcloudspringsecurityspringdataspring发展到今天是一个生态圈,提供了若干个子项目,每个子项目用于完成特定的功能。二.sp......
  • 复习笔记-Unity
    泛型与Object区别Object是所有类型的基类,泛型是一种数据类型,将类型参数化达到代码复用提高软件开发效率泛型不用装箱拆箱,泛型是替换,将泛型参数替换成具体的类型,并且不需要强制类型转换,并且编译时自动检查类型安全,避免隐性的类型转换异常。Toggle与Button监听事件:Toggle:On......
  • JSON及XML学习总结
    1.手写JSON中字符串转java对象的方式//构建java对象Studentstudent=newStudent();//利用JSON类中的toJSON对象转换成JSON字符串Strings=JSON.toJSON(Student).toString();2.手写java对象转JSON字符串的方式Stringss="{\"skills\":[\"1\",\"2\",\"足球\&qu......
  • Docker --镜像容器学习笔记
    Docker简介准备工作1.前提知识-linux-Git2.课程定位和范围(基于JavaEE方向)-JavaEEjavaSpringMVC/springBoot/mybatis...docker基础篇-DockerGoSwarm/compose/machine/mesos/k8s/---CI/CDjenkinds整合docker高级篇是什么1.问题:为什......
  • Springboot实现WebSocket
    一、什么是webSocketWebSocket是HTML5下一种新的协议(Websocket协议本质上是一个基于tcp的协议),它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的,WebSocket是一个持久化的协议。二、修改配置文件在application.properties,修改内容为:server.port=......
  • 【whale-starry-stl】01天 list学习笔记
    一、知识点1.std::bidirectional_iterator_tagstd::bidirectional_iterator_tag是C++标准库中定义的一个迭代器类型标签,用于标识支持双向遍历的迭代器类型。在C++中,迭代器是一种泛型指针,用于遍历容器中的元素。迭代器类型标签用于标识迭代器的特性,从而在算法中选择合适的......
  • kafka的学习之一_带SASL鉴权的集群安装与启动
    kafka的学习之一_带SASL鉴权的集群安装与启动背景想开始一段新的里程.可能会比现在累,可能会需要更多的学习和努力.kafka可能就是其中之一.自己之前总是畏缩不前.不想面对很多压力.年龄已经很大了,必须得向前看继续努力了.关于kafkakafka是linked开源的一套高效持......
  • 【React工作记录一百一十六】前端小知识点扫盲笔记记录14
    前言我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端巅峰交流群今天继续对前端知识的小结根据每个元素i属性进行排序<!DOCTYPEhtml><htmllang="en"> <head> <metacharset="UTF-8"/> <metahttp-equiv="X-UA-Compatible"content="IE=edge&......
  • 5步带你玩转SpringBoot自定义自动配置那些知识点
    目前SpringBoot框架真的深受广大开发者喜爱,毕竟它最大的特点就是:快速构建基于Spring的应用程序的框架,而且它提供了各种默认的功能和配置,可以让开发者快速搭建应用程序的基础结构。但是,当我们需要自定义一些配置时,我们就需要使用自定义自动配置。今天一定让大家深刻体验干货知识点......