You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CDI 1.2如何在指定JNDI资源存在时条件生成或覆盖Bean

解决方案

核心问题原因

@Resource 注解的资源注入由Java EE容器在CDI Bean初始化前置阶段处理,一旦指定JNDI名称不存在,容器会直接抛出启动异常,不会进入后续生产者逻辑,所以原方案无法达到条件降级的效果。


方案一:生产者内手动JNDI查找(最简实现)

无需额外扩展,仅修改生产者逻辑即可实现需求:

  • 放弃@Resource注入,在生产者方法内手动执行JNDI查找,自行捕获资源不存在的异常
  • 给JNDI实现的生产者设置更高优先级,确保存在时优先于默认内存实现注入
  • 默认内存实现加@Default注解作为兜底实现

调整后的代码

基础实现类

interface MyService{
  void doSomething();
}

// 默认内存实现,加@Default标识为兜底实现
@ApplicationScoped
@Default
public class InMemory implements MyService{
   Map<String, String> persistence = new HashMap<>();

   public void doSomething(){ persistence.put("now", new Date());}
}

// JNDI实现保持不变,修正原代码语法错误
public class JndiPersistence implements MyService{
   Cache<String, String> persistence;

   public JndiPersistence(Cache persistence){ this.persistence = persistence;}
   public void doSomething(){ persistence.put("now", new Date());}
}

// 客户端类无需修改
public class DataManager(){
   private MyService myservice;

   @Inject
   public DataManager( MyService myservice ){ this.myservice = myservice; }

   public void manageMyData(){
     myservice.doSomething();
   }
}

调整后的生产者代码

import javax.enterprise.inject.Alternative;
import javax.annotation.Priority;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JndiProducer{

   @Produces
   @ApplicationScoped
   @Alternative // 标识为可选实现
   @Priority(2000) // 数值越大优先级越高,默认普通Bean优先级为1000,保证本实现优先被选择
   public MyService jndiBean(){
      try {
          // 手动查找JNDI资源
          Cache cache = (Cache) new InitialContext().lookup("java:comp/env/persistenceCache");
          return new JndiPersistence(cache);
      } catch (NamingException e) {
          // JNDI资源不存在,抛出运行时异常,CDI会自动忽略该生产者,降级使用默认内存实现
          throw new RuntimeException("JNDI cache not found, fallback to in-memory implementation", e);
      }
   }
}

方案二:CDI Portable Extension(更规范的条件注册)

如果需要绑定多个Bean到同一个存在性条件,或者需要减少运行时判断开销,可以通过CDI扩展实现启动阶段条件注册:

  1. 自定义CDI扩展,在容器启动阶段执行一次JNDI查找
  2. 若JNDI资源不存在,直接禁用JndiPersistence的Bean注册,容器会自动使用默认内存实现

扩展代码实现

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JndiResourceCheckExtension implements Extension {

    private boolean jndiCacheExists = false;

    // 容器启动阶段仅执行一次JNDI存在性检查
    public JndiResourceCheckExtension() {
        try {
            new InitialContext().lookup("java:comp/env/persistenceCache");
            jndiCacheExists = true;
        } catch (NamingException e) {
            jndiCacheExists = false;
        }
    }

    // JNDI不存在时直接注销JndiPersistence的Bean定义
    public <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
        if (pat.getAnnotatedType().getJavaClass().equals(JndiPersistence.class) && !jndiCacheExists) {
            pat.veto();
        }
    }
}

启用扩展

在项目META-INF/services/javax.enterprise.inject.spi.Extension文件中添加扩展类的全限定名即可生效,之后给JndiPersistence类添加@ApplicationScoped和@Priority(2000)注解,不需要额外生产者即可实现需求。


方案对比

  • 方案一代码量少,无需额外配置,适合单Bean条件注册场景
  • 方案二逻辑更清晰,仅启动时执行一次资源检查,适合多Bean绑定同一条件的场景

内容的提问来源于stack exchange,提问作者Eric B.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:24:03