文章目录
- 1、策略模式
- 2、聚合策略类实现方式一
- 3、聚合策略类实现方式二
- 4、对比
- 5、补充:ApplicationContextAware接口
1、策略模式
近期工作中,需要处理4.x和5.x两个版本的数据,所以自然想到的是策略模式,写一个抽象类,然后两个版本分别实现抽象类,以后也好扩展。
public interface ClusterMetaDataProcessor {
void processData();
}
public class Version4ClusterMetaDataProcessor implements ClusterMetaDataProcessor {
@Override
void processData() {
//...
}
}
public class Version5ClusterMetaDataProcessor implements ClusterMetaDataProcessor {
@Override
void processData() {
//...
}
}
然后写个聚合策略类,或者叫环境类,给调用者统一使用,此时有两种实现方式,如下
2、聚合策略类实现方式一
使用ApplicationContextAware接口获取实现类的Bean对象:
@Component
public class MetaDataProcessorFactory implements ApplicationContextAware {
private final Map<String, ClusterMetaDataProcessor> PROCESSOR_MAP = new ConcurrentHashMap<>();
public ClusterMetaDataProcessor getProcessor(String version) {
ClusterMetaDataProcessor processor = PROCESSOR_MAP.get(version);
if (processor == null) {
throw new RuntimeException("Unknown version: " + version);
}
return processor;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
PROCESSOR_MAP.put("4.x", applicationContext.getBean(Version4ClusterMetaDataProcessor.class));
PROCESSOR_MAP.put("5.x", applicationContext.getBean(Version5ClusterMetaDataProcessor.class));
}
}
3、聚合策略类实现方式二
这种方式,实现的核心是自动装配,当 Spring 处理 @Bean 方法的参数时,若参数类型为 List<T>,容器会自动扫描所有类型是 T 或其子类的Bean,所有符合条件的 Bean 会被收集到 List 中,注入顺序与 Bean 的定义顺序一致(可通过 @Order 注解或配置文件调整),以后要新增6.x的处理器逻辑,只需新增实现 ClusterMetaDataProcessor 的 Bean,无需修改现有的代码,符合开闭原则
@Configuration
public class MetaDataProcessorFactory {
@Bean(name = "clusterMetaDataProcessorMap")
public Map<String, ClusterMetaDataProcessor> clusterMetaDataProcessorMap(List<ClusterMetaDataProcessor> processorList) {
Map<String, ClusterMetaDataProcessor> processorMap = new HashMap<>();
for (ClusterMetaDataProcessor processor : processorList) {
if (processorMap.put(processor.getVersion(), processor) != null) {
throw new IllegalStateException("Duplicate key for cluster metadata processor: " + processor.getVersion());
}
}
return processorMap;
}
}
在Service层代码中注入这个Map,使用@Qualifier指定前面定义时起的Bean的名字即可:
@Service
public class ServiceA {
private final Map<String, ClusterMetaDataProcessor> clusterMetaDataProcessorMap;
public ServiceA(@Qualifier("clusterMetaDataProcessorMap") Map<String, ClusterMetaDataProcessor> clusterMetaDataProcessorMap) {
this.clusterMetaDataProcessorMap = clusterMetaDataProcessorMap;
}
}
4、对比
特性 | @Bean + List<T>方案 | 手动注册方案(ApplicationContextAware) |
---|---|---|
扩展性 | 支持动态新增处理器版本 | 需手动修改代码注册新版本 |
代码简洁性 | 更简洁,无需实现接口 | 代码冗长,需手动管理版本号 |
5、补充:ApplicationContextAware接口
实现ApplicationContextAware接口,重写setApplicationContext方法,setApplicationContext方法的执行时机:
- Spring 容器首先会根据配置(XML/注解)实例化 Bean 对象
- 然后完成该 Bean 的属性注入(例如通过 @Autowired 或 XML 的 <property> 标签注入的其他 Bean)
- 此时,如果该 Bean 实现了 ApplicationContextAware 接口,容器就会调用 setApplicationContext 方法
- 最后再是@PostConstruct、自定义的 init-method等初始化Bean的操作
简单说就是:
1. 实例化 Bean 对象
2. 执行依赖注入(设置字段值)
3. 调用 `setApplicationContext` (如果 Bean 实现 ApplicationContextAware)
4. 执行初始化回调(如 @PostConstruct / init-method)
5. Bean 可用(被其他 Bean 引用)
举个例子:
@Component
public class MyBean implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
// 此时可以立即使用 context 获取其他 Bean
MyService service = context.getBean(MyService.class);
}
}
当 Spring 容器启动时,MyBean 会被实例化 → 注入依赖 → 调用 setApplicationContext → 最后执行初始化方法