Bootstrap

SpringBoot3

约定大于配置!

一、SpringBoot简介及快速搭建

1. 简介

SpringBoot基于Spring开发,继承了Spring框架原有的优秀特性,但并不是来替代Spring的解决方案,而和Spring框架紧密结合进一步简化了Spring应用的整个搭建和开发过程。其设计目的是用来简化Spring应用的初始搭建一级开发过程,主要是通过提供默认配置等方式让我们更容易使用。

关于SpringBoot有一句很出名的话就是约定大于配置。采用SpringBoot可以大大简化开发模式,它集成了大量常用的第三方配置,所有想集成的常用框架,都有对应的组件支持,例如Redis、MongoDB、Dubbo、kafka、ES等。Springboot应用中这些第三方库几乎可以零配置地开箱即用,大部分的SpringBoot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。另外SpringBoot通过集成大量的框架,使得依赖包的版本冲突以及引用的不稳定性等问题得到了很好的解决。

简化Spring应用开发的一个框架;

对整个企业级开发技术栈的一个大整合;

J2EE开发的一站式解决方案。

优点:

  • 快速构建一个独立的Spring应用程序;
  • 嵌入的Tomcat、Jetty或者Undertow,无需部署WAR文件(install成一个jar包,通过【java -jar 打包的jar包】 命令即可执行);
  • 提供Starter POMs来简化Maven配置和减少版本冲突所带来的问题;
  • 提供生产就绪型功能,如指标、健康检查和外部配置;
  • 无需配置XML(通过JavaConfig代替了)、无代码生成,开箱即用;

2. 微服务

SpringBoot简化了基于Spring开发,这只是最直观的一方面,还有一个方面,也是得力于各微服务组件的支持,这也是谈SpringBoot必谈微服务的原因。(起初是Netfix移植到Spring)可以说是SpringCloud带动了SpringBoot,SpirngBoot成就了SpringCloud。

传统的web开发架构模式的测试部署比较简单,因为不涉及到多个服务的互联互调,只需要把一个包上传到服务器就行了,同样也不会给运维带来麻烦,方便水平扩展,只需要把相同的应用复制多份放到不同的服务器中就达到了扩展的目的。单体应用的缺点也显而易见,容易牵一发而动全身,比如要更改一个小小的功能,就可能需要重新部署整个应用。当然,更大的挑战就是日益增长的用户需求。

3. SpringBoot Hello World

jdk需要17版本

@RestController
public class TestController {
    @RequestMapping("test")
    public String test() {
        return "hello springboot";
    }
}

启动主启动类后,访问:localhost:8080/test  可以看到以下结果: 

以上是在本地IDE中启动访问,还可以将程序打成jar包,放到服务器执行,模拟操作步骤如下:

第一步:打包:install或者package都可以

第二部:打开控制台,或者win+R输入cmd打开运行对话框

第三步:在IDE的控制台或者运行的对话框中输入【java -jar 生成的包名】--> 【回车】,即开启了服务

或 

经过以上操作,再去浏览器访问,即可看到返回的结果。

 通过命令方式启动程序后,终止程序运行的快捷键:Ctrl + C

4. 代码说明

SpringBoot提供了很多场景启动器starter。

所有的springboot都必须要继承spring-boot-starter-parent,它通过spring-boot-dependencies帮助管理了所有springboot内置的依赖的版本,避免了jar包跟jar包之间的依赖冲突。尚未内置的依赖,导入时依旧需要版本信息。该配置生成项目时就有,2.7.17目前(2025年1月)看来最稳定。

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.7.17</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent>

打包springboot项目为一个jar包:

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
	</plugins>
</build>

jar包可以运行的原理:生成的jar包下有一个叫BOOT-INF的文件夹,里面的lib文件夹中放了很多的jar包;META-INF文件夹中的MANIFEST.MF文件:

① 是运行java -jar后会执行的代码,是自定义的类加载器去加载BOOT-INF中lib里的所有jar包;

② 是我们开发的代码项目的主启动类。

综上所述:spring-boot-maven-plugin帮我们把所有依赖的jar统统放在生成的jar文件里面的BOOT-INF\lib。并且在文件MANIFEST.MF中设置了启动类—JarLauncher,自定义加载器去加载所有的jar;和调用start-class. SpringBoot内嵌Tomcat。

