学习资料分享,一定要点!!!
示例代码跳转链接无效,查看完整笔记点击:
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 自定义监听器
# 调用 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)
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 属性配置
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
configurePropertySources
# 添加合并 defaultProperties 和 命令行参数 org.springframework.boot.SpringApplication.configurePropertySources org.springframework.boot.SpringApplication.configureEnvironment org.springframework.boot.SpringApplication.prepareEnvironment org.springframework.boot.SpringApplication.run
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment
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 @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); } }
/** * 配置类,用于读取 application.properties 中的配置信息. * <p> * * @author Ping Wurth * @date 2020/1/5 3:23 */ @Data @ConfigurationProperties(prefix = "spring.studydemo") public class ExampleProperties { private String name; }
resources/META-INF/spring.factories
文件中添加如下配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.pingwurth.studydemo.ExampleAutoConfiguration
引入该 starter 包后,项目启动时 spring.factories 会被 Spring 扫描到, ExampleAutoConfiguration 就会被加载。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({ExampleAutoConfiguration.class}) public @interface EnableExampleClient { }
在 Spring Boot 启动类上使用该注解, 就可以在启动时激活 ExampleAutoConfig
本质上都是 @Conditional,可以自定义新的 @ConditionalXXX 注解。
自定义的注解需要用 @Conditional 标记,并设置 value 属性
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }需要自己定义一个 Condition 类,实现
matches
方法
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>
additivity=false
表示 <logger>
处理过 <root>
就不需要处理了additivity=true
表示 <logger>
处理过还会让 <root>
处理<configuration>
说明<configuration scan="true" scanPeriod="60 seconds" debug="false" />
<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 (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>
总结
<discriminator> 中定义好使用的 key
<sift>
中给每个业务类型配置 <appender>
扩展 —— 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")); } }