## Spring Framework 相关

### 1.1 Spring Bean 名称冲突

#### 问题描述

**时间**: 2025-10-21  
**版本**: v1.0.19  
**现象**: 应用启动失败,抛出异常

```
Cannot register alias 'taskExecutor' for name 'applicationTaskExecutor': 
Alias would override bean definition 'taskExecutor'
```

#### 根本原因

自定义的 `TaskExecutor` 组件与 Spring 框架默认的异步任务执行器 bean 名称冲突:

- **框架保留名称**: `taskExecutor` (Spring @EnableAsync 相关)
- **框架保留名称**: `applicationTaskExecutor` (Spring Boot 自动配置)
- **自定义名称**: `taskExecutor` (冲突!)

#### 解决方案

**方案 1**: 显式指定 Bean 名称(推荐)

```java
@Configuration
public class TaskInfrastructureConfig {
    
    @Bean(name = "infrastructureTaskExecutor")  // 显式指定名称
    public TaskExecutor taskExecutor(
            TaskNotificationService notificationService,
            UniqueTaskManager uniqueTaskManager,
            List<TaskNotificationListener> listeners) {
        return new TaskExecutor(notificationService, uniqueTaskManager, listeners);
    }
}
```

**方案 2**: 使用 @Qualifier 注入

```java
@Service
public class TaskSchedulerService {
    
    private final TaskExecutor taskExecutor;
    
    public TaskSchedulerService(
            @Qualifier("infrastructureTaskExecutor") TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }
}
```

#### 最佳实践

1. ✅ **避免使用框架保留名称**,如 `taskExecutor`、`applicationTaskExecutor`
2. ✅ **使用更具体的命名**,如 `xxxTaskExecutor`、`yyyScheduler`
3. ✅ **显式指定 Bean 名称**,使用 `@Bean(name = "...")`
4. ✅ **依赖注入时使用 @Qualifier**,明确指定 Bean

#### 参考链接

- [Spring Boot Async 配置](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.task-execution-and-scheduling)
- CHANGELOG: v1.0.19

---
### 4.2 代码重复问题

#### 问题描述

**时间**: 2025-10-22  
**版本**: v1.0.26  
**现象**: 
- 4 个平台服务各自实现配置转换逻辑
- 重复代码约 80 行 × 4 = 320 行
- 修改逻辑需要在 4 处同步

#### 解决方案

**抽象基类 + 模板方法模式**

```java
// 抽象基类 - 提供通用逻辑
public abstract class AbstractRecruitmentService implements RecruitmentService {
    
    protected final ConfigService configService;
    protected final UserProfileRepository userProfileRepository;
    
    protected AbstractRecruitmentService(
            ConfigService configService,
            UserProfileRepository userProfileRepository) {
        this.configService = configService;
        this.userProfileRepository = userProfileRepository;
    }
    
    // 统一的配置转换逻辑
    protected ConfigDTO convertConfigEntityToDTO(ConfigEntity entity) {
        // 从 UserProfile 获取用户配置
        UserProfile profile = userProfileRepository.findById(1L).orElse(null);
        
        ConfigDTO dto = new ConfigDTO();
        // 设置用户配置字段(6 个字段)
        if (profile != null) {
            dto.setSayHi(profile.getSayHi());
            dto.setEnableAIGreeting(profile.getEnableAIGreeting());
            // ...
        }
        
        // 设置平台配置字段
        dto.setCityCodes(entity.getCityCodes());
        dto.setFilterDeadHR(entity.getFilterDeadHR());
        // ...
        
        // 调用钩子方法,允许子类添加平台特定字段
        populatePlatformSpecificFields(dto, entity);
        
        return dto;
    }
    
    // 钩子方法 - 子类可覆写
    protected void populatePlatformSpecificFields(ConfigDTO dto, ConfigEntity entity) {
        // 默认实现为空
    }
    
    // 便捷方法 - 自动加载配置
    protected ConfigDTO loadPlatformConfig() {
        ConfigEntity entity = configService.loadByPlatformType(
            getPlatform().getPlatformCode());
        
        if (entity == null) {
            log.warn("{}平台配置未找到", getPlatform().getName());
            return null;
        }
        
        return convertConfigEntityToDTO(entity);
    }
}

// 子类 - Boss 直聘
public class BossRecruitmentServiceImpl extends AbstractRecruitmentService {
    
    public BossRecruitmentServiceImpl(
            ConfigService configService,
            UserProfileRepository userProfileRepository,
            PlaywrightService playwrightService) {
        super(configService, userProfileRepository);
        this.playwrightService = playwrightService;
    }
    
    // 无需重写配置转换,直接使用基类方法
}

// 子类 - 猎聘(有特殊字段)
public class LiepinRecruitmentServiceImpl extends AbstractRecruitmentService {
    
    // 覆写钩子方法,添加平台特定字段
    @Override
    protected void populatePlatformSpecificFields(ConfigDTO dto, ConfigEntity entity) {
        // 添加猎聘特有的字段
        dto.setPublishTime(entity.getPublishTime());
    }
}
```

#### 重构收益

| 指标 | 重构前 | 重构后 | 改进 |
|-----|--------|--------|------|
| 重复代码 | 320 行 | 0 行 | -100% |
| 配置转换逻辑 | 4 处 | 1 处 | 集中管理 |
| 新增平台成本 | 高 | 低 | 继承即可 |
| 维护成本 | 高 | 低 | 一处修改 |

#### 设计模式

1. **模板方法模式**:
   - 基类定义算法框架(`convertConfigEntityToDTO`)
   - 子类实现特定步骤(`populatePlatformSpecificFields`)

2. **钩子方法**:
   - 默认实现为空
   - 子类按需覆写
   - 保持灵活性

3. **DRY 原则**:
   - Don't Repeat Yourself
   - 消除重复代码
   - 单一真实来源

#### 最佳实践

1. ✅ **识别重复代码模式**,及时抽象
2. ✅ **使用模板方法模式**,统一核心逻辑
3. ✅ **提供钩子方法**,支持特殊需求
4. ✅ **保持基类通用性**,不要过度抽象
5. ✅ **文档清晰**,说明扩展点

#### 参考链接

- [Template Method Pattern](https://refactoring.guru/design-patterns/template-method)
- CHANGELOG: v1.0.26

---