在application.properties中设置懒加载:

spring.main.lazy-initialization=true

作用:在使用/访问的时候才创建bean,而不是在Srpingboot加载的时候创建。

自定义SpringApplication主启动类:

public static void main(String[] args) {
	SpringApplication app = new SpringApplication(Springboot2025Application.class);
	app.setBannerMode(Banner.Mode.OFF);  // 可以关闭springboot启动横幅
	app.run(args);
}

可以通过自定义主启动类的方式填加监听器、加载外部配置文件等。

二、SpringBoot配置文件和自动配置原理

配置文件的使用

SpringBoot使用一个全局的配置文件:核心配置文件,配置文件名在约定的情况下名字是固定的;

配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层给我们自动配置好。

application.properties

application.yml

YAML :Ain't Markup Language,是一个标记语言,

1. 两种配置文件的格式

(属性和值都是大小写敏感的)

在springboot框架中,resource文件夹里可以存放配置的文件有两种:properties和yml

① application.properties的用法:扁平的k/v格式

server.port=8099

② application.yml的用法:树形结构(冒号和属性值之间有空格)

server:
  port: 8080

以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的。

如果有特殊字符%&,要用单引号包起来 

2. 配置文件的加载顺序

<includes>
    <include>**/application*.yml</include>
    <include>**/application*.yaml</include>
    <include>**/application*.properties</include>
</includes>

如果同时存在不同后缀的文件按照这个顺序加载主配置文件,互补配置。

3. 外部约定配置文件加载顺序

springboot启动还会扫描以下位置的application.properties或者application.yml文件作为SpringBoot的默认配置文件

  1. classpath类根路径下的
  2. classpath类路径/config包下的
  3. 项目根目录:如果当前项目是继承/耦合关系maven项目的话,项目根目录=父maven项目的根目录
  4. 项目根目录/config
  5. 直接子目录/config的子目录:java -jar 打包的jar包 --server.port=8098 

优先级由低到高,高优先级的配置会覆盖低优先级的配置。3和4一般不用,打jar的时候,这两个位置的配置文件也不会打到包里,5的方式是在运行jar包的时候,以【--】开始写上配置信息,该配置信息的优先级最高。第5个优先级还可以指定外部配置文件的位置,如果是文件夹,最后需要以“/”结尾:

4. Profile文件的加载

Profile的意思是配置,对于应用程序来说,不同的环境需要不同的配置,SpringBoot框架提供了profile的管理功能,可以使用profile功能来区分不同环境的配置。

① 多Profile文件

Spring官方给出的语法规则是application-(profile).properties (.yaml / .yml)。

② 如果需要创建自定义的properties文件时,可以用application-xxx.properties的命名方式,根据实际情况,公司一般会创建开发环境下使用的properties文件和一个生产环境下使用的properties文件,其中只对端口进行了配置,例如

dev为开发环境,prod为生产环境,默认识别的是application.yml配置文件,三个文件具体的配置如下:

application-dev.yml

server:
  port: 8080
#开发环境的数据库信息等

application-prod.yml

server:
  port: 8081
#生产环境的数据库信息等

application.yml  (意为使用生产环境application-prod.yml的配置)

spring:
  profiles:
    active: prod

经上操作,运行时启动的是生产环境的端口:

5. 配置文件值注入

将yaml映射到属性

① 字面量:普通的值(数字、字符串、布尔) 

k: v字面直接来写;字符串默认不用加上单引号或者双引号;
""双引号:不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思

name: "zhangsan \n lisi"

输出:zhangsan 换行 lsi

''单引号:会转义特殊字符,特殊字符最终只是一个普通的字符串数据

name: 'zhangsan \n lisi'

输出:zhangsan \n lisi

② 对象、Map(属性和值)(键值对):

k: v :在下一行来写对象的属性和值的关系;注意缩进;对象还是k: v的方式

friend:
  userName: pattie
  age: 20

行内写法:

info: {userName: pattie, age: 20}

数组:(或List、Set)

用 “-值” 表示数组中的一个元素

pets:
  - cat
  - dog

行内写法:

pets: [cat,dog]

在配置文件中赋值之后,在java文件中如果想用这些值,可以通过以下方式:

