优雅地干掉Spring/SpringBoot中的大片 if else!

大片的if/else if/ 层层嵌套会使阅读代码的人很难理解业务逻辑, 同时可维护性很低

  • 模拟需求
    • 进出口枚举
    • 传统实现
    • 我的实现
      • 先展示最终实现代码
      • 自定义注解
      • 定义业务处理器超类:(可以根据自己业务进行调整)
      • 当前业务抽象类(主要为了处理参数,真实业务处理由实现类实现)
      • 真实业务实现类
        • 进口
        • 出口
      • SpringBeanUtil(获取Spring容器中Bean的工具类)
  • 以上, 有不懂得地方 ,或者有改进建议, 欢迎留言

模拟需求

新增集装箱,根据进出口类型,分别进行不同的操作. (这里我只比较进出口两个类型,实际代码中根据业务类型的不同可能存在多次判断, 会出现大批的if /else if/ else…)

进出口枚举

package com.howhan.cloud.e.common.enums;

public enum ImportExportTypeEnum {
    IMPORT(0, "进口"),
    EXPORT(1, "出口");

    private final Integer value;
    private final String desc;
    private final String name;

    private ImportExportTypeEnum(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
        this.name = this.name();
    }

    public Integer getValue() {
        return this.value;
    }

    public String getDesc() {
        return this.desc;
    }

    public String getName() {
        return this.name;
    }
}

传统实现

 public void newContainer(Long orderId,CustomerContainerVO vo){
      CustomerOrder order = customerOrderService.getById(orderId);
      if(ImportExportTypeEnum.EXPORT == order.getImportExportType()){//出口
          //do something

      }else{//进口
          //do something

      }
      //这里我只比较进出口两个类型,实际代码中根据业务类型的不同可能存在多次判断
      //会出现大批的if /else if/ else...
    
 }

我的实现

先展示最终实现代码

	@Override
    public SimpleTuber<Void> newContainer(Long orderId,CustomerContainerVO vo) {
        CustomerOrder order = customerOrderService.getById(orderId);
        Handler handler = SpringBeanUtil.getBean(
            NewContainerHandle.class,
            order.getImportExportType()
        );
        return  handler.handle(orderId,vo);

    }

自定义注解

package com.howhan.cloud.e.order.service.annotation;

import com.howhan.cloud.e.common.enums.ImportExportTypeEnum;

import java.lang.annotation.*;

/**
 * 新增箱
 * 进出口 处理句柄 handler标记注解
 * @author Kaizen
 * */
@Inherited //子类继承注解,如 CGLIB 生成带代理类 可以继承此注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NewContainerHandle {
    ImportExportTypeEnum value();
}

定义业务处理器超类:(可以根据自己业务进行调整)

package com.howhan.cloud.e.order.service.handler;

import wiki.kaizen.cloud.plugin.spring.SimpleTuber;

/**
 *处理器超类
 * */
public interface Handler {
    SimpleTuber<Void>  handle(Object... args);
}

当前业务抽象类(主要为了处理参数,真实业务处理由实现类实现)

package com.howhan.cloud.e.order.service.handler.container;

import com.howhan.cloud.e.order.service.handler.Handler;
import com.howhan.cloud.e.order.service.o.vo.CustomerContainerVO;
import lombok.extern.slf4j.Slf4j;
import wiki.kaizen.cloud.plugin.spring.SimpleTuber;

/**
 * 新增箱抽象处理类 
 * 对超类参数进一步处理
 * @author Kaizen 
 * */
@Slf4j
public abstract class AbstractNewContainerHandler implements Handler {

    /**
     * @param args 长度为2 第一个为Long orderId,第二个传参的vo CustomerContainerVO
     * */
    @Override
    public SimpleTuber<Void> handle(Object... args){
        if (args == null ||args.length !=2){
            log.error("Handler#handle(args...) 参数设置不正确 应为 handle(Long orderId,CustomerContainerVO vo)");
            return SimpleTuber.error("服务器发生异常");
        }
        Long orderId ;
        CustomerContainerVO vo;
        try{
            orderId =(Long) args[0];
            vo =(CustomerContainerVO)args[1];
        }catch (Exception ignore){
            log.error("Handler#handle(args...) 参数设置不正确 应为 handle(Long orderId,CustomerContainerVO vo)");
            return SimpleTuber.error("服务器发生异常");
        }
        return build(orderId,vo);
    }
    /**
     * 真实业务逻辑
     * */
    public abstract SimpleTuber<Void> build(Long orderId, CustomerContainerVO vo);


}

