需求: 这里虚拟一个业务需求,让大家容易理解。假设有一个订单系统,里面的一个功能是根据订单的不同类型作出不同的处理。
1. 常规代码实现
1.1 实体类
import lombok.Data; import java.math.BigDecimal; @Data public class OrderDTO { private String code; private BigDecimal price; /** * 订单类型 * 1: 普通订单 * 2: 团购订单 * 3: 促销订单 */ private String type; }
1.2 接口类
import qinfeng.zheng.strategy.dto.OrderDTO; public interface IOrderService { /** * 根据订单类型做出相应的处理 * * @param orderDTO * @return */ String handle(OrderDTO orderDTO); }
1.3 接口实现
import org.springframework.stereotype.Service; import qinfeng.zheng.strategy.dto.OrderDTO; import qinfeng.zheng.strategy.service.IOrderService; @Service public class V1OrderServiceImpl implements IOrderService { @Override public String handle(OrderDTO orderDTO) { String type = orderDTO.getType(); if (type.equals("1")) { return "处理普通订单成功"; } if (type.equals("2")) { return "处理团购订单成功"; } if (type.equals("3")) { return "处理促销订单成功"; } return "订单类型不存在"; } }
1.4 结论
不用说, 这代码 很low.
2. 使用策略模式实现此功能
策略模式的关键就是一个抽象处理类,配上一个持有这个抽象处理类实例的context. 下面是代码的具体的实现
2.1 抽象类
import qinfeng.zheng.strategy.dto.OrderDTO; public abstract class AbsHandler { abstract public String handle(OrderDTO orderDTO); }
2.2 具体实现类
import org.springframework.stereotype.Component; import qinfeng.zheng.strategy.anno.HandlerType; import qinfeng.zheng.strategy.dto.OrderDTO; @Component @HandlerType("1") public class NormalHandler extends AbsHandler { @Override public String handle(OrderDTO orderDTO) { return "处理普通订单"; } }
import org.springframework.stereotype.Component; import qinfeng.zheng.strategy.anno.HandlerType; import qinfeng.zheng.strategy.dto.OrderDTO; @Component @HandlerType("2") public class GroupHandler extends AbsHandler { @Override public String handle(OrderDTO orderDTO) { return "处理团购订单"; } }
import org.springframework.stereotype.Component; import qinfeng.zheng.strategy.anno.HandlerType; import qinfeng.zheng.strategy.dto.OrderDTO; @Component @HandlerType("3") public class PromotionHandler extends AbsHandler { @Override public String handle(OrderDTO orderDTO) { return "处理促销订单"; } }
2.3 context类
public class HandlerContext { private Map<String, Class> handlerMap; public HandlerContext(Map<String, Class> handlerMap) { this.handlerMap = handlerMap; } public AbsHandler getInstance(String type) throws Exception { Class aClass = handlerMap.get(type); if (aClass == null) { throw new IllegalArgumentException("not found handler type :" + type); } return (AbsHandler) aClass.newInstance(); } }
2.3 自定义注解类
import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface HandlerType { String value(); }
2.4 自定义BeanFactory后置处理类,将HandlerContext注册到spring容器中
import cn.hutool.core.lang.ClassScanner; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; import qinfeng.zheng.strategy.anno.HandlerType; import qinfeng.zheng.strategy.context.HandlerContext; import java.util.HashMap; import java.util.Map; @Component public class HandlerProcessor implements BeanFactoryPostProcessor { private static final String HANDLE_PACKAGE = "qinfeng.zheng.strategy"; /** * 扫描@HandlerType注解,初始化HandlerContext, 并将其注册到spring容器中 * @param beanFactory bean工厂呀...... * @throws BeansException */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Map<String, Class> handlerMap = new HashMap<>(); ClassScanner.scanPackageByAnnotation(HANDLE_PACKAGE,HandlerType.class).forEach(aClass -> { String value = aClass.getAnnotation(HandlerType.class).value(); handlerMap.put(value, aClass); }); // 初始化HandlerContext HandlerContext handlerContext = new HandlerContext(handlerMap); // 手动将HandlerContext注册到spring容器中 beanFactory.registerSingleton(HandlerContext.class.getName(), handlerContext); } }
2.4 修改IOrderService接口实现
@Service public class V2OrderServiceImpl implements IOrderService { @Autowired private HandlerContext handlerContext; @Transactional @Override public String handle(OrderDTO orderDTO) throws Exception { AbsHandler handler = handlerContext.getInstance(orderDTO.getType()); return handler.handle(orderDTO); } }
2.5 写个controller测试一下
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import qinfeng.zheng.strategy.dto.OrderDTO; import qinfeng.zheng.strategy.service.IOrderService; @RestController public class OrderController { /** * @Autowired自定根据 v2OrderServiceImpl 名称 适配 V2OrderServiceImpl这个实现类 */ @Autowired private IOrderService v2OrderServiceImpl; @GetMapping("/handle") public String handler(OrderDTO orderDTO) throws Exception { return v2OrderServiceImpl.handle(orderDTO); } }
好, 代码写完了,使用策略模式之后,代码的扩展性确实增强,若订单类型发生改变,只需添加AbsHandler的实现类即可,其它都不需要变。
但是,我觉得这代码并不好呀, 这样操作,实现策略模式的代码太多了。 其二,我使用了 aClass.newInstance()反射方式创建一个具体的Handler对象,这儿并没有使用spring的代理功能,所以如果在AbsHandler#handle方法上添加事务,是不会生效的,只能在V2OrderServiceImpl#handle方法添加事务。
不过这个示例也让我真实体验了一把 BeanFactoryPostProcessor 这个接口的功能, 以后如果有需要手动注册bean的场景,可以考虑使用这个接口,但是这个接口的主要功能并不注册bean, 而是修改管理bean工厂内所有的beandefinition(未实例化)数据,并且随心所欲的修改属性。
此外,ClassScanner这个类使用hutool类库。
以上示例代码大多来自“java那些事儿”