SpringBoot学习笔记
2023/6/21 1:22:42
本文主要是介绍SpringBoot学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
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
一、初始化器解析
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 中的事件
获取监听器列表
# 调用 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 详解
三、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
- 接口默认方法
- 父类
- 内部类
执行流程解析
七、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")); } }
这篇关于SpringBoot学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-01后台管理开发学习:新手入门指南
- 2024-11-01后台管理系统开发学习:新手入门教程
- 2024-11-01后台开发学习:从入门到实践的简单教程
- 2024-11-01后台综合解决方案学习:从入门到初级实战教程
- 2024-11-01接口模块封装学习入门教程
- 2024-11-01请求动作封装学习:新手入门教程
- 2024-11-01登录鉴权入门:新手必读指南
- 2024-11-01动态面包屑入门:轻松掌握导航设计技巧
- 2024-11-01动态权限入门:新手必读指南
- 2024-11-01动态主题处理入门:新手必读指南