Atlas Mapper 教程系列 (6/10):Spring Boot 集成与自动配置
🎯 学习目标
通过本篇教程,你将学会:
- 理解 Atlas Mapper 的 Spring Boot 自动配置机制
- 掌握 Spring Boot Starter 的使用方法
- 学会自定义配置属性和条件装配
- 理解依赖注入和 Bean 生命周期管理
📋 概念讲解:Spring Boot 集成架构
自动配置架构图
Bean 生命周期管理
🔧 实现步骤:Spring Boot 集成详解
步骤 1:添加 Spring Boot Starter 依赖
在你的 Spring Boot 项目中添加 Atlas Mapper Starter:
<!-- pom.xml -->
<dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Atlas Mapper Spring Boot Starter --><dependency><groupId>io.github.nemoob</groupId><artifactId>atlas-mapper-spring-boot-starter</artifactId><version>1.0.0</version></dependency><!-- 其他依赖... -->
</dependencies>
步骤 2:配置属性详解
在 application.yml
中配置 Atlas Mapper:
# application.yml
atlas:mapper:# 🔥 基础配置enabled: true # 是否启用 Atlas Mappercomponent-model: spring # 组件模型:spring, default, cdi# 🔥 映射策略配置unmapped-target-policy: IGNORE # 未映射目标字段策略:IGNORE, WARN, ERRORunmapped-source-policy: WARN # 未映射源字段策略:IGNORE, WARN, ERRORnull-value-mapping-strategy: RETURN_NULL # 空值映射策略:RETURN_NULL, RETURN_DEFAULTnull-value-check-strategy: ON_IMPLICIT_CONVERSION # 空值检查策略# 🔥 代码生成配置suppress-generator-timestamp: true # 是否抑制生成器时间戳suppress-generator-version-comment: false # 是否抑制版本注释# 🔥 性能优化配置collection-mapping-strategy: ACCESSOR_ONLY # 集合映射策略builder-pattern: true # 是否使用建造者模式# 🔥 调试配置verbose: false # 是否启用详细日志show-generated-code: false # 是否显示生成的代码# 🔥 自定义配置default-component-model: spring # 默认组件模型default-injection-strategy: FIELD # 默认注入策略:FIELD, CONSTRUCTOR# 🔥 包扫描配置base-packages: # 基础包扫描路径- "com.example.mapper"- "com.example.converter"# 🔥 类型转换器配置type-converters:date-format: "yyyy-MM-dd HH:mm:ss" # 默认日期格式number-format: "#,##0.00" # 默认数字格式timezone: "Asia/Shanghai" # 时区设置# Spring Boot 相关配置
spring:application:name: atlas-mapper-demo# 数据源配置(如果需要)datasource:url: jdbc:mysql://localhost:3306/demousername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driver# JPA 配置(如果使用)jpa:hibernate:ddl-auto: updateshow-sql: trueproperties:hibernate:format_sql: true# 日志配置
logging:level:io.github.nemoob.atlas.mapper: DEBUG # Atlas Mapper 日志级别org.springframework: INFOcom.example: DEBUGpattern:console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
步骤 3:自动配置类详解
查看 Atlas Mapper 的自动配置实现:
/*** Atlas Mapper 自动配置类*/
@Configuration
@ConditionalOnClass({Mapper.class, AtlasMapperProcessor.class}) // 🔥 条件装配:类存在
@ConditionalOnProperty( // 🔥 条件装配:属性配置prefix = "atlas.mapper", name = "enabled", havingValue = "true", matchIfMissing = true
)
@EnableConfigurationProperties(AtlasMapperProperties.class) // 🔥 启用配置属性
@AutoConfigureAfter({DataSourceAutoConfiguration.class}) // 🔥 配置顺序
public class AtlasMapperAutoConfiguration {private final AtlasMapperProperties properties;public AtlasMapperAutoConfiguration(AtlasMapperProperties properties) {this.properties = properties;}/*** 🔥 配置 Atlas Mapper 处理器*/@Bean@ConditionalOnMissingBean(AtlasMapperProcessor.class)public AtlasMapperProcessor atlasMapperProcessor() {AtlasMapperProcessor processor = new AtlasMapperProcessor();// 应用配置属性processor.setUnmappedTargetPolicy(properties.getUnmappedTargetPolicy());processor.setNullValueMappingStrategy(properties.getNullValueMappingStrategy());processor.setVerbose(properties.isVerbose());processor.setSuppressGeneratorTimestamp(properties.isSuppressGeneratorTimestamp());return processor;}/*** 🔥 配置类型转换器注册表*/@Bean@ConditionalOnMissingBean(TypeConverterRegistry.class)public TypeConverterRegistry typeConverterRegistry() {TypeConverterRegistry registry = new TypeConverterRegistry();// 注册默认类型转换器registry.registerConverter(new StringToDateConverter(properties.getTypeConverters().getDateFormat()));registry.registerConverter(new NumberToStringConverter(properties.getTypeConverters().getNumberFormat()));registry.registerConverter(new BooleanToStringConverter());return registry;}/*** 🔥 配置 Mapper 扫描器*/@Bean@ConditionalOnMissingBean(MapperScanner.class)public MapperScanner mapperScanner() {MapperScanner scanner = new MapperScanner();scanner.setBasePackages(properties.getBasePackages());scanner.setComponentModel(properties.getComponentModel());return scanner;}/*** 🔥 配置性能监控(可选)*/@Bean@ConditionalOnProperty(prefix = "atlas.mapper", name = "performance-monitoring", havingValue = "true")@ConditionalOnMissingBean(MapperPerformanceMonitor.class)public MapperPerformanceMonitor performanceMonitor() {return new MapperPerformanceMonitor();}/*** 🔥 配置缓存管理器(可选)*/@Bean@ConditionalOnProperty(prefix = "atlas.mapper", name = "caching.enabled", havingValue = "true")@ConditionalOnMissingBean(MapperCacheManager.class)public MapperCacheManager cacheManager() {MapperCacheManager cacheManager = new MapperCacheManager();cacheManager.setMaxSize(properties.getCaching().getMaxSize());cacheManager.setExpireAfterWrite(properties.getCaching().getExpireAfterWrite());return cacheManager;}/*** 🔥 配置健康检查端点*/@Bean@ConditionalOnClass(HealthIndicator.class)@ConditionalOnProperty(prefix = "atlas.mapper", name = "health-check", havingValue = "true")public AtlasMapperHealthIndicator atlasMapperHealthIndicator() {return new AtlasMapperHealthIndicator();}
}
步骤 4:配置属性类
/*** Atlas Mapper 配置属性类*/
@ConfigurationProperties(prefix = "atlas.mapper")
@Data
public class AtlasMapperProperties {/*** 是否启用 Atlas Mapper*/private boolean enabled = true;/*** 组件模型*/private String componentModel = "spring";/*** 未映射目标字段策略*/private ReportingPolicy unmappedTargetPolicy = ReportingPolicy.IGNORE;/*** 未映射源字段策略*/private ReportingPolicy unmappedSourcePolicy = ReportingPolicy.WARN;/*** 空值映射策略*/private NullValueMappingStrategy nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL;/*** 空值检查策略*/private NullValueCheckStrategy nullValueCheckStrategy = NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;/*** 是否抑制生成器时间戳*/private boolean suppressGeneratorTimestamp = true;/*** 是否抑制版本注释*/private boolean suppressGeneratorVersionComment = false;/*** 集合映射策略*/private CollectionMappingStrategy collectionMappingStrategy = CollectionMappingStrategy.ACCESSOR_ONLY;/*** 是否使用建造者模式*/private boolean builderPattern = false;/*** 是否启用详细日志*/private boolean verbose = false;/*** 是否显示生成的代码*/private boolean showGeneratedCode = false;/*** 默认组件模型*/private String defaultComponentModel = "spring";/*** 默认注入策略*/private InjectionStrategy defaultInjectionStrategy = InjectionStrategy.FIELD;/*** 基础包扫描路径*/private List<String> basePackages = new ArrayList<>();/*** 类型转换器配置*/private TypeConverters typeConverters = new TypeConverters();/*** 缓存配置*/private Caching caching = new Caching();/*** 性能监控配置*/private boolean performanceMonitoring = false;/*** 健康检查配置*/private boolean healthCheck = false;/*** 类型转换器配置类*/@Datapublic static class TypeConverters {private String dateFormat = "yyyy-MM-dd HH:mm:ss";private String numberFormat = "#,##0.00";private String timezone = "Asia/Shanghai";private boolean enableCustomConverters = true;}/*** 缓存配置类*/@Datapublic static class Caching {private boolean enabled = false;private long maxSize = 1000L;private Duration expireAfterWrite = Duration.ofMinutes(30);private Duration expireAfterAccess = Duration.ofMinutes(10);}
}
步骤 5:在 Spring Boot 应用中使用
创建 Spring Boot 主类
/*** Spring Boot 应用主类*/
@SpringBootApplication
@EnableAtlasMapper // 🔥 启用 Atlas Mapper(可选,自动配置会处理)
public class AtlasMapperDemoApplication {public static void main(String[] args) {SpringApplication.run(AtlasMapperDemoApplication.class, args);}/*** 🔥 自定义配置 Bean(可选)*/@Beanpublic AtlasMapperConfigurer atlasMapperConfigurer() {return new AtlasMapperConfigurer() {@Overridepublic void configure(AtlasMapperConfiguration configuration) {// 自定义配置configuration.setDefaultDateFormat("yyyy/MM/dd");configuration.setDefaultNumberFormat("¥#,##0.00");}};}
}
创建 Mapper 接口
/*** 用户映射器 - 自动注册为 Spring Bean*/
@Mapper(componentModel = "spring") // 🔥 指定为 Spring 组件
public interface UserMapper {UserDto toDto(User user);User toEntity(UserDto dto);List<UserDto> toDtoList(List<User> users);/*** 🔥 使用配置属性中的默认格式*/@Mapping(target = "createdTime", source = "createdAt", dateFormat = "${atlas.mapper.type-converters.date-format}")UserDetailDto toDetailDto(User user);
}
在 Service 中注入使用
/*** 用户服务类 - 注入 Mapper*/
@Service
@Transactional
public class UserService {private final UserRepository userRepository;private final UserMapper userMapper; // 🔥 自动注入 Mapper// 🔥 构造器注入(推荐)public UserService(UserRepository userRepository, UserMapper userMapper) {this.userRepository = userRepository;this.userMapper = userMapper;}/*** 获取用户列表*/public List<UserDto> getAllUsers() {List<User> users = userRepository.findAll();return userMapper.toDtoList(users); // 🔥 使用注入的 Mapper}/*** 根据 ID 获取用户*/public UserDto getUserById(Long id) {User user = userRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("用户不存在: " + id));return userMapper.toDto(user);}/*** 创建用户*/public UserDto createUser(UserDto userDto) {User user = userMapper.toEntity(userDto); // 🔥 DTO -> Entityuser.setCreatedAt(LocalDateTime.now());user.setUpdatedAt(LocalDateTime.now());User savedUser = userRepository.save(user);return userMapper.toDto(savedUser); // 🔥 Entity -> DTO}/*** 更新用户*/public UserDto updateUser(Long id, UserDto userDto) {User existingUser = userRepository.findById(id).orElseThrow(() -> new EntityNotFoundException("用户不存在: " + id));// 🔥 部分更新:只更新非空字段updateUserFields(existingUser, userDto);existingUser.setUpdatedAt(LocalDateTime.now());User updatedUser = userRepository.save(existingUser);return userMapper.toDto(updatedUser);}/*** 批量转换*/public List<UserDto> convertUsers(List<User> users) {return userMapper.toDtoList(users);}/*** 辅助方法:更新用户字段*/private void updateUserFields(User target, UserDto source) {if (source.getName() != null) {target.setName(source.getName());}if (source.getEmail() != null) {target.setEmail(source.getEmail());}// 其他字段更新...}
}
创建 REST 控制器
/*** 用户控制器*/
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}/*** 获取所有用户*/@GetMappingpublic ResponseEntity<List<UserDto>> getAllUsers() {List<UserDto> users = userService.getAllUsers();return ResponseEntity.ok(users);}/*** 根据 ID 获取用户*/@GetMapping("/{id}")public ResponseEntity<UserDto> getUserById(@PathVariable Long id) {UserDto user = userService.getUserById(id);return ResponseEntity.ok(user);}/*** 创建用户*/@PostMappingpublic ResponseEntity<UserDto> createUser(@Valid @RequestBody UserDto userDto) {UserDto createdUser = userService.createUser(userDto);return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);}/*** 更新用户*/@PutMapping("/{id}")public ResponseEntity<UserDto> updateUser(@PathVariable Long id, @Valid @RequestBody UserDto userDto) {UserDto updatedUser = userService.updateUser(id, userDto);return ResponseEntity.ok(updatedUser);}/*** 删除用户*/@DeleteMapping("/{id}")public ResponseEntity<Void> deleteUser(@PathVariable Long id) {userService.deleteUser(id);return ResponseEntity.noContent().build();}
}
💻 示例代码:完整的 Spring Boot 集成示例
示例 1:多环境配置
# application.yml - 基础配置
atlas:mapper:enabled: truecomponent-model: springbase-packages:- "com.example.mapper"---
# application-dev.yml - 开发环境
spring:config:activate:on-profile: devatlas:mapper:verbose: true # 开发环境启用详细日志show-generated-code: true # 显示生成的代码performance-monitoring: true # 启用性能监控logging:level:io.github.nemoob.atlas.mapper: DEBUG---
# application-prod.yml - 生产环境
spring:config:activate:on-profile: prodatlas:mapper:verbose: false # 生产环境关闭详细日志show-generated-code: falseperformance-monitoring: true # 保持性能监控caching:enabled: true # 启用缓存max-size: 10000expire-after-write: PT1H # 1小时过期logging:level:io.github.nemoob.atlas.mapper: WARN
示例 2:自定义配置器
/*** 自定义 Atlas Mapper 配置器*/
@Configuration
public class AtlasMapperCustomConfiguration {/*** 🔥 自定义类型转换器*/@Beanpublic CustomTypeConverter customTypeConverter() {return new CustomTypeConverter();}/*** 🔥 自定义映射策略*/@Beanpublic AtlasMapperConfigurer customMapperConfigurer() {return configuration -> {// 自定义默认配置configuration.setDefaultDateFormat("yyyy年MM月dd日");configuration.setDefaultNumberFormat("¥#,##0.00");configuration.setDefaultTimeZone(ZoneId.of("Asia/Shanghai"));// 注册自定义转换器configuration.addTypeConverter(new MoneyConverter());configuration.addTypeConverter(new PhoneNumberConverter());// 设置命名策略configuration.setFieldNamingStrategy(new CamelCaseNamingStrategy());};}/*** 🔥 自定义 Mapper 后处理器*/@Beanpublic MapperPostProcessor mapperPostProcessor() {return new MapperPostProcessor() {@Overridepublic void postProcessMapper(Object mapper, String mapperName) {log.info("Mapper 创建完成: {}", mapperName);// 可以在这里添加额外的初始化逻辑if (mapper instanceof BaseMapper) {((BaseMapper) mapper).initialize();}}};}/*** 🔥 条件化配置 - 仅在特定条件下生效*/@Bean@ConditionalOnProperty(prefix = "atlas.mapper", name = "advanced-features", havingValue = "true")public AdvancedMapperFeatures advancedMapperFeatures() {return new AdvancedMapperFeatures();}/*** 🔥 Profile 特定配置*/@Bean@Profile("development")public MapperDebugger mapperDebugger() {return new MapperDebugger();}
}
示例 3:健康检查和监控
/*** Atlas Mapper 健康检查指示器*/
@Component
@ConditionalOnProperty(prefix = "atlas.mapper", name = "health-check", havingValue = "true")
public class AtlasMapperHealthIndicator implements HealthIndicator {private final MapperRegistry mapperRegistry;private final MapperPerformanceMonitor performanceMonitor;public AtlasMapperHealthIndicator(MapperRegistry mapperRegistry, MapperPerformanceMonitor performanceMonitor) {this.mapperRegistry = mapperRegistry;this.performanceMonitor = performanceMonitor;}@Overridepublic Health health() {try {// 检查 Mapper 注册状态int registeredMappers = mapperRegistry.getRegisteredMapperCount();// 检查性能指标PerformanceMetrics metrics = performanceMonitor.getMetrics();// 构建健康状态Health.Builder builder = Health.up().withDetail("registeredMappers", registeredMappers).withDetail("totalMappings", metrics.getTotalMappings()).withDetail("averageExecutionTime", metrics.getAverageExecutionTime()).withDetail("errorRate", metrics.getErrorRate());// 检查错误率if (metrics.getErrorRate() > 0.1) { // 错误率超过 10%builder.down().withDetail("reason", "High error rate: " + metrics.getErrorRate());}// 检查平均执行时间if (metrics.getAverageExecutionTime() > 1000) { // 平均执行时间超过 1 秒builder.down().withDetail("reason", "High execution time: " + metrics.getAverageExecutionTime() + "ms");}return builder.build();} catch (Exception e) {return Health.down().withDetail("error", e.getMessage()).build();}}
}
示例 4:性能监控和指标
/*** Atlas Mapper 性能监控*/
@Component
@ConditionalOnProperty(prefix = "atlas.mapper", name = "performance-monitoring", havingValue = "true")
public class AtlasMapperPerformanceMonitor {private final MeterRegistry meterRegistry;private final Timer.Sample sample;public AtlasMapperPerformanceMonitor(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;this.sample = Timer.start(meterRegistry);}/*** 记录映射执行时间*/public void recordMappingTime(String mapperName, String methodName, long executionTime) {Timer.builder("atlas.mapper.execution.time").tag("mapper", mapperName).tag("method", methodName).register(meterRegistry).record(executionTime, TimeUnit.MILLISECONDS);}/*** 记录映射错误*/public void recordMappingError(String mapperName, String methodName, Exception error) {Counter.builder("atlas.mapper.errors").tag("mapper", mapperName).tag("method", methodName).tag("error", error.getClass().getSimpleName()).register(meterRegistry).increment();}/*** 记录映射成功*/public void recordMappingSuccess(String mapperName, String methodName) {Counter.builder("atlas.mapper.success").tag("mapper", mapperName).tag("method", methodName).register(meterRegistry).increment();}/*** 获取性能指标*/public PerformanceMetrics getMetrics() {// 实现指标收集逻辑return new PerformanceMetrics();}
}/*** 性能指标数据类*/
@Data
public class PerformanceMetrics {private long totalMappings;private double averageExecutionTime;private double errorRate;private Map<String, Long> mapperUsageCount;private Map<String, Double> mapperAverageTime;
}
🎬 效果演示:Spring Boot 集成测试
创建集成测试
/*** Spring Boot 集成测试*/
@SpringBootTest
@TestPropertySource(properties = {"atlas.mapper.enabled=true","atlas.mapper.verbose=true","atlas.mapper.performance-monitoring=true"
})
class AtlasMapperIntegrationTest {@Autowiredprivate UserService userService;@Autowiredprivate UserMapper userMapper;@Autowiredprivate AtlasMapperProperties properties;@Testvoid testMapperAutoConfiguration() {// 验证 Mapper 自动注入assertThat(userMapper).isNotNull();assertThat(userService).isNotNull();}@Testvoid testConfigurationProperties() {// 验证配置属性assertThat(properties.isEnabled()).isTrue();assertThat(properties.isVerbose()).isTrue();assertThat(properties.getComponentModel()).isEqualTo("spring");}@Testvoid testMappingFunctionality() {// 创建测试数据User user = new User();user.setId(1L);user.setName("张三");user.setEmail("zhangsan@example.com");user.setCreatedAt(LocalDateTime.now());// 测试映射功能UserDto dto = userMapper.toDto(user);assertThat(dto).isNotNull();assertThat(dto.getId()).isEqualTo(user.getId());assertThat(dto.getName()).isEqualTo(user.getName());assertThat(dto.getEmail()).isEqualTo(user.getEmail());}@Testvoid testServiceIntegration() {// 测试服务层集成UserDto userDto = new UserDto();userDto.setName("李四");userDto.setEmail("lisi@example.com");// 这里需要 Mock Repository 或使用 @DataJpaTest// UserDto result = userService.createUser(userDto);// assertThat(result).isNotNull();}
}
启动应用测试
# 启动应用
mvn spring-boot:run# 或者使用不同的 Profile
mvn spring-boot:run -Dspring-boot.run.profiles=dev# 测试 API 端点
curl -X GET http://localhost:8080/api/users
curl -X POST http://localhost:8080/api/users \-H "Content-Type: application/json" \-d '{"name":"张三","email":"zhangsan@example.com"}'# 查看健康检查
curl http://localhost:8080/actuator/health/atlasMapper# 查看性能指标
curl http://localhost:8080/actuator/metrics/atlas.mapper.execution.time
❓ 常见问题
Q1: Mapper 没有被自动注入怎么办?
A: 检查以下几个方面:
// 1. 确保 @Mapper 注解配置正确
@Mapper(componentModel = "spring") // 必须指定 componentModel
public interface UserMapper {// ...
}// 2. 确保包扫描路径正确
@SpringBootApplication
@ComponentScan(basePackages = {"com.example"}) // 包含 Mapper 所在包
public class Application {// ...
}// 3. 检查配置是否启用
atlas:mapper:enabled: true # 确保启用// 4. 查看生成的实现类
// target/generated-sources/annotations/ 目录下应该有生成的实现类
Q2: 如何自定义 Mapper 的 Bean 名称?
A: 使用 @Component
注解指定名称:
@Mapper(componentModel = "spring")
@Component("customUserMapper") // 自定义 Bean 名称
public interface UserMapper {// ...
}// 注入时使用自定义名称
@Autowired
@Qualifier("customUserMapper")
private UserMapper userMapper;
Q3: 如何在不同环境使用不同的映射策略?
A: 使用 Profile 特定配置:
# application-dev.yml
atlas:mapper:unmapped-target-policy: WARN # 开发环境警告verbose: true# application-prod.yml
atlas:mapper:unmapped-target-policy: IGNORE # 生产环境忽略verbose: false
Q4: 如何监控 Mapper 的性能?
A: 启用性能监控和指标收集:
# 配置
atlas:mapper:performance-monitoring: truemanagement:endpoints:web:exposure:include: health,metrics,infoendpoint:health:show-details: always
// 自定义性能监控
@Component
public class MapperPerformanceAspect {@Around("@within(io.github.nemoob.atlas.mapper.Mapper)")public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();try {Object result = joinPoint.proceed();long executionTime = System.currentTimeMillis() - startTime;log.info("Mapper method {} executed in {} ms", joinPoint.getSignature().getName(), executionTime);return result;} catch (Exception e) {log.error("Mapper method {} failed", joinPoint.getSignature().getName(), e);throw e;}}
}
🎯 本章小结
通过本章学习,你应该掌握了:
- 自动配置:理解 Spring Boot 自动配置机制和条件装配
- 配置属性:掌握配置文件的使用和自定义配置
- 依赖注入:学会在 Spring 容器中使用 Mapper
- 监控集成:了解健康检查和性能监控的实现