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
相关产品推荐
相关产品推荐

