如何在CDI与Spring中注入带特定初始值的对象?
需求可行性分析与实现方案
你的需求完全可以通过依赖注入实现,核心思路是为不同场景提供不同的InjectedClass实例,并通过限定符或生产者Bean区分注入逻辑。结合你当前Spring+CDI混合的环境,以下是具体实现方案:
先修正代码中的基础问题
首先得把原代码里的几个错误改了,否则后续逻辑无法正常运行:
InjectedClass的getSomeAttribute()方法返回值类型写错了,应该是SomeEnum而非voidMiddleLayer的middleLayerMethod()是private,外部无法调用,需要改为publicInjectedClass同时加了@RequestScoped和@Scope("request"),属于重复注解,保留一个即可
修正后的基础代码:
@Service @RequestScoped public class InjectedClass { private SomeEnum someAttribute; public SomeEnum getSomeAttribute() { return someAttribute; } public void setSomeAttribute(SomeEnum someAttribute) { this.someAttribute = someAttribute; } } @Service public class MiddleLayer { private final InjectedClass injectedClass; @Inject public MiddleLayer(InjectedClass injectedClass) { this.injectedClass = injectedClass; } public MethodResult middleLayerMethod() { if (injectedClass.getSomeAttribute() == SomeEnum.Y) { // 处理请求场景逻辑 } else if (injectedClass.getSomeAttribute() == SomeEnum.X) { // 处理定时任务场景逻辑 } // 返回结果 } }
具体实现方案:用CDI限定符区分实例
这种方案最灵活,能精准控制不同场景下的注入实例:
1. 定义限定符注解
创建一个自定义限定符,标记定时任务专用的实例:
@Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) public @interface PeriodicInstance { }
2. 编写生产者Bean,生成定时任务专用的InjectedClass实例
专门为定时场景创建一个属性固定为X的InjectedClass单例:
@Component public class InjectedClassProducer { @Produces @PeriodicInstance @Singleton public InjectedClass createPeriodicInjectedClass() { InjectedClass instance = new InjectedClass(); instance.setSomeAttribute(SomeEnum.X); return instance; } }
3. 为MiddleLayer配置多场景注入
因为MiddleLayer在请求场景用请求作用域的InjectedClass,定时场景用专用实例,我们可以通过生产者Bean生成不同的MiddleLayer实例:
@Component public class MiddleLayerProducer { // 生成请求场景用的MiddleLayer,使用默认的请求作用域InjectedClass @Produces @RequestScoped public MiddleLayer createRequestMiddleLayer(InjectedClass injectedClass) { return new MiddleLayer(injectedClass); } // 生成定时任务用的MiddleLayer,使用带PeriodicInstance限定符的InjectedClass @Produces @PeriodicInstance @Singleton public MiddleLayer createPeriodicMiddleLayer(@PeriodicInstance InjectedClass injectedClass) { return new MiddleLayer(injectedClass); } }
4. 修改控制器和定时任务的注入逻辑
- 控制器保持默认注入,使用请求场景的
MiddleLayer:
public class SomeController { private final MiddleLayer middleLayer; @Inject public SomeController(MiddleLayer middleLayer) { this.middleLayer = middleLayer; } public void someMethod () { MethodResult result = middleLayer.middleLayerMethod(); // 后续逻辑不变 } }
- 定时任务通过限定符注入专用的
MiddleLayer:
@Component public class PeriodicActivity { private final MiddleLayer middleLayer; @Inject public PeriodicActivity(@PeriodicInstance MiddleLayer middleLayer) { this.middleLayer = middleLayer; } public void method () { MethodResult result = middleLayer.middleLayerMethod(); // 后续逻辑不变 } }
替代方案:拆分MiddleLayer为两个Bean
如果不想用生产者Bean,也可以直接创建两个MiddleLayer的变体,分别对应不同场景:
// 请求场景专用MiddleLayer @Service public class RequestMiddleLayer extends MiddleLayer { @Inject public RequestMiddleLayer(InjectedClass injectedClass) { super(injectedClass); } } // 定时任务专用MiddleLayer @Service public class PeriodicMiddleLayer extends MiddleLayer { @Inject public PeriodicMiddleLayer(@PeriodicInstance InjectedClass injectedClass) { super(injectedClass); } }
然后控制器注入RequestMiddleLayer,定时任务注入PeriodicMiddleLayer即可,这种方式更直观,适合逻辑差异较大的场景。
关键注意点
- 定时任务属于无请求上下文的场景,直接注入
@RequestScoped的InjectedClass会抛出作用域异常,必须为定时场景单独创建非请求作用域的实例 - 如果你的Spring版本较低,可能需要调整CDI注解的兼容性,确保
@Qualifier和@Produces能被Spring正确识别
内容的提问来源于stack exchange,提问作者Victoriia
相关产品推荐
相关产品推荐

