浅谈Spring Boot中如何干掉if else的方法
前言
看到crossoverJie的文章《利用策略模式优化过多ifelse代码》后受到启发,可以利用策略模式简化过多的ifelse代码,文章中提到可以通过扫描实现处理器的自注册,我在这里介绍在SpringBoot框架中的实现方法。
需求
这里虚拟一个业务需求,让大家容易理解。假设有一个订单系统,里面的一个功能是根据订单的不同类型作出不同的处理。
订单实体:
publicclassOrderDTO{
privateStringcode;
privateBigDecimalprice;
/**
*订单类型
*1:普通订单;
*2:团购订单;
*3:促销订单;
*/
privateStringtype;
//...省略get/set...
}
service接口:
publicinterfaceIOrderService{
/**
*根据订单的不同类型作出不同的处理
*
*@paramdto订单实体
*@return为了简单,返回字符串
*/
Stringhandle(OrderDTOdto);
}
传统实现
根据订单类型写一堆的ifelse:
publicclassOrderServiceImplimplementsIOrderService{
@Override
publicStringhandle(OrderDTOdto){
Stringtype=dto.getType();
if("1".equals(type)){
return"处理普通订单";
}elseif("2".equals(type)){
return"处理团购订单";
}elseif("3".equals(type)){
return"处理促销订单";
}
returnnull;
}
}
策略模式实现
利用策略模式,只需要两行即可实现业务逻辑:
@Service
publicclassOrderServiceV2ImplimplementsIOrderService{
@Autowired
privateHandlerContexthandlerContext;
@Override
publicStringhandle(OrderDTOdto){
AbstractHandlerhandler=handlerContext.getInstance(dto.getType());
returnhandler.handle(dto);
}
}
可以看到上面的方法中注入了HandlerContext,这是一个处理器上下文,用来保存不同的业务处理器,具体在下文会讲解。我们从中获取一个抽象的处理器AbstractHandler,调用其方法实现业务逻辑。
现在可以了解到,我们主要的业务逻辑是在处理器中实现的,因此有多少个订单类型,就对应有多少个处理器。以后需求变化,增加了订单类型,只需要添加相应的处理器就可以,上述OrderServiceV2Impl完全不需改动。
我们先看看业务处理器的写法:
@Component
@HandlerType("1")
publicclassNormalHandlerextendsAbstractHandler{
@Override
publicStringhandle(OrderDTOdto){
return"处理普通订单";
}
}
@Component
@HandlerType("2")
publicclassGroupHandlerextendsAbstractHandler{
@Override
publicStringhandle(OrderDTOdto){
return"处理团购订单";
}
}
@Component
@HandlerType("3")
publicclassPromotionHandlerextendsAbstractHandler{
@Override
publicStringhandle(OrderDTOdto){
return"处理促销订单";
}
}
首先每个处理器都必须添加到spring容器中,因此需要加上@Component注解,其次需要加上一个自定义注解@HandlerType,用于标识该处理器对应哪个订单类型,最后就是继承AbstractHandler,实现自己的业务逻辑。
自定义注解@HandlerType:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public@interfaceHandlerType{
Stringvalue();
}
抽象处理器AbstractHandler:
publicabstractclassAbstractHandler{
abstractpublicStringhandle(OrderDTOdto);
}
自定义注解和抽象处理器都很简单,那么如何将处理器注册到spring容器中呢?
具体思路是:
- 扫描指定包中标有@HandlerType的类;
- 将注解中的类型值作为key,对应的类作为value,保存在Map中;
- 以上面的map作为构造函数参数,初始化HandlerContext,将其注册到spring容器中;
我们将核心的功能封装在HandlerProcessor类中,完成上面的功能。
HandlerProcessor:
@Component
@SuppressWarnings("unchecked")
publicclassHandlerProcessorimplementsBeanFactoryPostProcessor{
privatestaticfinalStringHANDLER_PACKAGE="com.cipher.handler_demo.handler.biz";
/**
*扫描@HandlerType,初始化HandlerContext,将其注册到spring容器
*
*@parambeanFactorybean工厂
*@seeHandlerType
*@seeHandlerContext
*/
@Override
publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{
MaphandlerMap=Maps.newHashMapWithExpectedSize(3);
ClassScaner.scan(HANDLER_PACKAGE,HandlerType.class).forEach(clazz->{
//获取注解中的类型值
Stringtype=clazz.getAnnotation(HandlerType.class).value();
//将注解中的类型值作为key,对应的类作为value,保存在Map中
handlerMap.put(type,clazz);
});
//初始化HandlerContext,将其注册到spring容器中
HandlerContextcontext=newHandlerContext(handlerMap);
beanFactory.registerSingleton(HandlerContext.class.getName(),context);
}
}
ClassScaner:扫描工具类源码
HandlerProcessor需要实现BeanFactoryPostProcessor,在spring处理bean前,将自定义的bean注册到容器中。
核心工作已经完成,现在看看HandlerContext如何获取对应的处理器:
HandlerContext:
publicclassHandlerContext{
privateMaphandlerMap;
publicHandlerContext(MaphandlerMap){
this.handlerMap=handlerMap;
}
publicAbstractHandlergetInstance(Stringtype){
Classclazz=handlerMap.get(type);
if(clazz==null){
thrownewIllegalArgumentException("notfoundhandlerfortype:"+type);
}
return(AbstractHandler)BeanTool.getBean(clazz);
}
}
BeanTool:获取bean工具类
#getInstance方法根据类型获取对应的class,然后根据class类型获取注册到spring中的bean。
最后请注意一点,HandlerProcessor和BeanTool必须能被扫描到,或者通过@Bean的方式显式的注册,才能在项目启动时发挥作用。
总结
利用策略模式可以简化繁杂的ifelse代码,方便维护,而利用自定义注解和自注册的方式,可以方便应对需求的变更。本文只是提供一个大致的思路,还有很多细节可以灵活变化,例如使用枚举类型、或者静态常量,作为订单的类型,相信你能想到更多更好的方法。
全部示例代码请查看:handler_demo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。