Spring Boot简介及快速搭建
简介
- SpringBoot它是基于Spring4.0设计,是由Pivotal公司提供的框架。
- Spring发展史,2004开源。
- 2008收购Tomcat Apatch Servlet,掌握整个生态。
- 2009 Spring VMware 4.6美元收购。
- 先后收购RabbitMQ、Redis。
- 2014 Spring Boot1基于Spring4.0 2018 Spring Boot2基于Spring5.0
- 2015 Spring Cloud
- 2018 上市
SpringBoot基于Spring开发,设计的目的就是简化Spring应用的初始化搭建以及开发过程。
SpringBoot约定大于配置,所有你想要继承的常用框架,他都有对应的组件支持,三方库几乎可零配置的开箱即用。
优点
- 快速的构建一个独立的Spring应用程序
- 嵌入的Tomcat、jetty或者Undertow,无需部署WAR文件
- 提供starter POMs来简化Maven配置和减少版本冲突所带来的问题
- 对于Spring和三方库提供默认配置
- 提供生产就绪功能,如指标、健康检查和外部配置
- 无需XMML,无代码生成,开箱即用
为什么使用SpringBoot?
一方面,SpringBoot简化了Spring的开发;另一方面更得力于各位服务组件的支持,这也是说SpringBoot就要谈微服务的原因。可以说是SpringCloud带动了SpringBoot,SpringBoot成就了SpringCloud。
微服务
2014Martin Fowler发表的关于微服务博客,之后被人熟知。
与微服务相对于的就是单体应用,单体应用有它的优势,它的测试与部署比较简单,不涉及多个服务的联调,只需要把一个包上传到服务器上就可以了。并且也方便水平扩展,只需要多复制几份放到不同的服务器上就可以。
缺点就是,牵一发都全身,要改一个小功能就要重新部署整个项目。更大的还是用户的日益增长和用户需求。
快速开始
https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started maven搭建 https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.maven springboot包 https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
有手就行
Spring底层在解析配置类回去解析@ComponentScan,读取basePackage,如果没有读取,则将当前配置类所在的包当做扫描包。
Spring Boot的配置和自动配置原理
https://www.yuque.com/docs/share/d955cbdb-ae23-4100-84a5-130b7ba39049?# 《SpringBoot2自动装配》
Spring Initializer创建Spring Boot
自定义SpringApplication
#懒加载spring.main.lazy-initialization=true
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SpringInitializrApplication.class);
//关闭横幅
springApplication.setBannerMode(Banner.Mode.OFF);
springApplication.run(args);
}
配置文件的使用
SpringBoot使用一个全局的核心配置文件,配置文件的名字在约定的情况下是固定的
- application.properties:格式采用扁平的k/v格式
- application.yml:格式采用树形结构
- application.yaml
YAML语法
- k:(空格)v:表示以应对键值对;
- 以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一层级的
- 属性与值大小写敏感
server: port: 8080 servlet: context-path: /tong配置文件加载顺序
在版本仲裁者下可以看到加载的顺序
spring-boot-starter-parent—->spring-boot-dependencies
外部约定文件加载顺序
SpringBoot启动会扫描一下位置的application.yml,从低到高:
- classpath根目录下

- 在config文件夹下

