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

J6EE环境下CDI结合工厂方法的Voter逻辑优化设计咨询

CDI环境下Voter路由逻辑的最优实现方案(适配Java EE 6)

现有方案的核心问题

当前场景需要实现动态请求路由:无法预先确定入参对应的Voter实现类,需要由各Voter自行判断是否处理请求,同时要复用CDI的依赖注入能力,避免重量级逻辑重复执行带来的性能损耗。

初始设计缺陷

初始设计采用canVoteFor+vote双方法接口,通过CDI的Instance<Voter>遍历所有实现类路由:

public interface Voter {
    boolean canVoteFor(Object context);
    // 返回值为枚举,取值ABSTAIN/GRANTED/DENIED
    VoteResult vote(Object context);
}

public class VoterCounter {
    @Inject
    Instance<Voter> voters;

    public void count(Object context) {
        for (Voter voter : voters) {
            // 第一次执行重量级预处理
            if (voter.canVoteFor(context)) {
                // 第二次执行相同的重量级预处理,产生重复性能损耗
                voter.vote(context);
            }
        }
    }
}

该方案在不引入缓存的前提下,会重复执行相同的重量级预处理逻辑,带来不必要的性能开销。

备选设计缺陷

拆分VoterCreator+NewVoter的两层接口方案,虽然解决了重复执行问题,但需要手动实现上下文绑定的伪构造逻辑,接口冗余度高,没有复用CDI容器的实例管理能力,设计合理性不足。


推荐实现方案

方案1:单接口合并语义(最优,复杂度最低)

直接调整Voter接口契约,用ABSTAIN(弃权)语义天然表示当前Voter不处理该请求,合并判断和投票逻辑为单次方法调用,从根源上避免重复执行:

// 投票结果枚举
public enum VoteResult {
    ABSTAIN, GRANTED, DENIED
}

// 仅保留单方法的Voter接口
public interface Voter {
    VoteResult vote(Object context);
}

// 重量级Voter实现,直接由CDI容器管理
@ApplicationScoped
public class HeavyVoter implements Voter {
    @Inject
    SomeService someService1;
    @Inject
    SomeService someService2;

    @Override
    public VoteResult vote(Object context) {
        // 重量级预处理仅执行1次
        PreProcessResult processRes = heavyPreProcess1(context);
        // 预处理后直接判断是否支持当前请求
        if (!processRes.isSupported()) {
            return VoteResult.ABSTAIN;
        }
        // 支持则执行后续逻辑返回投票结果
        heavyPreProcess2(processRes);
        return processRes.isGranted() ? VoteResult.GRANTED : VoteResult.DENIED;
    }

    private PreProcessResult heavyPreProcess1(Object context) {
        // 调用注入的Service执行重量级逻辑
    }

    private void heavyPreProcess2(PreProcessResult res) {
        // 后续重量级处理逻辑
    }
}

// 计票器实现
@ApplicationScoped
public class VoterCounter {
    @Inject
    Instance<Voter> voters;

    public VoteResult countVotes(Object context) {
        int grantNum = 0, denyNum = 0;
        for (Voter voter : voters) {
            // 单次调用完成判断+投票,无重复逻辑
            VoteResult res = voter.vote(context);
            if (res == VoteResult.GRANTED) grantNum++;
            if (res == VoteResult.DENIED) denyNum++;
        }
        // 按业务规则返回最终结果,例如一票否决、多数通过等
        return denyNum > 0 ? VoteResult.DENIED :
               grantNum > 0 ? VoteResult.GRANTED : VoteResult.ABSTAIN;
    }
}

该方案的核心优势:

  • 性能最优:所有预处理逻辑仅执行一次,不需要额外缓存
  • 复杂度极低:仅保留一层接口,无额外工厂类、上下文绑定逻辑
  • 完全适配CDI:所有Voter实现直接由容器管理,依赖注入、生命周期完全由容器负责
  • 语义清晰:ABSTAIN本身就代表投票者不参与当前轮次,天然匹配"不处理请求"的业务含义,无概念冗余

方案2:原型实例方案(兼容需要拆分判断/投票逻辑的场景)

如果部分轻量Voter的支持性判断成本极低,确实需要拆分canVoteFor和vote方法,可以直接利用CDI的原型作用域能力,不需要自定义Creator工厂:

// Voter实现标记为原型作用域,每次获取返回新实例,无线程安全问题
@Dependent
public class HeavyVoter implements Voter {
    private Object context;
    private Boolean supportedCache;

    // 注入属性由CDI容器自动完成
    @Inject
    SomeService someService1;

    // 仅设置上下文,不需要手动处理依赖注入
    public void setContext(Object context) {
        this.context = context;
    }

    @Override
    public boolean canVoteFor() {
        if (supportedCache == null) {
            // 重量级预处理仅执行一次,结果缓存到实例中
            supportedCache = heavyPreProcess(context);
        }
        return supportedCache;
    }

    @Override
    public VoteResult vote() {
        if (!canVoteFor()) return VoteResult.ABSTAIN;
        // 执行后续投票逻辑
    }
}

// 计票器中直接通过CDI获取注入完成的实例
@ApplicationScoped
public class VoterCounter {
    @Inject
    Instance<Voter> voters;

    public VoteResult countVotes(Object context) {
        for (Voter voter : voters) {
            // 获取CDI创建的全新实例,所有依赖已经注入完成
            Voter instance = voters.get();
            instance.setContext(context);
            if (instance.canVoteFor()) {
                VoteResult res = instance.vote();
                // 计票逻辑
            }
            // 销毁原型实例避免内存泄漏
            voters.destroy(instance);
        }
    }
}

该方案比自定义VoterCreator方案简化60%以上的代码量,所有依赖注入逻辑由CDI容器完成,仅需要手动设置上下文参数即可。


内容的提问来源于stack exchange,提问作者marrom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 02:28:04