真实业务实现类

进口

package com.howhan.cloud.e.order.service.handler.container;

import com.howhan.cloud.e.common.enums.ImportExportTypeEnum;
import com.howhan.cloud.e.order.service.annotation.NewContainerHandle;
import com.howhan.cloud.e.order.service.o.vo.CustomerContainerVO;
import org.springframework.stereotype.Service;
import wiki.kaizen.cloud.plugin.spring.SimpleTuber;

import java.time.LocalDateTime;
/**
 * 新增 进口箱 处理
 * @author Kaizen
 * */
@Service
@NewContainerHandle(ImportExportTypeEnum.IMPORT)
public class NewImportContainerHadnler extends AbstractNewContainerHandler {

   @Override
   public SimpleTuber<Void> build(Long orderId, CustomerContainerVO vo){

       //专注进口业务处理 do something
       return SimpleTuber.OK();
   }

}

出口

package com.howhan.cloud.e.order.service.handler.container;

import com.howhan.cloud.e.common.enums.ImportExportTypeEnum;
import com.howhan.cloud.e.order.service.annotation.NewContainerHandle;
import com.howhan.cloud.e.order.service.o.vo.CustomerContainerVO;
import org.springframework.stereotype.Service;
import wiki.kaizen.cloud.plugin.spring.SimpleTuber;

/**
 * 新增 出口箱 处理
 * @author Kaizen
 * */
@Service
@NewContainerHandle(ImportExportTypeEnum.EXPORT)
public class NewExportContainerHandler extends AbstractNewContainerHandler {
   
   @Override
   public SimpleTuber<Void> build(Long orderId, CustomerContainerVO vo){

       //专注进口业务处理 do something
       return SimpleTuber.OK();
   }


}

SpringBeanUtil(获取Spring容器中Bean的工具类)

package wiki.kaizen.cloud.plugin.spring.scan.compent;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;


@Component
@Slf4j
public class SpringBeanUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        if (SpringBeanUtil.applicationContext == null) {
            SpringBeanUtil.applicationContext = applicationContext;
        }
    }

    public static <A extends Annotation> Map<String, Object> getBeansWithAnnotation(Class<A> annoClass){
        return applicationContext.getBeansWithAnnotation(annoClass);
    }
    /**
     * 根据注解及注解的值获取 对应的Bean
     * 要求 注解必须包含方法value 且为空参
     * */
    @SuppressWarnings("unchecked")
    public static < B,A extends Annotation, E> B getBean(Class<A> annoClass, E em){
        Objects.requireNonNull(annoClass,"注解的值不能为空");
        Objects.requireNonNull(em,"注解value对应的值不能为空");
        Map<String, Object> beans = getBeansWithAnnotation(annoClass);
        Method value;
        try {
            value = annoClass.getDeclaredMethod("value");
        } catch (NoSuchMethodException e) {
            log.error(annoClass.getSimpleName()+"注解,未设置value方法");
            return null;
        }
        value.setAccessible(true);
        Optional<B> any = beans.values().stream().filter(
            bean -> {
                A anno = Optional.ofNullable(
                    bean.getClass().getAnnotation(annoClass)
                ).orElse(
                    bean.getClass().getSuperclass().getAnnotation(annoClass)
                );
                if (anno == null){
                    log.error("未正确获取注解,请给注解设置@Inherited");
                    return false;
                }
                E e;
                try {
                    e = (E) value.invoke(anno);
                } catch (IllegalAccessException | InvocationTargetException ex) {
                    return false;
                }
                return em == e || em.equals(e);
            }
        ).map(bean -> (B) bean).findAny();
        return any.orElseGet(
            ()->{
                log.error("未能从Spring容器中获取到{}对应的Bean",em.getClass());
                return null;
            }
        );
    }
}

以上, 有不懂得地方 ,或者有改进建议, 欢迎留言

你可能感兴趣的:(Spring,springboot2.0)