- 在根目录下
- 项目根目录config
- 直接子目录/config
java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.ymlProfile
我们需要在良种环境下进行切换,只需要在application.yml加如下配置:spring: profiles: active: dev
或者在启动的时候命令行形式
在加载到bean容器可以配置:java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev
所有配置文件按一下顺序考虑:优先级从低到高@Profile("dev")
- jar包之外的配置文件
- 类路径的config
- 类路径
- —spirng.config.location:指定了位置不会与其他配置文件进行互补
配置文件读取方式
1、@PropertySource会和约定的配置文件形成互补 只能是properties的配置文件
@SpringBootApplication
@PropertySource("classpath:appSource.properties")
public class ConfigurationFileApplication {
public static void main(String[] args) throws IOException {
SpringApplication.run(ConfigurationFileApplication.class, args);
}
}
2、默认属性(springApplication.setDefaultProperties)
会与约定的配置文件形成互补
@SpringBootApplication
public class ConfigurationFileApplication {
public static void main(String[] args) throws IOException {
SpringApplication springApplication = new SpringApplication(ConfigurationFileApplication.class);
//创建Properties
Properties properties = new Properties();
//通过当前类的ClassLoader
InputStream inputStream = ConfigurationFileApplication.class.getClassLoader()
.getResourceAsStream("app.properties");
//将输入流都城properties
properties.load(inputStream);
springApplication.setDefaultProperties(properties);
springApplication.run(args);
}
}
3、配置数据(application.properties)
约定的配置文件
4、操作系统环境变量
不会与约定的配置文件互补
idea设置
windows设置
set spring.config.location=D:\config/
5、Java系统属性(System.getProperties())
会使约定的配置文件失效
idea
命令行设置
java -Dspring.config.location=D:/config\ -jar configuration_file-0.0.1-SNAPSHOT.jar
代码
System.setProperty("spring.config.location", "D:/config\\");
6、命令行参数
会使命令行参数失效
java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.yml
7、单元测试使用
@TestPropertySource("classpath:appSource.properties")
配置文件注入
user:
username: 同哥
age: 18
birthday: 2020/01/01
hobbies: [ 唱歌,跳舞 ]
# - 唱歌
# - 跳舞
girl-friends: { 18: 小魔女, 20: 云韵 }
# 18: 小魔女
# 20: 云韵
address:
id: 1
desc: 大别墅
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "user")
public class User {
@Value("${username}")
private String username;
private Integer age;
private Date birthday;
private List<String> hobbies;
private Map<Integer, String> girlFriends;
private Address address;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private Integer id;
private String desc;
}
| @ConfigurationProperties | @Value | |
|---|---|---|
| 功能 | 批量注入配置文件中的属性 | 一个个指定 |
| 松散绑定 | 支持 | 支持有限 |
| SpEL | 不支持 | 支持 |
| 自动提示 | 支持 | 不支持 |
| JSR303数据校验 | 支持 | 支持 |
配置@ConfigurationProperties之后配置yml自动提示
<!--会生成META-INF元数据,用于提供idea自动提示配置文件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<!--依赖不会传播-->
<optional>true</optional>
</dependency>
随机属性与属性引用
my:
secret: "${random.value}"
number: "${random.int}"
bignumber: "${random.long}"
uuid: "${random.uuid}"
number-less-than-ten: "${random.int(10)}"
number-in-range: "${random.int[1024,65536]}"
数据校验
支持JSR-3030 @Validated


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
指定自定义的配置文件
//只支持properties
@PropertySource("classpath:data/user.properties")
Spring Boot的配置和自动配置原理
SpringBootApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
- @Target:设置当前注解课标记在哪
- @Retention:当前注解标注的类编译以什么方式保留
- SOURCE:编译后不会保留,不会加载源文件
- CLASS:不会加载到虚拟机中,通过反射是找不到
- RUNTIME:编译会保留,也会加载到源文件
- @Documented:java doc会生成注解信息
- @Inherited:是否会被继承
- @SpringBootConfiguration:设置为配置类
- @EnableAutoConfiguration:开启自动配置功能
- @Import({AutoConfigurationImportSelector.class})
@ComponentScan:扫描包
- TypeExcludeFilter:SpringBoot提供的扩展类,可以按照我们的方式进行排除
- AutoConfigurationExcludeFilter:排除所有配置类并且是自动配置类中里面的其中一个
@Configuration( proxyBeanMethods = false ) @EnableConfigurationProperties({ServerProperties.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) @ConditionalOnClass({CharacterEncodingFilter.class}) @ConditionalOnProperty( prefix = "server.servlet.encoding", value = {"enabled"}, matchIfMissing = true ) public class HttpEncodingAutoConfiguration {
@Configuration(proxyBeanMethods = false):底层会创建cglib动态代理,防止每次调用Bean重新创建对象
- @EnableConfigurationProperties({ServerProperties.class}):启用在配置类中设置的类
Spring Boot热部署与日志
热部署
1、pom
2、idea设置<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
3、ctrl+alt+shift+/
| 日志实现 | 日志门面 | | —- | —- | | log4j | JCL | | jul lang.util.logging | SJF4J | | log4j2 | | | logback | |
日志
开发标准:记录日志不能直接使用日志实现框架,必须通过日志门面来实现。
logback日志的集成
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging
- SpringBoot底层使用slf4j+logback的方式进行日志记录
- logback桥接:logback-classic
- SpringBoot也把其他的日志替换成了slf4j
- log4j适配:log4j-over-slf4j
- jul适配:jul-tos-slf4j
- 这两个适配器都是为了适配Spring默认的日志:jcl
日志级别
可设置TRACE,DEBUG,INFO,WRAN,ERROR,FATAL,OFF之一。
详细介绍
logging:
pattern:
console: '%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}'
- 2022-02-21 07:45:1.234
- 日期和时间:毫秒精度,易于排序
- %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}
- %clr 当前内容颜色 {faint}
- %d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}
- ${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}
- ${value:value2}SpringBoot的占位符+null条件的表达式(如果value为null使用value2)
- LOG_DATEFORMAT_PATTERN:系统环境变量中的值,底层将对应的项设置到环境变量中
- %d{-yyyy-MM-dd HH:mm:ss.SSS}
- %d:logback的日期显示方式
- {-yyyy-MM-dd HH:mm:ss.SSS}:日期格式
- ${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}
- 日志级别可设置
- TRACE,DEBUG,INFO,WRAN,ERROR
- -%5p:当前内容所占的长度
- 进程ID
- 一个分离器来区分实际日志消息的开始
- 线程名称:用括号括起来(对于控制台输出可能会被截断)
- 记录器名称:这通常是源类名称(通常缩写)
- 日志消息
文件输出
默认情况下,SpringBoot仅记录到控制台,不写日志文件。日过写日志文件,设置logging.file.name或logging.file.path。
- logging.file.name
- 可以设置文件的名称
- 指定路径+文件名
- logging.file.path
- 不可指定名称,必须指定一个物理文件路径
日志迭代
如果使用的是Logback,可以用使用application.yml文件微调日志轮播设置。对于其他记录系统,需要直接自己配置轮转设置(例如,Log4J2,则可以添加log4j.xml文件)
| 名称 | 描述 |
|---|---|
| logging.logback.rollingpolicy.file-name-pattern | 归档的文件名 |
| logging.logback.rollingpolicy.clean-history-on-start | 应用程序启动时进行日志归档清理 |
| logging.logback.rollingpolicy.max-file-size | 归档前日志文件的大小 |
| logging.logback.rollingpolicy.total-size-cap | 删除日志归档之前可以使用的最大大小 |
| logging.logback.rollingpolicy.max-history | 保留日志归档的天数(默认七天) |
- logging.logback.rollingpolicy.file-name-pattern
- 默认:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
- ${LOG_FILE}:对应logging.file.name
- %d{yyyy-MM-dd}:日期
- %i:索引,当文件超出指定大小后进行文件索引递增
- 默认:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
自定义日志
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,比如: 如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
<property name="log.path" value="/var/log/myapp"/>
<!--0. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--2. 输出到文档-->
<!-- 2.1 level为 DEBUG 日志,时间滚动输出 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/debug.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${log.path}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.2 level为 INFO 日志,时间滚动输出 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.3 level为 WARN 日志,时间滚动输出 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/warn.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.4 level为 ERROR 日志,时间滚动输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/error.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.5 所有 除了DEBUG级别的其它高于DEBUG的 日志,记录到一个文件 -->
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/all.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/all-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档记录除了DEBUG级别的其它高于DEBUG的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
</appender>
<!-- 4 最终的策略:
基本策略(root级) + 根据profile在启动时, logger标签中定制化package日志级别(优先级高于上面的root级)-->
<springProfile name="dev">
<root level="info">
<appender-ref ref="CONSOLE"/>
<!-- <appender-ref ref="DEBUG_FILE"/>-->
<!-- <appender-ref ref="INFO_FILE"/>-->
<!-- <appender-ref ref="WARN_FILE"/>-->
<!-- <appender-ref ref="ERROR_FILE"/>-->
<!-- <appender-ref ref="ALL_FILE"/>-->
</root>
<logger name="com.tong.demo" level="debug"/> <!-- 开发环境, 指定某包日志为debug级 -->
</springProfile>
<springProfile name="test">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="ALL_FILE"/>
</root>
<logger name="com.tong.demo" level="info"/> <!-- 测试环境, 指定某包日志为info级 -->
</springProfile>
<springProfile name="pro">
<root level="info">
<!-- 生产环境最好不配置console写文件 -->
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="ALL_FILE"/>
</root>
<logger name="com.tong.demo" level="warn"/> <!-- 生产环境, 指定某包日志为warn级 -->
<logger name="com.tong.demo.MyApplication" level="info"/> <!-- 特定某个类打印info日志, 比如application启动成功后的提示语 -->
</springProfile>
</configuration>
Spring Boot与Web开发
RestTemplate调用REST服务
适用于微服务架构下服务之间的远程调用 ps:以后使用微服务架构spring cloud feign