① @Value + SPEL 的方式直接绑定springboot配置文件中的值。

public class Friend {
    @Value("${friend.userName}")
    String userName;
    @Value("${friend.age}")
    int age;
}

 ② 在实体类上加注解:@ConfigurationProperties(prefix = "friend") ,其中前缀是配置文件中设置的属性的第一层的名称。该注解常用于bean属性和yml配置文件的绑定,prefix可以指定配置文件中某一个节点,该节点的子节点将自动和属性进行绑定。

@ConfigurationProperties(prefix = "friend")
@Component
@Data
public class Friend {
    String userName;
    int age;
}
friend:
  age: 20
  userName: pattie

 测试类:

@SpringBootTest
class ConfigfileApplicationTests {
    @Autowired
    private Friend friend;
    @Test
    void contextLoads() {
        System.out.println(friend);
    }
}

运行结果:

 

prefix指定的配置文件中某一个节点的子节点可以自动和属性进行绑定,且支持松散绑定,例如配置文件中friend的子节点有四种写法均可支持与属性值userName进行绑定,分别为:USERNAME、userName、user_name、user-name。

@Value 获取值和 @ConfigurationProperties(prefix = "friend") 获取值的比较:

@ConfigurationProperties@Value
绑定批量注入配置文件中的属性一个个指定
松散绑定支持有限支持
SpEL不支持支持
自动提示支持不支持
JSR303数据校验支持支持

@ConfigurationProperties 支持自动提示需要加入依赖:(2024版的IDE不用,其他版本不清楚)

<dependency>
	<!-- 会生成META-INF元数据,用于提供idea自动提示配置文件属性-->
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<!--以来不会传播-->
	<optional>true</optional>
</dependency>

 并且在settings中开启一下配置:

③ @PropertySource注解

friend.properties文件

friend.age= ${random.int(99)}
friend.userName: pattie

6. 属性占位符

支持对其他属性的引用,以及设置随机值

@ConfigurationProperties(prefix = "friend")
@Component
@Data
public class Friend {
    String userName;
    int age;
    String hobby;
}
friend:
  age: ${random.int(99)}
  userName: pattie
  hobby: ${friend.userName}的兴趣爱好是睡觉

运行结果:

其他例子如下所示:

JSR303的校验:@Validated (注意:只对@ConfigurationProperties起作用)

包括@NotNull (javax.validation下的注解)  @Nullable  @Email等等

@ConfigurationProperties(prefix = "friend")
@Component
@Data
@Validated    // JSR303的校验
public class Friend {
    String userName;
    int age;
    String hobby;
}

需要导入校验的依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
</dependency>

7. SpringBoot的配置和自动配置原理

spring-boot-starter-web依赖整合了springMVC和内嵌的Tomcat。

@SpringBootApplication
// 由↑↑↑进入↓↓↓
@Target({ElementType.TYPE})   // TYPE意为类,也就是该注解只能用在类上,如果要用在方法上,需要改为METHOD,多个地方时可以配置为数组
@Retention(RetentionPolicy.RUNTIME) // 意为:注解标注的类编译以什么方式保留。 RetentionPolicy有三个属性值:SOURCE,CLASS,RUNTIME。SOURCE为只保留java类,不保留注解;CLASS为会保留注解在类文件中,但是不会被java虚拟机加载到,通过反射的方式是找不到的。RUNTIME既会被保留在原文件,也会被加载到JVM中。
@Documented    // java doc 会生成注解信息
@Inherited     // 是否会被继承
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

@SpringBootConfiguration:SpringBoot的配置类;标注在某个类上,表示这是一个SpringBoot的配置类。

  • @Configuration:配置类上来标注这个注解;配置类——配置文件;配置类也是容器中的一个组件@Component
     

@ComponentScan:扫描包,相当于spring.xml配置中的<context:component-scan>,但是并没有指定basepackage,如果没有指定,spring底层会自动扫描当前配置类所有在的包

  • TypeExcludeFilter:SpringBoot对外提供的扩展类,可以供我们去按照自己的方式进行排出。
  • AutoConfigurationExcludeFilter:排除所有配置类并且是自动配置类里面的其中一个。

@EnableAutoConfiguration:开启自动配置功能;以前需要配置的东西,SpringBoot帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,自动加载配置类,这样自动配置才能生效。

