⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7424906244215193636 「离开地球表面_99」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

0. 引入

Spring Boot 以其简化配置、快速开发和微服务支持等特点,成为了 Java 开发的首选框架。本文将结合我在实际工作中遇到的问题,分享五个高效的 Spring Boot 的技巧。希望这些技巧能对你有所帮助。

1. Spring Boot 执行初始化逻辑

1.1 背景

项目的某次更新,数据库中的某张表新增了一个字段,且与业务有关联,需要对新建的字段根据对应的业务进行赋值操作。

一种解决方案就是,更新前手动写 SQL 更新字段的值,但这样做的效率太低,而且每给不同环境更新一次,就需要手动执行一次,容易出错且效率低。

另一种方案则是在项目启动时进行初始化操作,完成字段对应值的更新,这种方案效率更高且不容易出错。

1.2 实现

Spring Boot 提供了多种方案用于项目启动后执行初始化的逻辑。

实现CommandLineRunner接口,重写run方法。

@Slf4j
@Component
public class InitListen implements CommandLineRunner {

@Override
public void run(String... args) {
// 初始化相关逻辑...
}


}

实现ApplicationRunner接口,重写run方法。

@Slf4j
@Component
public class InitListen implements ApplicationRunner {

@Override
public void run(ApplicationArguments args) {
// 初始化相关逻辑...
}


}

实现ApplicationListener接口

@Slf4j
@Configuration
public class StartClientListener implements ApplicationListener<ContextRefreshedEvent> {

@Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
// 初始化逻辑
}
}

针对于上述这个需求,如何实现仅更新一次字段的值?

可在数据库字典表中设置一个更新标识字段,每次执行初始化逻辑之前,校验判断下字典中的这个值,确认是否已经更新,如果已经更新,就不需要再执行更新操作了。

2. Spring Boot 动态控制数据源的加载

2.1 背景

期望通过在application.yml文件中,添加一个开关来控制是否加载数据库。

2.2 实现

启动类上添加注解 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }),代表禁止 Spring Boot 自动注入数据源。

新建 DataSourceConfig配置类,用于初始化数据源。

在DataSourceConfig配置类上添加条件注解 @ConditionalOnProperty(name = "spring.datasource.enabled", havingValue = "true"),代表只有当 spring.datasource.enabled 为 true时,加载数据库,其余情况不加载数据库。

仓库类 XxxRepository 的注入,需要使用注解 @Autowired(required = false)

3. Spring Boot 根据不同环境加载配置文件

3.1 背景

实际开发工作中,针对同一个项目,可能会存在开发环境、测试环境、正式环境等,不同环境的配置内容可能会不一致,如:数据库、Redis等等。期望在项目在启动时能够针对不同的环境来加载不同的配置文件。

3.2 实现

Spring 提供 Profiles 特性,通过启动时设置指令-Dspring.profiles.active指定加载的配置文件,同一个配置文件中不同的配置使用---来区分。

启动 jar 包时执行命令:

java -jar test.jar -Dspring.profiles.active=dev

-Dspring.profiles.active=dev代表激活 profiles 为 dev 的相关配置。

## 用---区分环境,不同环境获取不同配置
---
# 开发环境
spring:
profiles: dev
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 命名空间为默认,所以不需要写命名空间
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
extension-configs[0]:
data-id: database-base.yaml
group: DEFAULT_GROUP
refresh: true
extension-configs[1]:
# 本地单机Redis
data-id: redis-base-auth.yaml
group: DEFAULT_GROUP
refresh: true
extension-configs[2]:
data-id: master-base-auth.yaml
group: DEFAULT_GROUP
refresh: true
---
#测试环境
spring:
profiles: test
cloud:
nacos:
discovery:
server-addr: 192.168.0.111:8904
# 测试环境注册的命名空间
namespace: b80b921d-cd74-4f22-8025-333d9b3d0e1d
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
extension-configs[0]:
data-id: database-base-test.yaml
group: DEFAULT_GROUP
refresh: true
extension-configs[1]:
data-id: redis-base-test.yaml
group: DEFAULT_GROUP
refresh: true
extension-configs[2]:
data-id: master-auth-test.yaml
group: DEFAULT_GROUP
refresh: true

---
# 生产环境
spring:
profiles: prod
cloud:
nacos:
discovery:
server-addr: 192.168.0.112:8848
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
extension-configs[0]:
# 生产环境
data-id: database-auth.yaml
group: DEFAULT_GROUP
refresh: true
extension-configs[1]:
# 生产环境
data-id: redis-base-auth.yaml
group: DEFAULT_GROUP
refresh: true
extension-configs[2]:
data-id: master-base-auth.yaml
group: DEFAULT_GROUP
refresh: true