WebClient也可以调用远程服务,区别:webclient依赖webflux,webclient请求远程服务是无阻塞的,响应的。
RestTemplate它是阻塞的,需要等待请求响应后才能执行下一句代码。
通过MorkMvc测试
MockMvc是有spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller调用,是的测试熟读看、不依赖网络环境。同事提供了一套验证的工具,结果的验证十分方便。
<!--junit5 spring-test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcTests {
@Autowired
MockMvc mockMvc;
@Test
void testMockMvcTest() throws Exception {
//发送一个模拟请求,不依赖网络,不依赖web服务,不需要启动web应用
mockMvc.perform(
MockMvcRequestBuilders.get("/test")
//设置响应文本类型
.accept(MediaType.APPLICATION_JSON_VALUE)
//设置请求文本类型
.contentType(MediaType.APPLICATION_JSON_VALUE)
)
//响应断言
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
swagger调用
<!--swagger2的依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("test-1")
.pathMapping("/")
.select()
.apis(RequestHandlerSelectors.basePackage("com.my.controller"))
.paths(PathSelectors.any())
.build().apiInfo(new ApiInfoBuilder()
.title("tong")
.description("详细信息")
.version("version")
.contact(new Contact("tong", "url", "email"))
.build());
}
}
访问:http://localhost:8080/swagger-ui.html
注解:
- 用于controller类上
- @API
- 用于方法上
- @ApiOperation 方法说明
- @ApilmplicitParams、@ApilmplicitParam 方法参数说明
- @ApiResponses、@ApiResponse 方法返回值说明
对象类
- @ApiModel 用于JavaBean类上
@ApiModelProperty 用于JavaBean类的属性上
AOP
<!--aop的场景启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>@Aspect @Component public class LogAspect { Logger log = LoggerFactory.getLogger(LogAspect.class); @Around("execution(* com.my.controller.*.*(..))&&@annotation(apiOperation)") public Object around(ProceedingJoinPoint joinPoint, ApiOperation apiOperation) throws Throwable { StringBuilder logInfo = new StringBuilder("用户访问了:"); Class<?> aClass = joinPoint.getThis().getClass(); Api api = aClass.getAnnotation(Api.class); if (api != null) { logInfo.append("class:" + api.value()); } String value = apiOperation.value(); logInfo.append("method:" + value); log.info(logInfo.toString()); return joinPoint.proceed(); } }自定义配置
拦截器
perHandle:方法执行前
postHandle:方法执行后,视图渲染前
afterCompletion:试图渲染后public class TimeInterceptor implements HandlerInterceptor { LocalDateTime begin; LocalDateTime end; Logger log = LoggerFactory.getLogger(TimeInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //开始时间 begin = LocalDateTime.now(); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { end = LocalDateTime.now(); Duration between = Duration.between(begin, end); log.info("当前请求执行了:" + between.toMillis() + "ms"); } }@Configuration public class MyWebMvcConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { //拦截规则 registry.addInterceptor(new TimeInterceptor()) //拦截路径 .addPathPatterns("/**"); //.excludePathPatterns("/**");//排除路径 } }跨域请求处理
全局跨域配置
@Override public void addCorsMappings(CorsRegistry registry) { //允许跨域的路径 registry.addMapping("/**") //配置跨域地址 .allowedOrigins("http://localhost:8081") //跨域方法 .allowedMethods("GET", "POST", "DELETE", "PUT"); }方法添加
@GetMapping("/test") @CrossOrigin public String test() { return "string"; }JSON
Gson
- Jackson
- JSON-B
Jackson使用:
- @JsonIgnore:加载属性上,将不会进行json转换
- @JsonFormat(pattern=”yyyy-M-dd hh:mm:ss”,locale=”zh”):进行日期格式化
- @JsonInclude(JsonInclude.Include.NON_NULL):当属性值为null不被包含
- @JsonProperty:为属性添加别名
SpringBoot提供自定义json序列化与反序列化
@JsonComponent
public class UserJsonCustom {
public static class Serializer extends JsonObjectSerializer<User> {
@Override
protected void serializeObject(User value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
}
}
public static class Deserializer extends JsonObjectDeserializer<User> {
@Override
protected User deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, JsonNode tree) throws IOException {
return null;
}
}
}
国际化
spring:
messages:
basename: i18n.message

@Autowired
MessageSource messageSource;
@GetMapping("/test")
public String test() {
String message = messageSource.getMessage("user.query.success", null, LocaleContextHolder.getLocale());
return message;
}
自定义国际化
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
//设置过期时间
localeResolver.setCookieMaxAge(60 * 60 * 24 * 30);
localeResolver.setCookieName("locale");
return localeResolver;
}
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加国际化拦截器
registry.addInterceptor(new LocaleChangeInterceptor())
.addPathPatterns("/**");
}
}
同意异常处理
SpringBoot有统一异常处理了的配置类
嵌入式Servlet容器配置修改
- server.*来进行web服务配置
- 会根据配置文件形成互补
注册Servlet的三大组件
- servlet listener filter
servlet3.0提供的注册
@WebServlet @WebListener @WebFilter public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }springboot提供的注册
public class BeanServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } }@Bean public ServletRegistrationBean myServlet() { ServletRegistrationBean servletServletRegistrationBean = new ServletRegistrationBean<>(); servletServletRegistrationBean.setServlet(new BeanServlet()); servletServletRegistrationBean.setName("BeanServlet"); servletServletRegistrationBean.addUrlMappings("/BeanServlet"); return servletServletRegistrationBean; }
切换其他servlet容器
- SpringBoot包含对嵌入式Tomcat、Jetty和Undertow服务器支持
- Tomcat(默认)
- Jetty(socket)
- Undertow(响应式)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
嵌入式servlet容器自动配置原理
- ServletWebServerFactoryAutoConfiguration
- 使用外部servlet容器
- 安装tomcat 环境变量
- war tomcat webapp starup.sh 启动
Spring Boot集成MyBatis
Druid
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency>spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/test?useUnicode=true&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # 数据源其他配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 druid: stat-view-servlet: enabled: true login-username: root login-password: root #初始时运行sql脚本 sql: init: schema-locations: classpath:sql/schema.sql mode: alwaysMyBatis
自动生成代码<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.1</version> </dependency><!--mybatis-generator插件,自动生成代码--> <build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.0</version> <configuration> <configurationFile>${project.basedir}/src/main/resources/generatorConfig.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> </dependencies> </plugin> </plugins> </build>#驼峰命名 mybatis: configuration: map-underscore-to-camel-case: trueSpring Boot启动原理源码剖析
https://www.yuque.com/docs/share/56012c8f-b238-4da6-95d3-ad1e723812d1?# 《SpringBoot2启动原理》
通过spring-boot-plugin生成MANIFEST.MF文件,其中有main-class指定运行java -jar的主程序,把依赖的jar文件打包在fat jar
JarLauncher通过加载BOOT-INF/classes目录以及BOOT-INF/lib目录下jar文件,实现了 fat jar的启动。
SpringBoot通过扩展JarFile、JarURLConnection及URLStreamHandler,实现了jar in jar中资源的加载。
SpringBoot通过扩展URLClassLoader-LuancherURLClassLoader,实现了jar in jar中class文件的加载。
Spring Boot自定义starters
https://gitee.com/tongtonghushen/tong-springboot.git