未完待续……

三、SpringBoot日志

1. SpringBoot中的devtools热部署

为了进一步提高开发效率,springboot为我们提供了全局项目热部署,日后在开发过程中修改了部分代码以及相关配置文件后,不需要每次重启使修改生效,在项目中开启了springboot全局热部署之后只需要在修改之后等待几秒即可是修改生效

2. 开启热部署

(1)引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
</dependency>

(2)IEDA中配置

当修改了类文件后,IDEA不会自动编译,得修改idea设置

① File-Settings-Compiler-勾选Build Project automatically-----

② ctrl+shift+alt+/,选择Registry,勾上Compiler autoMake allow when app running(2024版的IDEA没有这个选项)

(3)启动项目检测热部署是否生效

3. 混乱的日志系统 

<!--log4j的核心依赖-->
<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>
<!--slf4j的核心依赖-->
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.36</version>
</dependency>
<!--添加slf4j-log4j桥接器-->
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
</dependency>
<!--引入JCL门面依赖-->
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>
<!--为了日志统一实现,将JCL转化到SLF4J,添加JCL-SLF4J的适配器-->
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>jcl-over-slf4j</artifactId>
	<version>1.7.30</version>
</dependency>

4. SpringBoot的默认日志框架

logback

总结:

(1)SpringBoot底层也是使用slf4j + logback的方式进行日志记录

  • logback桥接:logback + classic

(2)SpringBoot也把其他的日志都替换成了slf4j;

  • log4j适配:log4j-over-slf4j
  • jul适配:jul-to-slf4j
  • jcl-over-slf4j
  • 这两个适配器都是为了适配Spring的默认日志:jcl

@Log4j注解依赖于Lombok插件。

控制权限范围:该范围还可以自定义自己创建的路径的权限,在level下层级接自己的包路径即可。

logging:
  level:
    root: debug

日志格式:

详细介绍:

  • 日期和时间:毫秒精度,易于排序。
  • 日志级别:ERROR  WARN  INFO  DEBUG  TRACE
  • 进程ID
  • 一个---分离器来区分实际日志消息的开始
  • 线程名称:用方括号括起来(对于控制台输出可能会被截断)。
  • 记录器名称:这通常是源类名称(通常缩写)。
  • 日志消息。

可以使用以下配置来更改默认的控制台的日志的格式:

logging:
  pattern:
    console: 

5. 日志文件输出

默认情况下,SpringBoot仅记录到控制台,不写日志文件,如果除了控制台输出外还想写日志文件,则需要设置一个logging.file.name或者logging.file.path属性。

logging.file.name

  • 可以设置文件的名称,如果没有设置路径会默认在项目的相对根路径下
  • 还可以指定路径+文件名:name: D:/wlp.log

logging.file.path

  • 不可以指定文件名称,必须要指定一个物理文件夹路径,会默认使用spring.log

6. 日志迭代

(基于logback生效)

logging:
  logback:
    rollingpolicy:
      clean-history-on-start:   # 如果应在应用程序启动时进行日志归档清理
      file-name-pattern:  #归档的文件名
      max-file-size:    #归档前日志文件的最大大小
      max-history:      #保留日志存档的天数(默认为7)
      total-size-cap:   #删除日志档案之前可以使用的最大大小

 7. 自定义日志配置文件

可以通过在类路径中包含日志配置文件来激活各种日志记录系统。

如果使用自定义日志配置文件,会使用SpringBoot中全局配置文件application.yml或application.properties的logging相关配置失效。

结合SpringBoot提供Profile来控制日志的生效:注意一定要将日志配置文件的名字改为logback-spring.xml,因为logback.xml会在SpringBoot容器加载前先被logback给加载到,那么由于logback无法解析SpringProfile将会报错。

8. 切换日志框架

(1)logback切换成log4j2

  1. 将logback的场景启动器排除(slf4j只能运行有一个桥接器)
  2. 添加log4j2的场景启动器
  3. 添加log4j2的配置文件

(2)logback切换成log4j

  1. 将要logback的桥接器排除
  2. 添加log4j的桥接器
  3. 添加log4j的配置文件
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<exclusion>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
</dependency>

log4j.properties配置文件

 

2. 测试

;