一 SpringBoot介绍
1 Spring缺点分析
Spring是一个非常优秀的轻量级框架,以IOC(控制反转)和AOP (面向切面)为思想内核,极大简化了JAVA企业级项目的开发。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。使用Spring进行项目开发需要在配置文件中写很多代码,所有这些配置都代表了开发时的损耗。
除此之外,Spring项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。比如Spring5.0以上只 能使用Junit4.12以上的版本。
总结 Spring的缺点:
-
配置过于繁琐。
-
引入的依赖过多,版本控制复杂。
2 什么是SpringBoot
SpringBoot对Spring的缺点进行改善和优化,基于约定大于配置的思想,简化了Spring的开发,所谓简化是指简化了Spring中大量的 配置文件和繁琐的依赖引入。所以SpringBoot是一个服务于框架的框架,它不是对Spring功能的增强,而是提供了一种快速使用Spring框架的方式
。
SpringBoot的优点:
- 配置简单
- 依赖引入简单
- 提供了一些大型项目的非功能特性,如嵌入式服务器,安全指标,健康监测等。
3 SpringBoot核心功能
自动配置
SpringBoot项目自动提供最优配置,同时可以修改默认值满足特定的要求。
起步依赖
SpringBoot的依赖是基于功能的,而不是普通项目的依赖是基于 JAR包的。SpringBoot将完成一个功能所需要的所有坐标打包到一 起,并完成了版本适配,我们在使用某功能时只需要引入一个依赖即可。
二 SpringBoot入门
1 通过官网搭建项目
接下来我们搭建一个SpringBoot项目,并引入SpringMVC的功能, 首先我们可以通过官网搭建项目:
① 访问start.spring.io
② 生成SpringBoot项目
SpringBoot版本说明:
- SNAPSHOT:快照版,表示开发版本,随时可能修改;
- M1(Mn):M1是milestone的缩写,也就是里程碑版本;
- RC1(RCn):RC1是release candidates的缩写,也就是发布预览版;
- Release:正式版,也可能没有任何后缀也表示正式版
SpringBoot打包类型说明:
- 使用SpringMVC开发的是一个web项目,但由于在SpringBoot中直接嵌入了Tomcat等 Web容器,所以在使用SpringBoot做Web开发时不需要部署War文件,只需要打成Jar 包即可。
③ 解压生成的SpringBoot项目
④ 在Idea中打开解压的SpringBoot项目即可
2 通过IDEA脚手架搭建项目
我们也可以在IDEA中利用脚手架工具搭建项目:
① 在IDEA中新建项目,项目类型为Spring Initializr,选择JDK版本 和搭建网站后点击下一步。
脚手架工具也会访问SpringBoot官网进行项目搭建,但 SpringBoot官网有时访问速度较慢,我们也可以选择国内的 镜像网站https://start.aliyun.com/搭建项目,阿里的脚手架 镜像SpringBoot版本有些滞后。
② 选择项目参数后,点击下一步
③ 选择SpringBoot版本和需要的起步依赖,点击下一步。
④ 完成项目搭建
3 SpringBoot项目结构
接下来我们分析SpringBoot的项目结构:
POM文件
- SpringBoot项目必须继承spring-boot-starter-parent,即所有 的SpringBoot项目都是spring-boot-starter-parent的子项目。 spring-boot-starter-parent中定义了常用配置、依赖、插件等 信息,供SpringBoot项目继承使用。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- SpringBoot项目中可以定义起步依赖,起步依赖不是以jar包为 单位,而是以功能为单位
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- spring-boot-maven-plugin插件是将项目打包成jar包的插件。 该插件打包后的SpringBoot项目无需依赖web容器,可以直接使用JDK运行
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
启动类
启动类的作用是启动SpringBoot项目,运行启动类的main方法即可 启动SpringBoot项目。
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
配置文件
由于SpringBoot极大简化了Spring配置,所以只有一个 application.properties配置文件,且Spring的自动配置功能使得大 部分的配置都有默认配置,该文件的功能是覆盖默认配置信息,该文件不写任何信息都可以启动项目。 启动后默认端口号为8080,我们可以覆盖该配置:
server.port=8888
4 通过Maven搭建项目
了解了SpringBoot结构后,我们也可以使用Maven手动搭建 SpringBoot项目:
- 使用Maven创建普通JAVA项目
- 在pom中添加项目的父工程、起步依赖、插件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 编写启动类
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args){
SpringApplication.run(SpringBootApp.class, args);
}
}
- 编写配置文件application.properties
#日志格式
logging.pattern.console=%d{MM/dd HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n
#端口号
server.port=8888
- 运行启动类主方法,启动项目
5 编写java代码
之前搭建的SpringBoot项目已经都整合了SpringMVC,我们编写一 个控制器进行测试:
@Controller
public class MyController {
@RequestMapping("/hello")
@ResponseBody
public String hello() {
System.out.println("hello springboot!");
return "hello springboot";
}
}
启动类在启动时会做注解扫描(@Controller、@Service、 @Repository......),扫描位置为同包或者子包下的注解,所以我 们要在启动类同级或同级包下编写代码。
启动项目,访问http://localhost:8888/hello
三 SpringBoot原理分析
1 起步依赖
查看spring-boot-starter-parent起步依赖
- 按住 Ctrl 点击 pom.xml 中的 spring-boot-starter-parent ,跳转到了 spring-bootstarter-parent 的 pom.xml ,发现 spring-boot-starter-parent 的父工程是 spring-boot-dependencies 。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.5</version>
</parent>
- 进入 spring-boot-dependencies 的 pom.xml 可以看到,一部分坐标的版本、 依赖管理、插件管理已经定义好,所以SpringBoot工程继承 spring-boot-starter-parent 后已经具备版本锁定等配置了。所以起步依赖 的作用就是进行依赖的传递。
查看spring-boot-starter-web起步依赖
按住 Ctrl 点击 pom.xml 中的 spring-boot-starter-web ,跳转到了 spring-boot-starter-web 的 pom.xml ,从 spring-boot-starter-web 的 pom.xml 中我们可以发现, spring-boot-starter-web 就是将web开发要使用的 spring-web 、 spring-webmvc 等坐标进 行了打包,这样我们的工程只要引入 spring-boot-starter-web 起步依赖的坐 标就可以进行web开发了,同样体现了依赖传递的作用。
2 自动配置
- 查看注解 @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 {
@SpringBootConfiguration: 等同与@Configuration, 既标注该类是Spring的一个配置类
@EnableAutoConfiguration: SpringBoot自动配置功能开启
- 查看注解@EnableAutoConfiguration的源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import(AutoConfigurationImportSelector.class)导入了AutoConfigurationImportSelector类
- 查看AutoConfigurationImportSelector的源码
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
SpringFactoriesLoader.loadFactoryNames 方法的作用就是从 META-INF/spring.factories 文件中读取指定类对应的类名称列表
- 点开 spring-boot-autoconfigure 的 spring.factories 文件
有关配置类的信息如下:
上面配置文件存在大量的以 Configuration 为结尾的类名称,这些类就是存有自动配置信息的类,而 SpringApplication 在获取这些类名后 再加载。
- 每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:
@ConditionalOnBean:当容器里有指定的bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
- 我们 ServletWebServerFactoryAutoConfiguration为例来分析源码:
@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@EnableConfigurationProperties(ServerProperties.class)代表加载 ServerProperties 服务器配置属性类。
- 进入 ServerProperties 类源码如下:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
/**
* Strategy for handling X-Forwarded-* headers.
*/
private ForwardHeadersStrategy forwardHeadersStrategy;
//其余代码略
prefifix = “server” 表示SpringBoot配置文件中的前缀,SpringBoot会 将配置文件中以server开始的属性映射到该类的字段中。所以配置网络端口的方式为 server.port
- 如果我们没有在配置文件中配置默认端口,SpringBoot就会读 取默认配置,而默认配置存放在
META-INF/spring-configuration-metadata.json
中,打开 spring-boot-autoconfigure 的 spring.factories 文件
该文件中保存的就是所有默认配置信息。
3 核心注解
@SpringBootApplication
标注是SpringBoot的启动类。 此注解等同于
@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
@SpringBootConfiguration
@SpringBootConfiguration 是 @Configuration 的派生注解,跟 @Configuration 功能一 致,标注这个类是一个配置类,只不过 @SpringBootConfiguration 是 Springboot的注解,而 @Configuration 是Spring的注解
@EnableAutoConfiguration
SpringBoot自动配置注解。
等同于 @AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
自动扫描包的注解,它会自动扫描主类所在包下所有加了注解的类 (@Controller,@Service等),以及配置类 (@Configuration)。
@Import({AutoConfigurationImportSelector.class})
该注解会导入 AutoConfifigurationImportSelector 类对象,该对象会从 META-INF/spring.factories 文件中读取配置类的名称列表。
@ComponentScan
该注解会扫描项目,自动装配一些项目启动需要的Bean。
4 总结
自动配置就是自动去把第三方组件的Bean装载到IOC容器里面,不需要开发人员手动再去写Bean相关的配置。
在SpringBoot应用里面,只需要在启动类上去加上@SpringBootApplication注解就可以实现自动配置。@SpringBootApplication注解它是一个复合注解,真正去实现自动配置的注解是@EnableAutoConfiguration注解。
自动配置的实现主要依靠三个核心的关键技术。
- 第一个引入Starter,启动依赖组件的时候,这个组件里面必须包含一个@Configuration配置类,而在这个配置类中需要通过@Bean这个注解去声明需要配置到IOC容器里面的Bean对象。
- 第二个呢,这个配置类是放置第三方的jar包里面,然后通过SpringBoot中得约定优于配置的这样一个理念,去把这个配置类的全路径放在classpath:/META-INF/spring.factories文件里面。这样的话,SpringBoot就可以知道第三方jar包里面这个配置类的位置。这个步骤主要是用到了spring里面的SpringFactoriesLoader来完成的。
- 第三个是SpringBoot拿到所有的第三方jar包里面声明的配置类以后,再通过Spring提供ImportSelector这样一个接口来实现对配置类的动态加载。从而去完成自动配置这样一个动作。
SpringBoot是约定优于配置这一理念下的产物,它的出现让开发人员可以更加聚焦在业务代码的编写上,而不需要去关心和业务无关的配置。
四 YAML文件
1 配置文件介绍
SpringBoot项目中,大部分配置都有默认值,但如果想替换默认配置的话,就可以使用application.properties或者application.yml进行配置。
SpringBoot默认会从resources目录下加载application.properties 或application.yml文件。其中,application.properties文件是键值对类型的文件,之前一直在使用,所以我们不再对properties文件进行阐述。
除了properties文件外,SpringBoot还支持YAML文件进行配置。 YAML文件的扩展名为 .yml 或 .yaml ,它的基本要求如下:
- 大小写敏感
- 使用缩进代表层级关系
- 相同的部分只出现一次
比如使用properties文件配置tomcat端口:
server.port=8888
而使用YAML文件配置tomcat端口:
server:
port: 8888
2 自定义配置简单数据
除了覆盖默认配置,我们还可以在YAML文件中配置其他信息以便我们在项目中使用。配置简单数据的方式如下:
- 语法:
数据名: 值
- 示例代码:
name: lxx
注意:value之前有一个空格
3 自定义配置对象数据
- 语法:
对象:
属性名1: 属性值
属性名2: 属性值
# 或者
对象: {属性名1: 属性值,属性名2: 属性值}
- 示例代码:
# 学生1
student1:
sex: female
age: 10
address: beijing
# 学生2
student2: {sex: female,age: 10,address: beijing}
属性名前面的空格个数不限,在yml语法中,相同缩进代表同一 个级别,只要每个属性前的空格数一样即可。
4 自定义配置集合数据
- 语法
集合:
- 值1
- 值2
# 或者
集合: [值1,值2]
- 示例代码
# 城市
city1:
- beijing
- tianjin
- shanghai
- chongqing
city2: [beijing,tianjin,shanghai,chongqing]
# 集合中的元素是对象
students:
- name: baizhan
age: 18
score: 100
- name: shangxuetang
age: 28
score: 88
- name: chengxuyuan
age: 38
score: 90
注意:值与之前的 - 之间存在一个空格
5 读取配置文件的数据
使用@Value读取
我们可以通过@Value注解将配置文件中的值映射到一个Spring管理 的Bean的字段上,用法如下:
- 配置文件
name: baizhan
student:
sex: female
age: 10
address: beijing
city:
- beijing
- tianjin
- shanghai
- chongqing
students:
- name: baizhan
age: 18
score: 100
- name: shangxuetang
age: 28
score: 88
- name: chengxuyuan
age: 38
score: 90
- 读取配置文件数据:
@Controller
public class YamlController {
@Value("${name}")
private String name;
@Value("${student.age}")
private Integer age;
@Value("${city[0]}")
private String city;
@Value("${students[0].score}")
private Double score;
@RequestMapping("/yaml1")
@ResponseBody
public String yaml1() {
System.out.println(name);
System.out.println(age);
System.out.println(city);
System.out.println(score);
return "hello springboot!";
}
}
@Value只能映射简单数据类型,不能将yaml文件中的对象、集合映射到属性中。
使用@ConfigurationProperties读取
通过 @ConfigurationProperties(prefifix="对象")
可以将配置文件中的配置自动与实 体进行映射,这样可以将yml文件中配置的对象属性直接映射到 Bean当中。
- 配置文件
user:
id: 10001
username: shangxuetang
address:
- beijing
- tianjin
- shanghai
- chongqing
grades:
- subject: math
score: 100
- subject: english
score: 90
- 实体类
public class Grade {
private String subject;
private double score;
// 省略getter/setter/tostring
}
- 读取配置文件
@Controller
@ConfigurationProperties(prefix = "user")
public class YamlController2 {
private Integer id;
private String username;
private List<String> address;
private List<Grade> grades;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getAddress() {
return address;
}
public void setAddress(List<String> address) {
this.address = address;
}
public List<Grade> getGrades() {
return grades;
}
public void setGrades(List<Grade> grades) {
this.grades = grades;
}
@RequestMapping("/yaml2")
@ResponseBody
public String yaml2() {
System.out.println(id);
System.out.println(username);
System.out.println(address);
System.out.println(grades);
return "hello springboot!";
}
}
6 占位符的使用
使用配置文件中的值
- 编写配置文件
server:
port: 8888
myconfig:
myport: ${server.port}
- 读取配置文件
@Controller
public class YamlController3 {
@Value("${myconfig.myport}")
private Integer port;
@RequestMapping("/yaml3")
@ResponseBody
public String yaml3() {
System.out.println(port);
return "hello springboot!";
}
}
使用框架提供的方法
SpringBoot框架提供了一些生成随机数的方法可以在yml文件中使用:
${random.value} :生成类似uuid的随机数,没有"-"连接
${random.uuid} :生成一个uuid,有短杠连接
${random.int} :随机取整型范围内的一个值
${random.int(10)}:随机生成一个10以内的数
${random.int(100,200)}:随机生成一个100-200 范围以内的数
${random.long}:随机取长整型范围内的一个值
${random.long(100,200)}:随机生成长整型100-200范围内的一个值
用法如下:
# 随机生成tomcat端口
server:
port: ${random.int(1024,9999)}
7 配置文件存放位置及优先级
配置文件有如下存放位置:
- 项目根目录下
- 项目根目录下的/config子目录中
- 项目的resources目录中
- 项目的resources下的/config子目录中
这些目录下都可以存放两类配置文件,分别是 application.yml 和 application.properties ,这些配置文件的优先级从高到低依次为:
项目根目录下的/config子目录中
-
config/application.properties
-
config/application.yml
项目根目录下
- application.properties
- application.yml
项目的resources下的/config子目录中
-
resources/config/application.properties
-
resources/config/application.yml
项目的resources目录中
-
resources/application.properties
-
resources/application.yml
优先级高的文件会覆盖优先级低的文件中的配置
8 bootstrap配置文件
SpringBoot中有两种容器对象,分别是bootstrap和application, bootstrap是应用程序的父容器,bootstrap加载优先于 applicaton。bootstrap配置文件主要对bootstrap容器进行配置, application配置文件是对applicaton容器进行配置。
bootstrap配置文件也同样支持properties和yml两种格式,主要用于从外部引入Spring应用程序的配置。
bootstrap配置文件特征
- boostrap由父ApplicationContext加载,比applicaton优先加载。
- boostrap里面的属性不能被覆盖。
bootstrap与application的应用场景
- application配置文件主要用于SpringBoot项目的自动化配 置。
- bootstrap配置文件有以下几个应用场景。
- 使用Spring Cloud Config配置中心时,需要在bootstrap配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息。
- 一些固定的不能被覆盖的属性。
- 一些加密/解密的场景。
五 SpringBoot注册web组件
1 注册Servlet
由于SpringBoot项目没有web.xml文件,所以无法在web.xml中注 册web组件,SpringBoot有自己的方式注册web组件。
注册方式一
- 编写servlet
@WebServlet("/first")
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("FirstServlet...");
}
}
- 启动类扫描web组件
@SpringBootApplication
//SpringBoot启动时扫描注册注解标注的Web组件
@ServletComponentScan
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
注册方式二
- 编写servlet
public class SecondServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("SecondServlet...");
}
}
- 使用配置类注册servlet
@Configuration
public class ServletConfig {
@Bean
//ServletRegistrationBean可以注册Servlet组件,将其放入Spring容器中即可注册Servlet
public ServletRegistrationBean getServletRegistrationBean() {
// 注册Servlet组件
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new SecondServlet());
// 添加Servlet组件访问路径
servletRegistrationBean.addUrlMappings("/second");
return servletRegistrationBean;
}
}
2 注册Filter
注册方式一
- 编写filter
@WebFilter("/first")
public class FirstFilter extends HttpFilter {
@Override
public void init() throws ServletException {
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("进入FirstFilter");
chain.doFilter(request,response);
System.out.println("离开FirstFilter");
}
@Override
public void destroy() {
}
}
- 启动类扫描web组件
@SpringBootApplication
//SpringBoot启动时扫描注册注解标注的Web组件
@ServletComponentScan
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
注册方式二
- 编写Filter
public class SecondFilter extends HttpFilter {
@Override
public void init() throws ServletException {
}
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("进入SecondFilter");
chain.doFilter(request,response);
System.out.println("离开SecondFilter");
}
@Override
public void destroy() {
}
}
- 使用配置类注册filter
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean getFilterRegistrationBean() {
// 注册filter组件
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new SecondFilter());
// 添加过滤路径
filterRegistrationBean.addUrlPatterns("/second");
return filterRegistrationBean;
}
}
3 注册Listener
注册方式一
- 编写Listener
@WebListener
public class FirstListener implements ServletContextListener {
@Override
public void
contextInitialized(ServletContextEvent sce) {
System.out.println("First Listener Init......");
}
@Override
public void
contextDestroyed(ServletContextEvent sce) {
}
}
- 启动类扫描web组件
@SpringBootApplication
//SpringBoot启动时扫描注册注解标注的Web组件
@ServletComponentScan
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
注册方式二
- 编写Listener
public class SecondListener implements ServletContextListener {
@Override
public void
contextInitialized(ServletContextEvent sce) {
System.out.println("Second Listener Init......");
}
@Override
public void
contextDestroyed(ServletContextEvent sce) {
}
}
- 使用配置类注册Listener
@Configuration
public class ListenerConfig {
@Bean
public ServletListenerRegistrationBean getServletListenerRegistrationBean() {
ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean(new SecondListener());
return servletListenerRegistrationBean;
}
}
六 SpringBoot访问静态资源
1 静态资源相关目录
SpringBoot项目中没有WebApp目录,只有src目录。在 src/main/resources
下面有 static 和 templates 两个文件夹。SpringBoot默认在 static
目录中存放静态资源,而 templates
中放动态页面。
static目录
SpringBoot通过 /resources/static 目录访问静态资源,在 resources/static 中编写html页面:
- page1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试html</title>
</head>
<body>
<h1>我的HTML</h1>
<img src="/img/img.png">
</body>
</html>
- 目录结构
templates目录
在SpringBoot中不推荐使用JSP作为动态页面,而是默认使用 Thymeleaf编写动态页面。templates目录是存放Thymeleaf页面的目录
2 静态资源其他存放位置
除了 /resources/static 目录,SpringBoot还会扫描以下位置的静态资源:
- /resources/META‐INF/resources/
- /resources/resources/
- /resources/public/
我们还可以在配置文件自定义静态资源位置
在SpringBoot配置文件进行自定义静态资源位置配置
spring:
web:
resources:
static-locations: classpath:/suibian/,classpath:/static/
注意:
- 该配置会覆盖默认静态资源位置,如果还想使用之前的静态资源位置,还需要配置在后面。
- SpringBoot2.5之前的配置方式为: spring.resources.static-locations
七 SpringBoot整合JSP
在SpringBoot中不推荐使用JSP作为动态页面,我们要想使用JSP编 写动态页面,需要手动添加webapp目录。
- 由于SpringBoot自带tomcat无法解析JSP,需要在pom文件添加 JSP引擎
<!-- 支持jsp的jar包 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
- 创建webapp目录,编写JSP文件
<%@ page
contentType="text/html;charset=UTF-8"
language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Hello JSP</h1>
</body>
</html>
- 将webapp标记为web目录
- 修改配置文件,配置视图解析器
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
logging:
pattern:
console: "%d{MM/dd HH:mm:ss.SSS}%clr(%-5level) --- [%-15thread]%cyan(%-50logger{50}):%msg%n"
- 创建Controller
@Controller
public class PageController {
// 页面跳转
@GetMapping("/{page}")
public String showPage(@PathVariable String page) {
return page;
}
}
- 启动项目,访问http://localhost:8080/myJsp
八 Thymeleaf
1 Thymeleaf入门
Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎,类似 JSP。它可以轻易的与SpringMVC等Web框架进行集成作为Web应 用的模板引擎。在SpringBoot中推荐使用Thymeleaf编写动态页 面。
Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板 页面,而不需要启动整个Web应用。
Thymeleaf在有网络和无网络的环境下皆可运行,它即可以让美工 在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。没有数据时,Thymeleaf的模板可以静态地运行;当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。
- 创建Springboot项目
- 引入SpringMVC和Thymeleaf起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--添加Thymeleaf起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 创建视图index.html
<!DOCTYPE html>
<!-- 引入thymeleaf命名空间,方便使用thymeleaf属性 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>thymeleaf入门</title>
</head>
<body>
<!-- 静态页面显示程序员,动态页面使用后端传来的msg数据代替 -->
<!-- thymeleaf支持el表达式 -->
<h2 th:text="${msg}">程序员</h2>
</body>
</html>
- template中的html文件不能直接访问,需要编写Controller跳转 到页面中
@Controller
public class PageController {
@GetMapping("/show")
public String showPage(Model model) {
model.addAttribute("msg", "Hello Thymeleaf");
return "index";
}
}
- 进行springboot配置
#日志格式
logging:
pattern:
console: "%d{MM/dd HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n"
- 启动项目,访问
http://localhost:8080/show
及静态页面
2 变量输出
语法 | 作用 |
---|---|
th:text | 将model中的值作为内容放入标签中 |
th:value | 将model中的值放入 input 标签的 value 属性中 |
- 准备模型数据
@GetMapping("/show")
public String showPage(Model model) {
model.addAttribute("msg", "Hello Thymeleaf");
return "index";
}
- 在视图展示model中的值
<span th:text="${msg}"></span>
<input th:value="${msg}">
3 操作字符串
Thymeleaf提供了一些内置对象可以操作数据,内置对象可直接在 模板中使用,这些对象是以#引用的,操作字符串的内置对象为 strings。
方法 | 说明 |
---|---|
${#strings.isEmpty(key)} | 判断字符串是否为空,如果为空返回true,否则返回false |
${#strings.contains(msg,‘T’)} | 判断字符串是否包含指定的子串,如果包含返回true,否则返 回false |
${#strings.startsWith(msg,‘a’)} | 判断当前字符串是否以子串开头,如果是返回true,否则返回 false |
${#strings.endsWith(msg,‘a’)} | 判断当前字符串是否以子串结尾,如果是返回true,否则返回 false |
${#strings.length(msg)} | 返回字符串的长度 |
${#strings.indexOf(msg,‘h’)} | 查找子串的位置,并返回该子串的下标,如果没找到则返回-1 |
${#strings.substring(msg,2,5)} | 截取子串,用法与JDK的 subString 方法相同 |
${#strings.toUpperCase(msg)} | 字符串转大写 |
${#strings.toLowerCase(msg)} | 字符串转小写 |
使用方式:
<span th:text="${#strings.isEmpty(msg)}"></span>
<hr/>
<span th:text="${#strings.contains(msg,'s')}"></span>
<hr/>
<span th:text="${#strings.length(msg)}"></span>
4 操作时间
操作时间的内置对象为dates
方法 | 说明 |
---|---|
${#dates.format(key)} | 格式化日期,默认的以浏览器默认语言为格式化标准 |
${#dates.format(key,‘yyyy/MM/dd’)} | 按照自定义的格式做日期转换 |
${#dates.year(key)} | 取年 |
${#dates.month(key)} | 取月 |
${#dates.day(key) | 取日 |
- 准备数据
@GetMapping("/show")
public String showPage(Model model) {
model.addAttribute("date", new Date(130, 01, 01));
return "index";
}
- 使用内置对象操作时间
<span th:text="${#dates.format(date)}">
</span>
<hr/>
<span th:text="${#dates.format(date,'yyyy/MM/dd')}"></span>
<hr/>
<span th:text="${#dates.year(date)}">
</span>
<span th:text="${#dates.month(date)}">
</span>
<span th:text="${#dates.day(date)}">
</span>
5 条件判断
语法 | 作用 |
---|---|
th:if | 条件判断 |
- 准备数据
@GetMapping("/show")
public String showPage(Model model) {
model.addAttribute("sex","女");
return "index";
}
- 进行条件判断
<div>
<span th:if="${sex} == '男'">
性别:男
</span>
<span th:if="${sex} == '女'">
性别:女
</span>
</div>
语法 | 作用 |
---|---|
th:switch/th:case | th:switch/th:case与Java中的switch语句等效。 th:case=“" 表示Java中 switch的default,即没有case的值为true时显示 th:case="” 的内容。 |
- 准备数据
@GetMapping("/show")
public String showPage(Model model) {
model.addAttribute("id","12");
return "index";
}
- 进行条件判断
<div th:switch="${id}">
<span th:case="1">ID为1</span>
<span th:case="2">ID为2</span>
<span th:case="3">ID为3</span>
<span th:case="*">ID为*</span>
</div>
6 迭代遍历
语法 | 作用 |
---|---|
th:each | 迭代器,用于循环迭代集合 |
遍历集合
- 编写实体类
public class User {
private String id;
private String name;
private int age;
}
- 准备数据
@GetMapping("/show")
public String showPage(Model model) {
List<User> users = new ArrayList<>();
users.add(new User("1","sxt",23));
users.add(new User("2","baizhan",22));
users.add(new User("3","admin",25));
model.addAttribute("users",users);
return "index";
}
- 在页面中展示数据
<table border="1" width="50%">
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
</tr>
<!-- 遍历集合的每一项起名为user -->
<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
</tr>
</table>
使用状态变量
thymeleaf将遍历的状态变量封装到一个对象中,通过该对象的属 性可以获取状态变量:
状态变量 | 含义 |
---|---|
index | 当前迭代器的索引,从0开始 |
count | 当前迭代对象的计数,从1开始 |
size | 被迭代对象的长度 |
odd/even | 布尔值,当前循环是否是偶数/奇数,从0开始 |
first | 布尔值,当前循环的是否是第一条,如果是返回true,否则返回false |
last | 布尔值,当前循环的是否是最后一条,如果是则返回true,否则返回false |
使用状态变量
<!--冒号前的第一个对象是遍历出的对象,第二个对象是封
装状态变量的对象-->
<tr th:each="user,status : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
<td th:text="${status.index}"></td>
<td th:text="${status.count}"></td>
<td th:text="${status.size}"></td>
<td th:text="${status.odd}"></td>
<td th:text="${status.even}"></td>
<td th:text="${status.first}"></td>
<td th:text="${status.last}"></td>
</tr>
遍历Map
- 准备数据
@GetMapping("/show")
public String showPage(Model model) {
Map<String, User> map = new HashMap<>();
map.put("user1", new User("1", "shangxuetang", 23));
map.put("user2", new User("2", "baizhan", 22));
map.put("user3", new User("3", "admin", 25));
model.addAttribute("map", map);
return "index";
}
- 遍历map
<table border="1" width="50%">
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
<th>Key</th>
</tr>
<!-- 遍历出的是一个键值对对象,key获取键,value获取值 -->
<tr th:each="m : ${map}">
<td th:text="${m.value.id}"></td>
<td th:text="${m.value.name}">
</td>
<td th:text="${m.value.age}"></td>
<td th:text="${m.key}"></td>
</tr>
</table>
6 获取域中的数据
thymeleaf也可以获取request,session,application域中的数 据,方法如下:
- 准备数据
@GetMapping("/show")
public String showPage(HttpServletRequest request, HttpSession session) {
request.setAttribute("req", "HttpServletRequest");
session.setAttribute("ses", "HttpSession");
session.getServletContext().setAttribute(" app", "application");
return "index";
}
- 获取域数据
request1: <span th:text="${#request.getAttribute('req')}"/>
request2:<span th:text="${#httpServletRequest.getAttribute('req')}"/>
<hr/>
session1: <span th:text="${session.ses}"/>
session2: <span th:text="${#httpSession.getAttribute('ses')}"/>
<hr/>
application1: <span th:text="${application.app}"/>
application2:<span th:text="${#servletContext.getAttribute('app')}"/>
7 URL写法
在Thymeleaf中路径的写法为 @{路径}
<a th:href="@{http://www.baidu.com}">百度</a>
在路径中添加参数
- 准备数据
@GetMapping("/show1")
public String show1(Model model) {
model.addAttribute("id", "100");
model.addAttribute("name", "lxx");
return "index";
}
- 准备跳转后访问的Controller
@GetMapping("/show2")
@ResponseBody
public String show2(String id, String name) {
return id + ":" + name;
}
- 在URL中添加参数
<a th:href="@{show2?id=1&name=sxt}">静态参数 一</a>
<a th:href="@{show2(id=2,name=bz)}">静态参数 二</a>
<a th:href="@{'show2?id='+${id}+'&name='+${name}}">动态参数一</a>
<a th:href="@{show2(id=${id},name=${name})}">动态参数二</a>
在RESTful风格路径中添加参数
- 准备跳转后访问的Controller
@GetMapping("/show3/{id}/{name}")
@ResponseBody
public String show3(@PathVariable String id,@PathVariable String name){
return id+":"+name;
}
在URL中添加参数
<a th:href="@{/show3/{id}/{name}(id=${id},name=${name})}">restful格式传递参数方式</a>
8 相关配置
在Springboot配置文件中可以进行Thymeleaf相关配置
配置项 | 含义 |
---|---|
spring.thymeleaf.prefix | 视图前缀 |
spring.thymeleaf.suffix | 视图后缀 |
spring.thymeleaf.encoding | 编码格式 |
spring.thymeleaf.servlet.content-type | 响应类型 |
spring.thymeleaf.cache=false | 页面缓存,配置为false则不启用页面缓存,方便测试 |
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
encoding: UTF-8
cache: false
servlet:
content-type: text/html
九 SpringBoot热部署
热部署,就是在应用正在运行的时候升级软件,却不需要重新启动 应用。即修改完代码后不需要重启项目即可生效。在SpringBoot 中,可以使用DevTools工具实现热部署
- 添加DevTools依赖
<!-- 热部署工具 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
- 在idea中设置自动编译
点击 File–>Settings
- 在Idea设置自动运行
此时热部署即可生效
十 SpringBoot整合MyBatis
Spring整合MyBatis时需要进行大量配置,而SpringBoot整合 MyBatis则可以简化很多配置:
- 准备数据库数据
- 创建SpringBoot项目,添加MyBatis起步依赖和Mysql驱动依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 热部署工具 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
- 编写实体类
public class Student {
private int id;
private String name;
private String sex;
private String address;
}
- 编写Mapper接口
@Mapper
public interface StudentMapper {
List<Student> findAll();
}
- 编写Mapper映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace="com.lxx.springbootdemo.mapper.StudentMapper">
<select id="findAll" resultType="student">
select id, name, sex, address
from student
</select>
</mapper>
- 编写配置文件
# 数据源
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/student?characterEncoding=utf-8
username: root
password: 123456
# mybatis配置
mybatis:
# 映射文件位置
mapper-locations: com/lxx/springbootdemo/mapper/*Mapper.xml
# 别名
type-aliases-package: com.lxx.springbootdemo.domain
#日志格式
logging:
pattern:
console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
- 编写测试类
// 测试类注解,可以在运行测试代码时加载Spring容器
@SpringBootTest
public class StudentMapperTest {
@Autowired
private StudentMapper studentMapper;
@Test
public void testFindAll() {
List<Student> all = studentMapper.findAll();
all.forEach(System.out::println);
}
}
十一 SpringBoot参数验证
1 校验简单数据类型
SpringBoot自带了validation工具可以从后端对前端传来的参数进 行校验,用法如下:
- 引入 validation 起步依赖
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 编写Controller
// 该控制器开启参数校验
@Validated
@Controller
public class TestController {
@RequestMapping("/t1")
@ResponseBody
// 在参数前加校验注解,该注解的意思是字符串参数不能为null
public String t1(@NotBlank String username) {
System.out.println(username);
return "请求成功!";
}
}
-
访问
http://localhost:8080/t1
,发现当没有传来参数时,会抛 出 ConstraintViolationException 异常。 -
在校验参数的注解中添加 message 属性,可以替换异常信息。
// 该控制器开启参数校验
@Validated
@Controller
public class TestController {
@RequestMapping("/t1")
@ResponseBody
// 在参数前加校验注解,该注解的意思是字符串参数不能为null
public String t1(@NotBlank(message = "用户名不能为空") String username) {
System.out.println(username);
return "请求成功!";
}
}
2 异常处理
当抛出 ConstraintViolationException 异常后,我们可以使用SpringMVC的异 常处理器,也可以使用SpringBoot自带的异常处理机制。
当程序出现了异常,SpringBoot会使用自带的 BasicErrorController 对象处 理异常。该处理器会默认跳转到/resources/templates/error.html 页面。
编写异常页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>错误页面</title>
</head>
<body>
<h1>服务器开小差了!</h1>
</body>
</html>
注意:要加入thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3 校验相关注解
注解 | 作用 |
---|---|
@NotNull | 判断包装类是否为null |
@NotBlank | 判断字符串是否为null或者是空串(去掉首尾空格) |
@NotEmpty | 判断集合是否为空 |
@Length | 判断字符的长度(最大或者最小) |
@Min | 判断数值最小值 |
@Max | 判断数值最大值 |
判断邮箱是否合法 |
@RequestMapping("/t2")
@ResponseBody
public String t2(
@NotBlank @Length(min = 1, max = 5) String username,
@NotNull @Min(0) @Max(150) Integer age,
@NotEmpty @RequestParam List<String> address,
@NotBlank @Email String email) {
System.out.println(username);
System.out.println(age);
System.out.println(address);
System.out.println(email);
return "请求成功!";
}
4 校验对象类型
SpringBoot也可以校验对象参数中的每个属性,用法如下:
- 添加实体类
public class Hero {
@NotNull(message = "id不能为空")
private Integer id;
@NotBlank(message = "姓名不能为空")
private String name;
}
- 编写控制器
@Controller
public class TestController2 {
@RequestMapping("/t3")
@ResponseBody
// 校验的对象参数前添加@Validated,并将异常信息封装到BindingResult对象中
public String t3(@Validated Hero hero, BindingResult result) {
// 判断是否有参数异常
if (result.hasErrors()) {
// 所有参数异常
List<ObjectError> list = result.getAllErrors();
// 遍历参数异常,输出异常信息
for (ObjectError err : list) {
FieldError fieldError = (FieldError) err;
System.out.println(fieldError.getDefaultMessage());
}
return "参数异常";
}
System.out.println(hero);
return "请求成功!";
}
}
十二 SpringBoot指标监控
1 添加Actuator功能
Spring Boot Actuator可以帮助程序员监控和管理SpringBoot应 用,比如健康检查、内存使用情况统计、线程使用情况统计等。我 们在SpringBoot项目中添加Actuator功能,即可使用Actuator监控 项目,用法如下:
- 在被监控的项目中添加Actuator起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 编写配置文件
#开启所有监控端点
management:
endpoints:
web:
exposure:
include: "*"
- 访问项目:
http://localhost:8080/actuator
通过URL可以调用actuator的功能:
URL | 查看的数据 |
---|---|
/env | 环境属性 |
/health | 健康检查 |
/mappings | 显示所有的@RequestMapping路径 |
/loggers | 日志 |
/info | 定制信息 |
/metrics | 查看内存、CPU核心等系统参数 |
/trace | 用户请求信息 |
例如查询健康数据,访问http://localhost:8080/actuator/health
2 Spring Boot Admin
Actuator使用JSON格式展示了大量指标数据,不利于我们查看,我们可以使用可视化工具Spring Boot Admin查看actuator生成指标 数据。Spring Boot Admin是一个独立的项目,我们需要创建并运行该项目。
创建Spring Boot Admin服务端项目
- 创建SpringBoot项目,添加SpringMVC和Spring Boot Admin服 务端起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
- 修改配置文件
# 端口号
server.port=9090
#日志格式
logging.pattern.console=%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n
- 修改启动类
@SpringBootApplication
@EnableAdminServer //开启Spring Boot Admin服务端
public class SpringbootadminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootadminApplication.class, args);
}
}
连接Spring Boot Admin项目
在被监控的项目中连接Spring Boot Admin项目,才能使用Spring Boot Admin查看指标数据。
- 被监控项目添加Spring Boot Admin客户端起步依赖
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.6.0</version>
</dependency>
- 修改配置文件
#Spring boot admin访问地址
spring.boot.admin.client.url=http://localhost:9090
- 此时Spring Boot Admin即可连接被监控的项目
十三 SpringBoot日志管理
SpringBoot默认使用Logback组件作为日志管理。Logback是log4j 创始人设计的一个开源日志组件。在SpringBoot项目中我们不需要 额外的添加Logback的依赖,因为在 spring-boot-parent 中已经包含了 Logback的依赖。
- 在 /resources 下添加Logback配置文件logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--定义日志文件的存储地址-->
<property name="LOG_HOME" value="${catalina.base}/logs/"/>
<!-- 控制台输出 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志输出编码 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化输出:%d表示时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/server.%d{yy99-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化输出:%d表示时间,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="Stdout"/>
<appender-ref ref="RollingFile"/>
</root>
</configuration>
注:Logback配置文件名为logback-test.xml或 logback.xml,如果classpath下没有这两个文件,LogBack 会自动进行最小化配置。
- 在代码中打印日志
@Controller
public class LogbackController {
private final static Logger logger = LoggerFactory.getLogger(LogbackController.class);
@RequestMapping("/printLog")
@ResponseBody
public String showInfo(){
logger.info("记录日志");
return "Hello Logback";
}
}
- 如果日志过多,可以屏蔽一些包的日志,在配置文件中配置
#屏蔽org包中的日志输出
logging.level.org=off
补充:Log4j2安全漏洞
在2021年12月,Log4j2爆出了极其严重的安全漏洞,攻击者可以让记录的日志包含指定字符串,从而执行任意程序。很多大 型网站,如百度等都是此次Log4j漏洞的受害者,很多互联网企 业连夜做了应急措施。
Log4j2.0到2.14.1全部存在此漏洞,危害范围极其广泛, Log4j2.15.0-rc1中修复了这个 bug。
因Log4j2漏洞的反复无常,导致某些公司已经切换到Logback 来记录日志,但在Log4j2漏洞爆出后,Logback也爆出漏洞: 在Logback1.2.7及之前的版本中,具有编辑配置文件权限的攻 击者可以制作恶意配置,允许从LDAP服务器加载、执行任意代 码。
解决方案为将Logback升级到安全版本: Logback1.2.9+
SpringBoot2.6.2以上的Logback版本已经升到了1.2.9,Log4j2 的版本也升到了2.17.0,所以我们使用SpringBoot2.6.2以上版 本无需担心Log4j2和Logback安全漏洞。
十四 SpringBoot项目部署
1 项目打包
SpringBoot项目是依赖于Maven构建的,但打包时如果只依赖 Maven打包工具则会打包不完整,我们还需要在SpringBoot项目中 引入SpringBoot打包插件 :
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
此时再使用Maven插件打包:
打包后jar包解压目录如下:
如果不添加SpringBoot打包插件,打包后jar包解压目录如下:
可以看到该目录少了BOOT-INF,打包是不完整的,也无法运行jar 包
运行jar包:
- 进入jar包所在目录,使用cmd打开命令行窗口
- 输入命令:
java -jar jar包名
2 多环境配置
在真实开发中,在不同环境下运行项目往往会进行不同的配置,比如开发环境使用的是开发数据库,测试环境使用的是测试数据库, 生产环境使用的是生产数据库。SpringBoot支持不同环境下使用不 同的配置文件,用法如下:
配置文件名:
application-环境名.properties/yml
如:
- application-dev.properties/yml 开发环境配置文件
# 开发环境端口号为8080
server:
port: 8080
- application-test.properties/yml 测试环境配置文件
# 测试环境端口号为8081
server:
port: 8081
- application-prod.properties/yml 生产环境配置文件
# 生产环境端口号为80
server:
port: 80
运行jar包时选择环境:
java -jar jar包名 --spring.profiles.active=环境名