能否从Java枚举类中访问Spring管理的ServiceConfig组件?
问题解答
枚举是JVM类加载阶段就初始化完成的静态单例结构,初始化时机早于Spring容器Bean的创建流程,无法直接在枚举内部自动注入并访问Spring管理的ServiceConfig实例,但可以通过无侵入的绑定设计实现用枚举替换原有硬编码Map的需求,以下是可直接落地的实现方案:
推荐实现方案(无状态、无Spring依赖侵入枚举)
这个方案不会让枚举持有Spring Bean的全局状态,避免初始化顺序冲突、测试环境污染问题,只把枚举作为「实体类-存储过程获取方法」的绑定元数据容器,具体逻辑如下:
- 定义枚举存储绑定关系,通过方法引用关联ServiceConfig中对应的存储过程名获取方法
import java.util.function.Function; public enum RecordProcBinding { // 新增实体类绑定只需要在这里加一行枚举值即可 MAIN_DEP(MainDepRecord.class, ServiceConfig::mainDepProc), ; private final Class<? extends IRecord> recordClass; private final Function<ServiceConfig, String> procNameGetter; RecordProcBinding(Class<? extends IRecord> recordClass, Function<ServiceConfig, String> procNameGetter) { this.recordClass = recordClass; this.procNameGetter = procNameGetter; } public Class<? extends IRecord> getRecordClass() { return recordClass; } public Function<ServiceConfig, String> getProcNameGetter() { return procNameGetter; } }
- 修改BundleMaker的构造逻辑,遍历枚举值自动填充原有procNameSupplier映射,不需要手动逐个put
import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.StoredProcedureQuery; import org.springframework.stereotype.Component; import static javax.persistence.ParameterMode.REF_CURSOR; @Component public class BundleMaker { @PersistenceContext private final EntityManager entityManager; private final Map<Class<? extends IRecord>, Supplier<String>> procNameSupplier = new HashMap<>(); public BundleMaker(EntityManager entityManager, ServiceConfig config) { this.entityManager = entityManager; // 扫描枚举自动构建绑定关系 Arrays.stream(RecordProcBinding.values()) .forEach(binding -> procNameSupplier.put( binding.getRecordClass(), () -> binding.getProcNameGetter().apply(config) )); } public <T extends IRecord> Bundle bundle(Class<T> cl) { StoredProcedureQuery query = entityManager.createStoredProcedureQuery(procNameSupplier.get(cl).get(), cl) .registerStoredProcedureParameter(1, cl, REF_CURSOR); List<T> resultList = query.getResultList(); List<Resource> resources = resultList.stream() .map(T::toResource) .collect(Collectors.toList()); return new Bundle(resources); } }
备选方案(枚举持有ServiceConfig实例,不推荐)
如果一定要在枚举内部直接获取存储过程名,可以通过静态注入的方式给枚举传入ServiceConfig实例,但这种方案会给枚举引入全局可变状态,跨上下文单元测试、多Spring容器场景下容易出现状态污染问题:
- 给枚举增加静态的ServiceConfig持有字段和赋值方法
public enum RecordProcBinding { MAIN_DEP(MainDepRecord.class, ServiceConfig::mainDepProc); private static ServiceConfig config; private final Class<? extends IRecord> recordClass; private final Function<ServiceConfig, String> procNameGetter; RecordProcBinding(Class<? extends IRecord> recordClass, Function<ServiceConfig, String> procNameGetter) { this.recordClass = recordClass; this.procNameGetter = procNameGetter; } public static void setConfig(ServiceConfig config) { RecordProcBinding.config = config; } public Class<? extends IRecord> getRecordClass() { return recordClass; } public String getProcName() { // 注意如果Spring初始化完成前调用该方法会抛空指针 return procNameGetter.apply(config); } }
- 新增Spring配置类,在Bean初始化完成后给枚举注入ServiceConfig实例
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @Configuration public class EnumBindingConfig { @Autowired public void initEnumConfig(ServiceConfig config) { RecordProcBinding.setConfig(config); } }
注意:优先选择第一种实现方案,完全满足你把绑定关系从BundleMaker中抽离收敛到枚举的需求,同时没有额外的初始化风险,后续新增绑定关系只需要修改枚举类,不需要改动BundleMaker的业务逻辑。
内容的提问来源于stack exchange,提问作者00vk
相关产品推荐
相关产品推荐

