ObjectProvider的使用教程 简介: 在spring的传统开发中,我们拿bean对象,通常通过beanFactory接口里面的getBean方法获得,但是在spring4.3版本开始,为了进一步规范bean的管理机制,提供了ObjectProvider操作接口。个人感觉主要用法可以用来获得一个类型的多个对象,替代@Autowired注入一个接口,并能够获取Bean实例数组,或者转为集合类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public interface ObjectProvider <T> extends ObjectFactory <T>, Iterable<T> { T getObject (Object... args) throws BeansException; @Nullable T getIfAvailable () throws BeansException; @Nullable T getIfUnique () throws BeansException; default Stream<T> orderedStream () { throw new UnsupportedOperationException ("Ordered element access not supported" ); } }
从策略模式展开 策略模式(Strategy Pattern) 定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如在做制单付款操作时,对付款单有一些校验,比如校验金额和账号余额,账号是否是公司黑名单的。
何时使用策略模式 阿里开发规约-编程规约-控制语句-第六条 :超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现。相信大家都见过这种代码:
1 2 3 4 5 6 7 8 9 10 if (conditionA) { 逻辑1 } else if (conditionB) { 逻辑2 } else if (conditionC) { 逻辑3 } else { 逻辑4 }
这种代码虽然写起来简单,但是很明显违反了面向对象的 2 个基本原则:
单一职责原则(一个类应该只有一个发生变化的原因):因为之后修改任何一个逻辑,当前类都会被修改
开闭原则(对扩展开放,对修改关闭):如果此时需要添加(删除)某个逻辑,那么不可避免的要修改原来的代码
因为违反了以上两个原则,尤其是当 if-else 块中的代码量比较大时,后续代码的扩展和维护就会逐渐变得非常困难且容易出错,使用卫语句也同样避免不了以上两个问题。因此根据我的经验,得出一个我个人认为比较好的实践:
if-else 不超过 2 层,块中代码 1~5 行,直接写到块中,否则封装为方法
if-else 超过 2 层,但块中的代码不超过 3 行,尽量使用卫语句
if-else 超过 2 层,且块中代码超过 3 行,尽量使用策略模式
实战 需求背景,对于页面的付款单,在后台可以开启各种类型的校验,如账号余额校验,黑名单校验。
1 定义策略接口 此接口一般做两件事 1 获取类型,用于后期定位具体的处理类 2 具体的处理方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface VerifyStrategy { String getVerifyType () ; void verify (PaymentApplicationEntity paymentApplicationEntity) ; }
2 相关实现类 黑名单校验,对付款账号进行校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Component public class BlackVerifyHandler implements VerifyStrategy { @Override public String getVerifyType () { return VerifyTypeEnum.BLACK_TYPE.getType(); } @Override public void verify (PaymentApplicationEntity paymentApplicationEntity) { List<String> blackPayerAccountNoList = Arrays.asList("932033" , "334422" ); if (blackPayerAccountNoList.contains(paymentApplicationEntity.getPayerAccountNo())){ throw new RuntimeException ("付款账户不可用" ); } } }
账号余额校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Component public class AmountVerifyHandler implements VerifyStrategy { @Override public String getVerifyType () { return VerifyTypeEnum.AMOUNT_TYPE.getType(); } @Override public void verify (PaymentApplicationEntity paymentApplicationEntity) { BigDecimal balanceAmount = new BigDecimal ("500" ); if (balanceAmount.compareTo(paymentApplicationEntity.getAmount()) < 0 ){ throw new RuntimeException ("余额不足" ); } } }
3 相关的对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class VerifyDto { private String verifyType; private PaymentApplicationEntity paymentApplicationEntity; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Getter public enum VerifyTypeEnum { BLACK_TYPE("black" , "黑名单校验" ), AMOUNT_TYPE("amount" , "金额校验" ); private final String type; private final String desc; VerifyTypeEnum(String type, String desc) { this .type = type; this .desc = desc; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Data public class PaymentApplicationEntity { private String paymentApplicationNo; private BigDecimal amount; private String payeeAccountNo; private String payerAccountNo; private String verifyType; }
工厂类生产不同校验器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Component public class VerifyHandlerFactory implements InitializingBean , ApplicationContextAware { public static Map<String, VerifyStrategy> VERIFY_HANDLER_MAP = new HashMap <>(); private ApplicationContext applicationContext; @Override public void afterPropertiesSet () throws Exception { applicationContext.getBeansOfType(VerifyStrategy.class).values() .forEach(verifyStrategy -> VERIFY_HANDLER_MAP.put(verifyStrategy.getVerifyType(), verifyStrategy)); } public VerifyStrategy getVerifyHandler (String verifyType) { VerifyStrategy verifyStrategy = VERIFY_HANDLER_MAP.get(verifyType); if (Objects.isNull(verifyStrategy)){ throw new RuntimeException ("未找到对应的校验类型" ); } return verifyStrategy; } @Override public void setApplicationContext (@NonNull ApplicationContext applicationContext) throws BeansException { this .applicationContext = applicationContext; } }
付款单service类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface IPaymentApplicationService { void submitPaymentApplication (PaymentApplicationEntity paymentApplicationEntity) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Service public class PaymentApplicationServiceImpl implements IPaymentApplicationService { @Resource private VerifyHandlerFactory verifyHandlerFactory; @Override public void submitPaymentApplication (PaymentApplicationEntity paymentApplicationEntity) { verifyHandlerFactory.getVerifyHandler(paymentApplicationEntity.getVerifyType()) .verify(paymentApplicationEntity); } }
controller和http请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @RestController @RequestMapping("/paymentApplication") public class PaymentApplicationController { @Resource private IPaymentApplicationService paymentApplicationService; @PostMapping("/submit") public String submitPaymentApplication (@RequestBody PaymentApplicationEntity paymentApplicationEntity) { paymentApplicationService.submitPaymentApplication(paymentApplicationEntity); return "success" ; } }
1 2 3 4 5 6 7 8 9 POST http://localhost:8081/paymentApplication/submit Content-Type : application/json{ "amount": "1000", "payerAccountNo": "123456789", "payeeAccountNo": "987654321", "verifyType":"amount" }
之前是利用VerifyHandlerFactory类中的applicationContext.getBeansOfType(VerifyStrategy.class),获取所有的VerifyStrategy实现。
现在利用ObjectProvider修改VerifyHandlerFactory类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component public class VerifyHandlerFactory2 { public static Map<String, VerifyStrategy> VERIFY_HANDLER_MAP = new HashMap <>(); VerifyHandlerFactory2(ObjectProvider<VerifyStrategy> objectProvider){ objectProvider.orderedStream().forEach(verifyStrategy -> VERIFY_HANDLER_MAP.put(verifyStrategy.getVerifyType(), verifyStrategy)); } public VerifyStrategy getVerifyHandler (String verifyType) { VerifyStrategy verifyStrategy = VERIFY_HANDLER_MAP.get(verifyType); if (verifyStrategy == null ){ throw new RuntimeException ("未找到对应的校验类型" ); } return verifyStrategy; } }