也可以定义多个配置文件,如在application.yml中定义和环境无关的配置,而application-{profile}.yml则根据环境做不同区分,如在 application-dev.yml 中定义开发环境相关配置、application-test.yml 中定义测试环境相关配置。

启动时指定环境命令同上,仍为:

java -jar test.jar -Dspring.profiles.active=dev

4. Spring Boot 配置文件加密

4.1 背景

配置文件中包含的敏感信息(如数据库密码)都会以明文的形式存储,这种情况可能会存在安全风险,期望通过加密配置文件,确保应用程序的安全。

4.2 实现

pom.xml 文件中引入依赖。

<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>

如果遇到 Unresolved dependency: 'com.github.ulisesbocchio:jasypt-spring-boot-starter:jar:2.1.2' 的错误,可执行mvn clean install -U强制更新依赖。

application.yml 文件中增加配置如下:

jasypt:
encryptor:
password: G0C3D17o2n6
algorithm: PBEWithMD5AndDES

执行测试用例,获取加密后的内容。

@RunWith(SpringRunner.class)
@SpringBootTest
public class DatabaseTest {

@Autowired
private StringEncryptor encryptor;

@Test
public void getPass() {
String url = encryptor.encrypt("jdbc:mysql://localhost:3306/demo");
String name = encryptor.encrypt("root");
String password = encryptor.encrypt("123456");
System.out.println("database url: " + url);
System.out.println("database name: " + name);
System.out.println("database password: " + password);
Assert.assertTrue(url.length() > 0);
Assert.assertTrue(name.length() > 0);
Assert.assertTrue(password.length() > 0);
}
}

根据测试用例获取的结果,将加密后的字符串替换明文。

启动程序,验证数据库能否正常连接。

为了防止 jasypt.encryptor.password 泄露,反解出密码,有两种方案:

将 jasypt.encryptor.password 设置为环境变量,如:

vim /etc/profile
export jasypt.encryptor.password=YOUR_SECRET_KEY

将 jasypt.encryptor.password 作为启动程序的参数,如:

java -jar xxx.jar -Djasypt.encryptor.password=YOUR_SECRET_KEY

5. Spring Boot对打包好的jar包瘦身

5.1 背景

Sprng Boot项目的 jar 包动辄几百MB,如果有小的需求更新或者是Bug修复,就需要重新打包部署,改了一行代码,却上传几百MB的文件,这样会很浪费时间。

期望通过给 jar 包瘦身,从而节省部署的时间。

5.2 实现

pom.xml 文件中添加如下配置:

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<layout>ZIP</layout>
<!--这里是填写需要包含进去的jar,
必须项目中的某些模块,会经常变动,那么就应该将其坐标写进来
如果没有则nothing ,表示不打包依赖 -->
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>

<!--拷贝依赖到jar外面的lib目录-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!--指定的依赖路径-->
<outputDirectory>
${project.build.directory}/lib
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

执行mvn clean package得到 jar 包,在项目启动时,需要通过 -Dloader.path指定lib的路径,如:

java -Dloader.path=./lib -jar testProject-0.0.1-SNAPSHOT.jar

效果如下:

通过分析 jar 包的结构可以得知,jar 包的 “大” 实际上是因为在打包时,会将项目所依赖的 jar 包放在 lib 夹文件中。而这部分依赖在版本迭代稳定后,基本是不会变化的。

上述这种给 jar 包瘦身的方案,实际上是在打包的时候忽略 lib 文件夹中的这些依赖,将这部分不变的依赖提前放到服务器上,打出来的 jar 包就变小了,从而提升发版效率。

文章目录
  1. 1. 0. 引入
  2. 2. 1. Spring Boot 执行初始化逻辑
    1. 2.0.1. 1.1 背景
    2. 2.0.2. 1.2 实现
  • 3. 2. Spring Boot 动态控制数据源的加载
    1. 3.0.1. 2.1 背景
    2. 3.0.2. 2.2 实现
  • 4. 3. Spring Boot 根据不同环境加载配置文件
    1. 4.0.1. 3.1 背景
    2. 4.0.2. 3.2 实现
  • 5. 4. Spring Boot 配置文件加密
    1. 5.0.1. 4.1 背景
    2. 5.0.2. 4.2 实现
  • 6. 5. Spring Boot对打包好的jar包瘦身
    1. 6.0.1. 5.1 背景
    2. 6.0.2. 5.2 实现