最近更新于 2024-04-18 19:55

1 前言

去年接过一个 Java 的单子,现学了 Java(https://blog.iyatt.com/?p=11305 ),在之后都没碰过 Java 了。
前段时间又遇到一个 Spring Boot 的项目,因为时间比较紧只有三四天就要见初版,我也不能现学来做就推掉了。
现在抽空学一下,做个记录。

2 环境

  • Java 21.0.2
  • Maven 3.9.6
  • Spring Boot 3.2.4

3 辅助

3.1 项目创建器

3.1.1 官方网站

Spring Boot 项目创建器:https://start.spring.io/
这个是官方提供的,而且是开源的,自己也可以部署这个网页
左侧配置基本信息,右侧添加其它依赖
file

3.1.2 VScode

如果像我一样是使用 VScode 开发,可以安装一个插件,实现项目创建(感觉应该也是基于上面的官方项目),可以不需要手动解包打开项目,这个直接跳转到创建好的项目
file

F1或者Ctrl+Shift+P打开 VScode 的命令面板,搜索 Spring,开始创建一个 Maven 或 Gradle 项目,后续填的参数和网页一样
file


除了上面的项目创建插件,VScode 中进行 Spring Boot 开发可能用到的其它插件:

  • Java 插件集合:Extension Pack for Java
    file

  • Spring Boot 仪表盘:Spring Boot Dashboard
    file
    在侧边栏会显示所有的 Spring Boot 项目,可以集中调试和运行管理
    file

3.1.3 IDEA

如果用 IDEA(2023.1),也可以用同样的方法创建,这个默认就是调用官方的创建器网站
file

3.2 文档

官方文档:https://spring.io/projects/spring-boot#learn
中文文档:https://springdoc.cn/spring-boot/

4 “Hello world” 体验

创建一个 Spring Boot 项目,搜索添加 Spring Web 依赖
file

直接运行
file

浏览器访问本机 8080 端口,可以看到一个错误页面,但是确实运行起来了
file


一般的 Spring Boot 项目结构

myproject/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           ├── controllers/       # 存放控制器类
│   │   │           ├── models/            # 存放模型类
│   │   │           ├── repositories/      # 存放数据访问层接口
│   │   │           ├── services/          # 存放服务层接口和实现类
│   │   │           ├── utilities/         # 存放工具类
│   │   │           └── Application.java   # 项目的入口类
│   │   ├── resources/
│   │   │   ├── application.properties     # 应用程序配置文件
│   │   │   └── static/                    # 存放静态资源文件
│   │   └── webapp/                        # 存放Web应用程序资源(可选)
│   └── test/
│       ├── java/
│       │   └── com/
│       │       └── example/
│       │           └── tests/            # 存放测试类
│       └── resources/                    # 存放测试资源文件
└── pom.xml                               # Maven项目配置文件
  • src/main/java:存放Java源代码的根目录。
  • src/main/java/com/example/controllers:存放控制器类,处理HTTP请求和响应。
  • src/main/java/com/example/models:存放模型类,表示应用程序的数据结构。
  • src/main/java/com/example/repositories:存放数据访问层接口,用于与数据库或其他数据源进行交互。
  • src/main/java/com/example/services:存放服务层接口和实现类,处理业务逻辑。
  • src/main/java/com/example/utilities:存放工具类,提供辅助功能。
  • src/main/resources:存放项目的资源文件,如应用程序配置文件、静态资源文件等。
  • src/main/webapp:如果使用传统的Web应用程序部署方式,该目录存放Web应用程序资源(例如JSP文件)。
  • src/test:存放测试代码和资源文件的目录。
  • pom.xml:Maven项目的配置文件,用于定义项目的依赖和构建配置。

上面直接运行,访问页面是个错误页就是因为缺少 controller 来处理请求
现在创建一个
file

package com.iyatt.test1.controllers;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Test1Controller
{
    @RequestMapping("/") // 处理 http://IP:8080/ 地址的请求
    public String hello()
    {
        return "Hello World";
    }
}

再运行 Test1Application 后访问可以看到 Hello world
file

5 配置文件

5.1 公共应用属性

application.properties 就是配置文件,另外支持用 yaml 格式配置,即命名为 application.yaml
file

官方文档有关于公共应用属性的说明:https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html
比如 server.port 设置端口,默认是 8080
file
我设置端口为 80,即 http 的默认端口,这样浏览器就可以不用指定端口进行访问
在 application.properties 中添加
file
这样就可以直接用 IP 访问了
file

把上面设置的端口注释掉,创建 application.yaml 文件,写入
file
现在可以通过 81 端口访问
file

那么如果两种格式的配置文件同时存在,且对同一个参数设置了不同值优先级如何?
把 properties 设置端口为 80,yaml 设置端口为 81,再运行
可以看到实际运行的是 80,代表 properties 的优先级更高
file

5.2 读取配置文件

这里以 YAML 格式的配置文件进行演示

YAML 的语法参考官方文档:https://yaml.org/spec/
目前最新的一版是 2021-10-01 的 1.2.2 版本

application.yaml 写入内容如下:

name: Xiao Qiang

# 对象
person:
    name: ${name} # 引用
    age: 18
    adult: true

# 数组
address:
    - Chong Qing
    - Beijing

msg1: 'hello\n world' # 保留原始字符串
msg2: "hello\n world" # 特殊字符会转义

5.2.1 逐个读取

package com.iyatt.test1.controllers;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Test1Controller
{
    @Value("${name}") // 读取application.yaml中的name属性
    private String name;

    @RequestMapping("/name") // 访问 http://localhost/name 获取name属性值
    public String get_name()
    {
        return this.name;
    }

    @Value("${person.name}") // 读取application.yaml中的person.name属性
    private String person_name;

    @Value("${person.age}") // 读取application.yaml中的person.age属性
    private Integer person_age;

    @Value("${person.adult}") // 读取application.yaml中的person.adult属性
    private boolean person_adult;

    @RequestMapping("/person") // 访问 http://localhost/person 获取person对象
    public String get_person()
    {
        return "Name: " + this.person_name + ", Age: " + this.person_age + ", Adult: " + this.person_adult;
    }

    @Value("${address[0]}") // 读取application.yaml中的address数组第一个元素
    private String address1;

    @Value("${address[1]}") // 读取application.yaml中的address数组第二个元素
    private String address2;

    @RequestMapping("/address") // 访问 http://localhost/address 获取address数组
    public String get_address()
    {
        return "Address1: " + this.address1 + ", Address2: " + this.address2;
    }

    @Value("${msg1}") // 读取application.yaml中的msg1属性
    private String msg1;

    @Value("${msg2}") // 读取application.yaml中的msg2属性
    private String msg2;

    @RequestMapping("/msg") // 访问 http://localhost/msg 获取msg数组
    public String get_msg()
    {
        return "msg1: " + this.msg1 + " msg2: " + this.msg2;
    }
}

分别访问
file

file

file

file

5.2.2 按需获取

这种方式不需要像上面那种逐个读取值到变量中,可以整体加载,然后按需要获取属性值

package com.iyatt.test1.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Test1Controller
{
    @Autowired
    private Environment env;

    @RequestMapping("/") // 访问 http://localhost
    public String get_env()
    {
        return "name: " + env.getProperty("name") +
                ", person.name: " + env.getProperty("person.name") +
                ",  person.age: " + env.getProperty("person.age") +
                ", person.adult: " + env.getProperty("person.adult") +
                ", adddress1: " + env.getProperty("address[0]") +
                ", address2: " + env.getProperty("address[1]") +
                ", msg1: " + env.getProperty("msg1") +
                ", msg2: " + env.getProperty("msg2");
    }
}

file

5.2.3 反序列化

直接将配置文件中的内容加载到对象
Bean 通用方法自动生成,参考:https://blog.iyatt.com/?p=14637

application.yaml 改为

person:
    name: Xiao Wang
    age: 18
    adult: true
    address:
        - Chong Qing
        - Beijing

创建一个 Person.java

package com.iyatt.test1;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;

@Component
@ConfigurationProperties(prefix = "person") // 读取 person 对象
@Data // 自动生成 Baen 通用方法
public class Person
{
    private String name;
    private Integer age;
    private boolean adult;
    private String[] address;
}

controller.java

package com.iyatt.test1;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Test1Controller
{
    @Autowired
    private Person person;

    @RequestMapping("/person")
    public String get_person()
    {
        return person.toString();
    }
}

运行访问
file

5.3 配置文件分类

5.3.1 多文件

application 作为主配置文件,一般测试环境的配置文件命名为 application-test,开发环境命名为 application-dev,生产环境命名为 application-pro。
file

比如我在 dev、pro、test 分别设置服务端口为80、81、82
在 application.properties 控制当前的激活环境(后缀)为 pro

spring.profiles.active=pro

运行服务就是 pro 配置的 81 端口启用了
file

5.3.2 单文件分段

application.yaml

---
Server:
  port: 80

spring:
    config:
        activate: 
            on-profile: dev
---
Server:
  port: 81

spring:
    config:
        activate: 
            on-profile: pro
---
Server:
  port: 82

spring:
    config:
        activate: 
            on-profile: test
---
spring:
    profiles:
        active: test # 处于激活状态的配置

test 设置的 82 端口运行
file

5.3.3 运行时指定配置文件

执行命令打包

mvn package

file

运行在 target 目录下的 jar 文件,并在后方通过参数指定

java -jar .\target\test2-profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

比如我这里指定为 dev 运行,就会以 80 端口运行(即使文件里指定的是 test)
file

5.4 外部配置文件

官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

前面说的 application 配置文件在打包的时候会一起打包,如果在运行的时候想要修改,可以按照同样的格式编辑作为外部配置文件,在运行的时候指定或者放置到默认自动加载的路径,会覆盖打包的配置文件起作用。可放置的位置挺多的,需要用的时候可以现查文档。