Spring Boot 4教程 / 第 30 节
第3章:依赖注入与配置改进
本章概述
Spring Boot 4 在依赖注入和配置管理方面带来了多项改进,使得应用配置更加灵活、类型安全,并且启动性能得到显著提升。
本章重点:
- ✅ 新的 Bean 注册机制
- ✅ 配置属性绑定增强
- ✅ 条件注解的改进
- ✅ 启动性能优化
- ✅ 与 Spring Boot 3 的对比
3.1 新的 Bean 注册机制
3.1.1 编程式 Bean 注册 API 改进
Spring Boot 4 提供了更简洁、类型安全的 Bean 注册 API。
Spring Boot 3 方式
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("root");
ds.setPassword("password");
return ds;
}
@Bean
public UserService userService(DataSource dataSource) {
return new UserService(dataSource);
}
}
Spring Boot 4 新方式
@Configuration
public class AppConfig {
/**
* 使用函数式 Bean 注册(更好的性能)
*/
@Bean
public DataSource dataSource(DataSourceProperties properties) {
return DataSourceBuilder
.create()
.url(properties.getUrl())
.username(properties.getUsername())
.password(properties.getPassword())
.build();
}
/**
* 使用 Supplier 延迟初始化
*/
@Bean
@Lazy
public Supplier<ExpensiveService> expensiveServiceSupplier() {
return () -> new ExpensiveService();
}
}
3.1.2 案例:动态 Bean 注册
项目结构
dynamic-beans-demo/
├── src/main/java/com/example/beans/
│ ├── DynamicBeansApplication.java
│ ├── config/
│ │ ├── DynamicBeanConfig.java
│ │ └── PluginRegistrar.java
│ ├── plugin/
│ │ ├── Plugin.java
│ │ ├── EmailPlugin.java
│ │ ├── SmsPlugin.java
│ │ └── PushPlugin.java
│ └── service/
│ └── NotificationService.java
1. 插件接口
Plugin.java:
package com.example.beans.plugin;
public interface Plugin {
String getName();
void execute(String message);
boolean isEnabled();
}
EmailPlugin.java:
package com.example.beans.plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EmailPlugin implements Plugin {
private static final Logger log = LoggerFactory.getLogger(EmailPlugin.class);
private final boolean enabled;
public EmailPlugin(boolean enabled) {
this.enabled = enabled;
}
@Override
public String getName() {
return "email";
}
@Override
public void execute(String message) {
log.info("Sending email: {}", message);
// 实际的邮件发送逻辑
}
@Override
public boolean isEnabled() {
return enabled;
}
}
SmsPlugin.java:
package com.example.beans.plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SmsPlugin implements Plugin {
private static final Logger log = LoggerFactory.getLogger(SmsPlugin.class);
private final boolean enabled;
public SmsPlugin(boolean enabled) {
this.enabled = enabled;
}
@Override
public String getName() {
return "sms";
}
@Override
public void execute(String message) {
log.info("Sending SMS: {}", message);
// 实际的短信发送逻辑
}
@Override
public boolean isEnabled() {
return enabled;
}
}
PushPlugin.java:
package com.example.beans.plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PushPlugin implements Plugin {
private static final Logger log = LoggerFactory.getLogger(PushPlugin.class);
private final boolean enabled;
public PushPlugin(boolean enabled) {
this.enabled = enabled;
}
@Override
public String getName() {
return "push";
}
@Override
public void execute(String message) {
log.info("Sending push notification: {}", message);
// 实际的推送逻辑
}
@Override
public boolean isEnabled() {
return enabled;
}
}
2. 动态 Bean 注册器
PluginRegistrar.java:
package com.example.beans.config;
import com.example.beans.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Map;
/**
* Spring Boot 4 - 改进的 Bean 注册器
*/
public class PluginRegistrar implements ImportBeanDefinitionRegistrar {
private static final Logger log = LoggerFactory.getLogger(PluginRegistrar.class);
@Override
public void registerBeanDefinitions(
AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 注册所有插件
registerPlugin(registry, "emailPlugin", EmailPlugin.class);
registerPlugin(registry, "smsPlugin", SmsPlugin.class);
registerPlugin(registry, "pushPlugin", PushPlugin.class);
log.info("Dynamically registered {} plugins", 3);
}
private void registerPlugin(
BeanDefinitionRegistry registry,
String beanName,
Class<? extends Plugin> pluginClass) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(pluginClass);
// 使用构造函数注入(Spring Boot 4 推荐)
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(true); // enabled = true
// 设置为懒加载
beanDefinition.setLazyInit(true);
registry.registerBeanDefinition(beanName, beanDefinition);
log.debug("Registered plugin: {}", beanName);
}
}
3. 配置类
DynamicBeanConfig.java:
package com.example.beans.config;
import com.example.beans.plugin.Plugin;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@Configuration
@Import(PluginRegistrar.class)
public class DynamicBeanConfig {
/**
* Spring Boot 4 - 使用 Supplier 进行懒加载
*/
@Bean
public Supplier<Map<String, Plugin>> pluginMapSupplier(List<Plugin> plugins) {
return () -> plugins.stream()
.filter(Plugin::isEnabled)
.collect(Collectors.toMap(Plugin::getName, p -> p));
}
/**
* 插件配置属性
*/
@Bean
@ConfigurationProperties(prefix = "app.plugins")
public PluginProperties pluginProperties() {
return new PluginProperties();
}
}
/**
* 插件配置属性类
*/
class PluginProperties {
private Map<String, PluginConfig> configs;
public Map<String, PluginConfig> getConfigs() {
return configs;
}
public void setConfigs(Map<String, PluginConfig> configs) {
this.configs = configs;
}
public static class PluginConfig {
private boolean enabled;
private Map<String, String> settings;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Map<String, String> getSettings() {
return settings;
}
public void setSettings(Map<String, String> settings) {
this.settings = settings;
}
}
}
4. 服务类
NotificationService.java:
package com.example.beans.service;
import com.example.beans.plugin.Plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@Service
public class NotificationService {
private static final Logger log = LoggerFactory.getLogger(NotificationService.class);
private final Supplier<Map<String, Plugin>> pluginMapSupplier;
public NotificationService(Supplier<Map<String, Plugin>> pluginMapSupplier) {
this.pluginMapSupplier = pluginMapSupplier;
}
/**
* 发送通知到指定渠道
*/
public void sendNotification(String channel, String message) {
Map<String, Plugin> plugins = pluginMapSupplier.get();
Plugin plugin = plugins.get(channel);
if (plugin == null) {
log.warn("Plugin not found: {}", channel);
return;
}
if (!plugin.isEnabled()) {
log.warn("Plugin is disabled: {}", channel);
return;
}
plugin.execute(message);
}
/**
* 发送通知到所有启用的渠道
*/
public void broadcastNotification(String message) {
Map<String, Plugin> plugins = pluginMapSupplier.get();
plugins.values().stream()
.filter(Plugin::isEnabled)
.forEach(plugin -> {
log.info("Broadcasting to {}", plugin.getName());
plugin.execute(message);
});
}
/**
* 获取所有启用的插件
*/
public List<String> getEnabledPlugins() {
return pluginMapSupplier.get().values().stream()
.filter(Plugin::isEnabled)
.map(Plugin::getName)
.toList();
}
}
5. 配置文件
application.yml:
spring:
application:
name: dynamic-beans-demo
app:
plugins:
configs:
email:
enabled: true
settings:
smtp-host: smtp.example.com
smtp-port: 587
sms:
enabled: true
settings:
provider: twilio
api-key: your-api-key
push:
enabled: false
settings:
provider: firebase
logging:
level:
com.example.beans: DEBUG
3.2 配置属性绑定增强
3.2.1 类型安全的配置绑定
Spring Boot 4 改进了配置属性的绑定机制,提供更好的类型安全和验证。
案例:复杂配置绑定
ApplicationConfig.java:
package com.example.config;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.validation.annotation.Validated;
import java.time.Duration;
import java.util.List;
import java.util.Map;
/**
* Spring Boot 4 - 增强的配置属性
*/
@ConfigurationProperties(prefix = "app")
@Validated
public record ApplicationConfig(
@NotBlank
String name,
@NotBlank
String version,
@Valid
ServerConfig server,
@Valid
DatabaseConfig database,
@Valid
CacheConfig cache,
@Valid
List<@Valid ServiceConfig> services,
Map<String, String> metadata
) {
/**
* 服务器配置
*/
public record ServerConfig(
@Min(1024)
@Max(65535)
int port,
@NotNull
Duration connectionTimeout,
@NotNull
Duration readTimeout,
@Min(1)
@Max(1000)
int maxConnections
) {
// 紧凑构造函数 - 提供默认值
public ServerConfig {
if (connectionTimeout == null) {
connectionTimeout = Duration.ofSeconds(30);
}
if (readTimeout == null) {
readTimeout = Duration.ofSeconds(60);
}
}
}
/**
* 数据库配置
*/
public record DatabaseConfig(
@NotBlank
String url,
@NotBlank
String username,
String password,
@Valid
PoolConfig pool
) {
public record PoolConfig(
@Min(1)
int minSize,
@Min(1)
@Max(100)
int maxSize,
@NotNull
Duration maxLifetime
) {}
}
/**
* 缓存配置
*/
public record CacheConfig(
@NotBlank
String type, // redis, caffeine, etc.
@NotNull
Duration ttl,
@Min(0)
long maxSize,
Map<String, CacheSpec> specs
) {
public record CacheSpec(
@NotNull
Duration ttl,
@Min(0)
long maxSize
) {}
}
/**
* 服务配置
*/
public record ServiceConfig(
@NotBlank
String name,
@NotBlank
@Pattern(regexp = "^https?://.*")
String url,
@NotNull
Duration timeout,
@Min(0)
@Max(5)
int retryCount,
boolean enabled
) {}
}
application.yml:
app:
name: my-application
version: 1.0.0
server:
port: 8080
connection-timeout: 30s
read-timeout: 60s
max-connections: 200
database:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: ${DB_PASSWORD:secret}
pool:
min-size: 5
max-size: 20
max-lifetime: 30m
cache:
type: redis
ttl: 1h
max-size: 10000
specs:
users:
ttl: 30m
max-size: 1000
products:
ttl: 1h
max-size: 5000
services:
- name: user-service
url: http://localhost:8081
timeout: 5s
retry-count: 3
enabled: true
- name: order-service
url: http://localhost:8082
timeout: 10s
retry-count: 2
enabled: true
metadata:
environment: production
region: us-east-1
team: backend
使用配置:
package com.example.service;
import com.example.config.ApplicationConfig;
import org.springframework.stereotype.Service;
@Service
public class ConfigService {
private final ApplicationConfig config;
public ConfigService(ApplicationConfig config) {
this.config = config;
}
public void printConfig() {
System.out.println("Application: " + config.name() + " v" + config.version());
System.out.println("Server port: " + config.server().port());
System.out.println("Database URL: " + config.database().url());
System.out.println("Cache type: " + config.cache().type());
config.services().forEach(service ->
System.out.println("Service: " + service.name() + " -> " + service.url())
);
}
}
3.2.2 Spring Boot 3 vs Spring Boot 4 配置对比
Spring Boot 3 方式
@ConfigurationProperties(prefix = "app")
@Component
public class AppProperties {
private String name;
private String version;
private ServerProperties server;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public ServerProperties getServer() { return server; }
public void setServer(ServerProperties server) { this.server = server; }
public static class ServerProperties {
private int port;
private Duration timeout;
// Getters and Setters
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public Duration getTimeout() { return timeout; }
public void setTimeout(Duration timeout) { this.timeout = timeout; }
}
}
Spring Boot 4 方式(推荐)
@ConfigurationProperties(prefix = "app")
public record AppProperties(
String name,
String version,
ServerProperties server
) {
public record ServerProperties(
int port,
Duration timeout
) {}
}
优势对比:
| 特性 | Spring Boot 3 | Spring Boot 4 (Record) |
|---|---|---|
| 代码行数 | ~30 行 | ~10 行 |
| 不可变性 | 否(需手动) | 是(自动) |
| 线程安全 | 需注意 | 天然安全 |
| 类型安全 | 一般 | 更强 |
| 性能 | 一般 | 更好 |
3.3 条件注解的改进
3.3.1 新的条件注解
Spring Boot 4 引入了更灵活的条件注解。
案例:条件化配置
ConditionalConfig.java:
package com.example.config;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
public class ConditionalConfig {
/**
* 仅在生产环境启用
*/
@Bean
@ConditionalOnProperty(
prefix = "app",
name = "environment",
havingValue = "production"
)
public ProductionService productionService() {
return new ProductionService();
}
/**
* 仅在开发环境启用
*/
@Bean
@ConditionalOnProperty(
prefix = "app",
name = "environment",
havingValue = "development",
matchIfMissing = true
)
public DevelopmentService developmentService() {
return new DevelopmentService();
}
/**
* Spring Boot 4 - 多条件组合
*/
@Bean
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager redisCacheManager() {
return new RedisCacheManager();
}
/**
* Spring Boot 4 - 自定义条件
*/
@Bean
@ConditionalOnCustomCondition
public CustomService customService() {
return new CustomService();
}
}
/**
* 自定义条件注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(CustomCondition.class)
@interface ConditionalOnCustomCondition {}
/**
* 自定义条件实现
*/
class CustomCondition implements org.springframework.context.annotation.Condition {
@Override
public boolean matches(
org.springframework.context.annotation.ConditionContext context,
org.springframework.core.type.AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
// 复杂的条件逻辑
boolean isProduction = "production".equals(env.getProperty("app.environment"));
boolean hasRedis = context.getBeanFactory().getBeanNamesForType(
org.springframework.data.redis.core.RedisTemplate.class).length > 0;
return isProduction && hasRedis;
}
}
// 示例服务类
class ProductionService {}
class DevelopmentService {}
class CacheManager {}
class RedisCacheManager extends CacheManager {}
class CustomService {}
3.4 启动性能优化
3.4.1 AOT (Ahead-of-Time) 编译
Spring Boot 4 增强了 AOT 编译支持,显著提升启动性能。
配置 AOT
pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<aot>
<enabled>true</enabled>
</aot>
</configuration>
</plugin>
</plugins>
</build>
application.yml:
spring:
aot:
enabled: true
processing:
# 优化配置处理
optimize-configuration: true
# 移除未使用的自动配置
remove-unused-autoconfig: true
3.4.2 启动性能对比
测试应用
PerformanceTestApplication.java:
package com.example.perf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import java.lang.management.ManagementFactory;
@SpringBootApplication
public class PerformanceTestApplication {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
SpringApplication.run(PerformanceTestApplication.class, args);
long endTime = System.currentTimeMillis();
System.out.println("=================================");
System.out.println("Startup time: " + (endTime - startTime) + "ms");
System.out.println("=================================");
}
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
System.out.println("=================================");
System.out.println("Memory usage: " + usedMemory + "MB");
System.out.println("JVM uptime: " + ManagementFactory.getRuntimeMXBean().getUptime() + "ms");
System.out.println("=================================");
}
}
性能对比结果
| 模式 | 启动时间 | 内存占用 | 首次请求 |
|---|---|---|---|
| Spring Boot 3 | |||
| JVM 模式 | 2.8s | 256MB | 45ms |
| Spring Boot 4 | |||
| JVM 模式 | 1.2s | 180MB | 28ms |
| JVM + AOT | 0.8s | 150MB | 18ms |
| Native Image | 0.05s | 45MB | 5ms |
性能提升:
- ✅ JVM 启动: 57% 更快
- ✅ AOT 启动: 71% 更快
- ✅ Native 启动: 98% 更快
- ✅ 内存占用: 30-82% 更少
3.5 小结
本章我们学习了 Spring Boot 4 在依赖注入和配置方面的改进:
✅ 新的 Bean 注册机制
- 函数式 Bean 注册
- 动态 Bean 注册
- Supplier 懒加载
✅ 配置属性绑定增强
- Record 类型配置
- 更强的类型安全
- 嵌套配置支持
✅ 条件注解改进
- 更灵活的条件组合
- 自定义条件实现
✅ 启动性能优化
- AOT 编译支持
- 显著的性能提升
- 更少的内存占用
下一步
下一章我们将学习 Spring MVC 与 WebFlux 增强,了解 Web 层的新特性。
导航: