
Spring Boot:Java开发的神奇加速器(二)
除了使用 Spring Boot 提供的默认配置项和常用配置项外,我们还可以根据项目的业务需求自定义配置属性,并将其注入到 Java 类中使用。1.在配置文件中定义自定义属性:首先,在application.properties或application.yml文件中添加自定义的配置属性。myapp.custom.name=Spring Boot自定义配置示例myapp:custom:name: S
目录
四、深入理解 Spring Boot 配置
4.1 配置文件类型
在 Spring Boot 应用中,主要有两种配置文件类型,即application.properties和application.yml(或application.yaml),它们都用于配置应用程序的各种属性,但在语法和格式上有所不同。
- application.properties:这是一种传统的属性文件格式,使用键值对的方式进行配置,每个属性占一行,以key=value的形式表示。例如:
server.port=8080 spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=123456
这种格式的优点是简单直观,易于理解和编写,适合简单的配置场景。缺点是对于复杂的配置,尤其是具有层级结构的配置,会显得不够灵活和清晰,因为它只能通过点号(.)来模拟层级关系,例如spring.datasource.url,但对于更复杂的嵌套结构,代码会变得冗长且难以维护。
- application.yml:它采用 YAML(YAML Ain't Markup Language)格式,使用缩进和冒号来表示属性的层级关系,具有更好的可读性和层次性。例如:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: 123456
YAML 格式的优势在于能够清晰地展示配置的层级结构,对于复杂的配置更加友好,易于维护和管理。同时,它还支持列表和嵌套对象的表示,例如:
my: servers: - host: 192.168.1.100 port: 80 - host: 192.168.1.101 port: 8080
但需要注意的是,YAML 对缩进的要求非常严格,缩进错误可能会导致配置解析失败。在使用时,务必确保缩进的一致性和正确性。
两种配置文件在功能上是等价的,Spring Boot 会自动加载并解析它们。开发者可以根据个人喜好和项目的实际需求来选择使用哪种格式。一般来说,对于简单的项目或配置较少的情况,application.properties可能更方便;而对于大型项目或复杂的配置场景,application.yml则能更好地展现其优势。
4.2 常用配置项
Spring Boot 应用的配置项非常丰富,涵盖了服务器、数据库、日志、安全等多个方面。以下是一些常用的配置项及其配置方法:
服务器端口配置:在开发和部署 Spring Boot 应用时,经常需要修改服务器的端口号,以避免与其他应用冲突或满足特定的部署需求。可以在application.properties或application.yml文件中进行配置。
- 在application.properties中配置:
server.port=8081
- 在application.yml中配置:
server: port: 8081
除了在配置文件中设置,还可以通过命令行参数的方式来指定端口号,这种方式在测试或临时部署时非常方便。例如:
java -jar your - application.jar --server.port=8082
命令行参数的优先级高于配置文件中的配置,即如果同时在配置文件和命令行中设置了端口号,应用会采用命令行中指定的端口。
数据库连接配置:在大多数 Web 应用中,与数据库进行交互是必不可少的。Spring Boot 支持多种数据库,如 MySQL、PostgreSQL、H2 等,配置方式也相对简单。以 MySQL 数据库为例,在application.properties中配置如下:
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver - class - name=com.mysql.cj.jdbc.Driver
在application.yml中配置则为:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver - class - name: com.mysql.cj.jdbc.Driver
其中,spring.datasource.url指定了数据库的连接地址,包括主机名、端口号和数据库名,?useSSL=false&serverTimezone=UTC是一些连接参数,用于禁用 SSL 连接和设置时区;spring.datasource.username和spring.datasource.password分别是数据库的用户名和密码;spring.datasource.driver - class - name指定了数据库驱动类,对于 MySQL 8 及以上版本,通常使用com.mysql.cj.jdbc.Driver。
如果使用的是其他数据库,如 H2 嵌入式数据库,配置会有所不同。在application.properties中配置:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver - class - name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
在application.yml中配置:
spring:
datasource:
url: jdbc:h2:mem:testdb
driver - class - name: org.h2.Driver
username: sa
password: password
这里jdbc:h2:mem:testdb表示使用内存中的 H2 数据库,数据库名为testdb。
日志配置:日志在应用开发和运维中起着至关重要的作用,它可以帮助我们记录应用的运行状态、排查问题等。Spring Boot 默认使用 Logback 作为日志框架,通过配置文件可以灵活地控制日志的输出级别、格式和目标等。在application.properties中配置日志示例如下:
# 根日志级别
logging.level.root=INFO
# 指定包下的日志级别
logging.level.com.example.myapp=DEBUG
# 日志文件名称
logging.file.name=myapp.log
# 日志文件路径
logging.file.path=/var/log/myapp/
在application.yml中配置:
logging:
level:
root: INFO
com.example.myapp: DEBUG
file:
name: myapp.log
path: /var/log/myapp/
上述配置中,logging.level.root设置了根日志级别为INFO,表示只输出INFO级别及以上的日志;logging.level.com.example.myapp将com.example.myapp包下的日志级别设置为DEBUG,这样该包下的详细调试信息也会被记录;logging.file.name指定了日志文件的名称为myapp.log,logging.file.path指定了日志文件的存储路径为/var/log/myapp/。如果不指定路径,日志文件会生成在项目的根目录下。
4.3 自定义配置
除了使用 Spring Boot 提供的默认配置项和常用配置项外,我们还可以根据项目的业务需求自定义配置属性,并将其注入到 Java 类中使用。以下是实现自定义配置的详细步骤:
1.在配置文件中定义自定义属性:首先,在application.properties或application.yml文件中添加自定义的配置属性。例如,在application.properties中添加:
myapp.custom.name=Spring Boot自定义配置示例 myapp.custom.value=123
在application.yml中添加:
myapp: custom: name: Spring Boot自定义配置示例 value: 123
2.创建 Java 类来接收配置属性:然后,创建一个 Java 类,使用@ConfigurationProperties注解来绑定配置文件中的属性。假设我们创建一个名为MyAppConfig的类,代码如下:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "myapp.custom") public class MyAppConfig { private String name; private int value; // 生成getter和setter方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
在这个类中,@Component注解将其标记为一个 Spring 组件,使其能够被 Spring 容器扫描并管理。@ConfigurationProperties(prefix = "myapp.custom")注解表示将配置文件中以myapp.custom为前缀的属性绑定到该类的属性上,例如myapp.custom.name会绑定到name属性,myapp.custom.value会绑定到value属性。
3.使用自定义配置属性:在其他类中,通过依赖注入的方式获取MyAppConfig实例,就可以使用自定义的配置属性了。例如,创建一个控制器类CustomConfigController:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class CustomConfigController { @Autowired private MyAppConfig myAppConfig; @GetMapping("/custom-config") public String getCustomConfig() { return "自定义配置名称: " + myAppConfig.getName() + ", 自定义配置值: " + myAppConfig.getValue(); } }
在这个控制器中,通过@Autowired注解将MyAppConfig实例注入到myAppConfig变量中,然后在getCustomConfig方法中使用其属性,返回自定义配置的信息。当访问/custom-config接口时,就可以看到输出的自定义配置内容。
通过上述步骤,我们实现了自定义配置属性并将其注入到 Java 类中使用,这种方式使得我们可以方便地管理和使用项目特定的配置信息,提高了应用的可配置性和灵活性。
五、数据访问与持久化
在现代 Web 应用开发中,数据访问与持久化是至关重要的环节。它负责与数据库进行交互,实现数据的存储、读取、更新和删除等操作,是连接业务逻辑与数据存储的桥梁。Spring Boot 提供了强大的数据访问支持,结合 Spring Data JPA 等技术,能够极大地简化数据访问层的开发。
5.1 集成 Spring Data JPA
Spring Data JPA 是 Spring 框架的一个模块,它简化了与 Java 持久化 API(JPA)的交互,提供了一种声明式的数据访问方式。在 Spring Boot 应用中集成 Spring Data JPA,可以显著提高数据访问层的开发效率。以下是集成 Spring Data JPA 的详细步骤:
1.添加依赖:在pom.xml文件中添加 Spring Data JPA 和数据库驱动的依赖。以 MySQL 数据库为例,添加如下依赖:
<dependencies> <!-- Spring Data JPA依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- MySQL数据库驱动依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>
这里,spring-boot-starter-data-jpa依赖引入了 Spring Data JPA 的核心功能和相关组件,mysql-connector-java依赖则提供了与 MySQL 数据库进行交互的驱动程序。<scope>runtime</scope>表示该依赖仅在运行时需要,不会参与编译过程。
2.配置数据源:在application.properties或application.yml文件中配置数据库连接信息,包括数据库的 URL、用户名、密码和驱动类名。在application.properties中配置如下:
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver - class - name=com.mysql.cj.jdbc.Driver
在application.yml中配置:
spring: datasource: url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC username: root password: 123456 driver - class - name: com.mysql.cj.jdbc.Driver
上述配置中,spring.datasource.url指定了 MySQL 数据库的连接地址,包括主机名localhost、端口号3306和数据库名mydb,?useSSL=false&serverTimezone=UTC是连接参数,用于禁用 SSL 连接和设置时区为 UTC;spring.datasource.username和spring.datasource.password分别是数据库的用户名和密码;spring.datasource.driver - class - name指定了 MySQL 数据库的驱动类,对于 MySQL 8 及以上版本,通常使用com.mysql.cj.jdbc.Driver。
3.配置 JPA:同样在配置文件中,配置 JPA 的相关属性,如数据库方言、实体扫描位置和 DDL 自动更新策略等。在application.properties中添加:
spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
在application.yml中添加:
spring: jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: dialect: org.hibernate.dialect.MySQL5InnoDBDialect
其中,spring.jpa.hibernate.ddl-auto=update表示在应用启动时,根据实体类的定义自动更新数据库表结构,如果表不存在则创建;spring.jpa.show-sql=true表示在控制台打印 JPA 执行的 SQL 语句,方便调试;spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect指定了使用 MySQL 5 InnoDB 存储引擎的方言,用于生成针对 MySQL 数据库的 SQL 查询和语句。
4.创建实体类:创建与数据库表对应的实体类,并使用 JPA 注解来标记实体类和字段。例如,创建一个User实体类,代码如下:
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; // 生成getter和setter方法 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
在这个实体类中,@Entity注解表示该类是一个实体类,会被映射到数据库表;@Id注解标识id字段为主键;@GeneratedValue(strategy = GenerationType.IDENTITY)表示主键采用自增长策略,即数据库会自动为每条记录生成唯一的主键值。
5.创建 Repository 接口:创建一个继承自JpaRepository的接口,Spring Data JPA 会根据接口方法名称自动实现数据访问逻辑。例如,创建UserRepository接口:
import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { // 可以添加自定义查询方法 User findByName(String name); }
UserRepository接口继承自JpaRepository,其中User是实体类类型,Long是主键的类型。通过继承JpaRepository,我们可以直接使用它提供的一系列默认方法,如save(保存或更新实体)、findById(根据主键查找实体)、findAll(查找所有实体)等。同时,我们还可以在接口中定义自定义的查询方法,如findByName,Spring Data JPA 会根据方法名自动生成相应的 SQL 查询语句。
5.2 编写数据访问层代码
完成 Spring Data JPA 的集成后,就可以使用Repository接口进行数据访问操作了。Spring Data JPA 提供了丰富的方法来执行 CRUD(创建、读取、更新、删除)操作,同时还支持通过方法名定义查询、使用查询注解等方式进行复杂查询。以下是一些常见的数据访问层代码示例:
1.保存数据:使用save方法可以保存或更新一个实体对象。如果实体对象的主键为空,save方法会执行插入操作;如果主键已存在,则执行更新操作。例如:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserRepository userRepository; public User saveUser(User user) { return userRepository.save(user); } }
在上述代码中,UserService类通过@Autowired注解注入了UserRepository实例,然后在saveUser方法中调用userRepository.save(user)方法将User对象保存到数据库中。如果保存成功,save方法会返回保存后的User对象。
2.查询数据:
- 根据主键查询:使用findById方法可以根据主键查找实体对象。该方法返回一个Optional对象,通过isPresent方法可以判断是否找到对应的实体,通过get方法可以获取实体对象。例如:
public User getUserById(Long id) { return userRepository.findById(id).orElse(null); }
这里,userRepository.findById(id)返回一个Optional<User>对象,如果Optional对象不为空,即找到了对应的用户,则通过orElse(null)方法返回该用户对象;如果Optional对象为空,即未找到对应的用户,则返回null。
- 查询所有数据:使用findAll方法可以查询所有实体对象,返回一个Iterable集合。例如:
public Iterable<User> getAllUsers() { return userRepository.findAll(); }
- 自定义方法查询:如前面在UserRepository接口中定义的findByName方法,可以根据用户名查询用户。例如:
public User findUserByName(String name) { return userRepository.findByName(name); }
Spring Data JPA 会根据方法名findByName自动生成 SQL 查询语句,实现根据用户名查询用户的功能。
3.更新数据:更新数据同样使用save方法,因为save方法会根据实体对象的主键来判断是执行插入还是更新操作。例如:
public User updateUser(User user) { return userRepository.save(user); }
在调用updateUser方法时,传入的User对象的主键必须是已存在于数据库中的,这样save方法就会执行更新操作,将User对象的最新数据更新到数据库中。
4.删除数据:使用deleteById方法可以根据主键删除实体对象,使用delete方法可以删除一个实体对象,使用deleteAll方法可以删除所有实体对象。例如:
public void deleteUserById(Long id) { userRepository.deleteById(id); } public void deleteUser(User user) { userRepository.delete(user); } public void deleteAllUsers() { userRepository.deleteAll(); }
上述代码分别展示了根据主键删除用户、删除指定用户对象和删除所有用户的方法。在实际应用中,应根据具体需求选择合适的删除方法。
5.3 事务管理
事务管理是保证数据完整性和一致性的重要机制,它确保一组数据库操作要么全部成功执行,要么全部失败回滚。在 Spring Boot 中,使用事务非常简单,通过@Transactional注解就可以轻松实现事务管理。
1.事务管理的概念:事务是一个不可分割的工作单元,它包含多个数据库操作,这些操作要么全部成功提交,使数据持久化到数据库中;要么全部失败回滚,撤销之前的所有操作,保证数据的一致性。例如,在一个银行转账的业务场景中,涉及到从转出账户扣除金额和向转入账户增加金额两个操作,这两个操作必须作为一个事务来处理,否则可能会出现转出账户金额已扣除,但转入账户金额未增加的情况,导致数据不一致。事务具有 ACID 特性:
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会出现部分成功部分失败的情况。
- 一致性(Consistency):事务执行前后,数据库的完整性约束不会被破坏,数据的状态保持一致。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不能被其他事务干扰,每个事务都感觉不到其他事务的存在。
- 持久性(Durability):一旦事务提交成功,其对数据库的修改将永久保存,即使系统出现故障也不会丢失。
2.在 Spring Boot 中使用事务:在 Spring Boot 中,使用事务主要通过@Transactional注解来实现。@Transactional注解可以作用在类或方法上:
- 作用在类上:当@Transactional注解作用在类上时,表示该类中的所有公共方法都将被事务管理。例如:
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional public class UserService { // 类中的所有公共方法都被事务管理 public void someBusinessMethod() { // 业务逻辑代码 } }
在上述代码中,UserService类上添加了@Transactional注解,因此该类中的someBusinessMethod方法会被事务管理。如果在someBusinessMethod方法中执行多个数据库操作,这些操作将被视为一个事务,要么全部成功提交,要么全部失败回滚。
- 作用在方法上:当@Transactional注解作用在方法上时,仅该方法会被事务管理。例如:
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Transactional public void specificBusinessMethod() { // 业务逻辑代码 } }
在这个例子中,只有specificBusinessMethod方法被事务管理,而UserService类中的其他方法不会受到事务的影响。
3.事务的传播行为:事务的传播行为定义了一个事务方法被另一个事务方法调用时,事务应该如何管理。Spring Boot 支持多种事务传播行为,常见的有以下几种:
- REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。例如:
@Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void methodA() { // 业务逻辑 serviceB.methodB(); // 业务逻辑 } } @Service public class ServiceB { @Transactional public void methodB() { // 业务逻辑 } }
在上述代码中,ServiceA的methodA方法和ServiceB的methodB方法都被标记为@Transactional。当methodA调用methodB时,由于methodA已经在一个事务中,methodB会加入到methodA的事务中,它们共享同一个事务。
- REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务。如果当前存在事务,则将当前事务挂起,直到新事务完成。例如:
@Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void methodA() { // 业务逻辑 serviceB.methodB(); // 业务逻辑 } } @Service public class ServiceB { @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // 业务逻辑 } }
这里,ServiceB的methodB方法使用了REQUIRES_NEW传播行为。当methodA调用methodB时,methodB会创建一个新的事务,并且将methodA的事务挂起,直到methodB的事务完成。这样,methodA和methodB的事务是相互独立的,互不影响。
- NESTED:如果当前存在事务,则在当前事务中创建一个嵌套事务;如果当前没有事务,则创建一个新的事务。嵌套事务可以有自己的回滚点,当嵌套事务回滚时,不会影响外层事务的提交。例如:
@Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void methodA() { try { // 业务逻辑 serviceB.methodB(); // 业务逻辑 } catch (Exception e) { // 捕获异常并处理,不会影响外层事务的提交 } } } @Service public class ServiceB { @Transactional(propagation = Propagation.NESTED) public void methodB() { // 业务逻辑 throw new RuntimeException("模拟异常"); } }
在这个例子中,ServiceB的methodB方法使用了NESTED传播行为。当methodA调用methodB时,methodB会在methodA的事务中创建一个嵌套事务。如果methodB中抛出异常,嵌套事务会回滚,但methodA的事务不会回滚,除非methodA中也抛出未处理的异常。
4.事务的隔离级别:事务隔离级别定义了一个事务对其他事务的可见性和数据一致性的保证程度。Spring Boot 支持多种事务隔离级别,常见的有以下几种:
- READ_UNCOMMITTED:读取未提交的数据。这是最低的隔离级别,一个事务可以读取到其他事务尚未提交的数据,可能会导致脏读、不可重复读和幻读等问题。
- READ_COMMITTED:读取已提交的数据。一个事务只能读取到其他事务已经提交的数据,可以避免脏读,但仍可能出现不可重复读和幻读问题。
- REPEATABLE_READ:可重复读。在一个事务内多次读取同一数据时,得到的结果是一致的,即使其他事务对该数据进行了修改并提交,也不会影响当前事务的读取结果,可以避免脏读和不可重复读,但仍可能出现幻读问题。
- SERIALIZABLE:串行化。这是最高的隔离级别,事务串行执行,避免了脏读、不可重复读和幻读等所有并发问题,但会严重影响系统的并发性能。
在 Spring Boot 中,可以通过@Transactional注解的isolation属性来指定事务的隔离级别。例如:
@Service public class UserService { @Transactional(isolation = Isolation.REPEATABLE_READ) public void someBusinessMethod() { // 业务逻辑 } }
上述代码中,someBusinessMethod方法的事务隔离级别被设置为REPEATABLE_READ,
更多推荐
所有评论(0)