JavaEE策略模式注入Lambda时出现序列化错误求助
JavaEE策略模式序列化问题解决方案及替代实现方式
一、Lambda序列化问题的解决方案
你遇到的问题核心是:Java原生Function接口未实现Serializable,因此基于它生成的Lambda不会自动具备序列化能力,而@ViewScoped Bean在会话钝化/激活时需要序列化所有成员变量。以下是两种可行的修复方案:
1. 自定义可序列化的函数式接口
创建一个继承Function并实现Serializable的自定义接口,以此作为策略选择器的类型:
@FunctionalInterface public interface SerializableFunction<T, R> extends Function<T, R>, Serializable { // 无需额外方法,继承自Function的apply方法即可 }
然后在SalesCalculatorStrategyProducer中返回该类型的Lambda:
public class SalesCalculatorStrategyProducer { public SerializableFunction<String, SalesCalculatorService> getStrategySelector() { return type -> { switch(type) { case "MONTHLY": return new MonthlySalesCalculatorService(); // 添加其他策略分支 default: throw new IllegalArgumentException("Unknown strategy type: " + type); } }; } }
注意:Lambda中捕获的所有变量(如果有的话)也必须实现Serializable,否则仍会出现序列化错误。
2. 替换Lambda为可序列化的匿名内部类
如果不想自定义接口,可以直接使用显式实现Serializable的匿名内部类:
public class SalesCalculatorStrategyProducer { public Function<String, SalesCalculatorService> getStrategySelector() { return new Function<String, SalesCalculatorService>() implements Serializable { @Override public SalesCalculatorService apply(String type) { switch(type) { case "MONTHLY": return new MonthlySalesCalculatorService(); // 其他分支 default: throw new IllegalArgumentException("Unknown strategy type: " + type); } } }; } }
这种方式虽然代码更繁琐,但能确保实例具备序列化能力。
二、策略模式的其他运行时切换方式
除了Lambda选择器,还有几种更适配JavaEE/CDI生态的策略切换方案:
1. CDI限定符+Instance选择器
利用CDI的限定符(Qualifier)标记不同策略实现,通过Instance<T>动态获取实例:
步骤1:定义带参数的限定符
@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface SalesPeriod { Period value(); enum Period { MONTHLY, QUARTERLY, YEARLY } }
步骤2:给策略实现类添加限定符
@SalesPeriod(Period.MONTHLY) public class MonthlySalesCalculatorService implements SalesCalculatorService, Serializable { // 业务实现 }
步骤3:在ViewScoped Bean中注入并选择策略
@ViewScoped public class BarChartProductSales implements Serializable { @Inject private Instance<SalesCalculatorService> salesCalculatorInstances; public SalesCalculatorService getCurrentStrategy(String periodStr) { SalesPeriod.Period period = SalesPeriod.Period.valueOf(periodStr); // 创建限定符实例(需实现AnnotationLiteral) SalesPeriod qualifier = new SalesPeriodLiteral(period); return salesCalculatorInstances.select(qualifier).get(); } // 实现限定符的Literal类 private static class SalesPeriodLiteral extends AnnotationLiteral<SalesPeriod> implements SalesPeriod { private final Period period; public SalesPeriodLiteral(Period period) { this.period = period; } @Override public Period value() { return period; } } }
2. 可序列化的策略工厂类
创建一个由CDI管理的工厂类,维护策略类型与实例供应商的映射:
@ApplicationScoped public class SalesCalculatorFactory implements Serializable { private final Map<String, Supplier<SalesCalculatorService>> strategyMap = new HashMap<>(); @Inject private Instance<MonthlySalesCalculatorService> monthlyInstance; @Inject private Instance<QuarterlySalesCalculatorService> quarterlyInstance; @PostConstruct public void initStrategies() { strategyMap.put("MONTHLY", monthlyInstance::get); strategyMap.put("QUARTERLY", quarterlyInstance::get); } public SalesCalculatorService getStrategy(String type) { Supplier<SalesCalculatorService> supplier = strategyMap.get(type); if (supplier == null) { throw new IllegalArgumentException("Unknown strategy type: " + type); } return supplier.get(); } }
在ViewScoped Bean中注入该工厂,直接调用getStrategy方法获取实例即可。
3. 枚举策略容器
用枚举封装所有策略,利用枚举天生可序列化的特性实现切换:
public enum SalesCalculatorStrategy implements Serializable { MONTHLY(MonthlySalesCalculatorService::new), QUARTERLY(QuarterlySalesCalculatorService::new); private final Supplier<SalesCalculatorService> instanceSupplier; SalesCalculatorStrategy(Supplier<SalesCalculatorService> supplier) { this.instanceSupplier = supplier; } public SalesCalculatorService getInstance() { return instanceSupplier.get(); } }
使用时直接通过枚举值获取:
SalesCalculatorService strategy = SalesCalculatorStrategy.valueOf("MONTHLY").getInstance();
如果策略实例是CDI管理的Bean,可将Supplier替换为Instance<T>::get,确保实例由容器创建。
内容的提问来源于stack exchange,提问作者CodeFarmer94
相关产品推荐
相关产品推荐

