一:解决的业务问题:
业务A的处理到一个节点,需要不同情况需要调用或回调不同的多个平级业务。一般情况下,我们会在业务A的主表等中有一个业务类型,通过业务类型判断应该调用或者回调目标业务。但是这种处理是一旦有新的业务需要业务线A去调用或回调其他业务线,不仅需要其他的业务线提供方法,还需要业务线A增加分支判断去调用,会造成业务A中的代码中有大量的跟本身业务无关的其他业务的服务层等的注入,这样就违背了职责单一原则,代码耦合性非常高,维护难度大。
二:解决方案
对于本文的业务问题,提供一种方案是单例模式+策略模式来处理。
单例模式:作用保证全局有唯一的一个工厂。
策略模式:即向工厂添加的不同的业务线的具体业务实现,通过key实现具体业务的调用
两种方式优略对比如下,单例模式+策略模式 的使用只要类型的静态类做好维护,对于可读性的影响基本没有。
三:代码实现
1,静态类
/**
* 册回调 指定类型的业务的类型指定 每个类型只能使用一次
**/
public class BussTypeConstant {
/**
* 业务类型
*/
public static final String BUSS_TYPE1="buss_type1";
}
2,处理类实体
@Schema(description = "业务回调实体类")
@Data
public class BussInfo implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description ="业务id")
private String bussId;
/**
* 这里列举的是返回给要调用的业务的结果 如果是其他处理可自行调整,比如用json
* REFUSE 任务结束 流程拒绝
* SUCCESS 任务结束 整个流程完成
*/
@Schema(description = "NO_PASS 不通过,SUCCESS 通过")
private BussStatus bussStatus;
}
3,枚举类
@Getter
public enum BussStatus {
NO_PASS("未通过"),
SUCCESS("通过");
private String name;
BussStatus(String name) {
this.name = name;
}
}
4,核心代码
//
public interface MyFunction<T, R> {
R apply(T t);
}
@Component
public class BussInfoHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(BussInfoHandler.class);
private final Map<String,List<MyFunction<BussInfo,Boolean>>> bussIdHandler;
public BussInfoHandler() {
this.bussIdHandler = new HashMap<>();
}
public void addBussHandler(String bussTp,MyFunction<BussInfo,Boolean> handler){
List<MyFunction<BussInfo,Boolean>> handlers = bussIdHandler.get(bussTp);
if(CollectionUtils.isEmpty(handlers)){
bussIdHandler.put(bussTp,new ArrayList<MyFunction<BussInfo,Boolean>>());
}
bussIdHandler.get(bussTp).add(handler);
}
public boolean handlerBuss(String bussTp,BussInfo bussInfo){
List<MyFunction<BussInfo,Boolean>> handlers = bussIdHandler.get(bussTp);
for(MyFunction<BussInfo,Boolean> handler : handlers){
if(Objects.nonNull(handler)){
return handler.apply(bussInfo);
}else{
LOGGER.warn("not found for bussTp"+bussTp);
return false;
}
}
return false;
}
}
5,任务线代码的业务注册
@Autowired
BussInfoHandler bussInfoHandler;
@PostConstruct
private void registerBussHandler() {
bussInfoHandler.addBussHandler(BussTypeConstant.BUSS_TYPE1, new MyFunction<BussInfo, Boolean>() {
@Override
public Boolean apply(BussInfo bussInfo) {
String id = bussInfo.getBussId(); //对应业务的id
if (StrUtil.equals(BussStatus.SUCCESS.getName(), bussInfo.getBussStatus().getName())) {
//通过的处理
} else {
//不通过的处理
}
return true; //根据实际情况处理
}
});
}
6.业务A调用
@Autowired
BussInfoHandler bussInfoHandler;
BussInfo bussInfo =new BussInfo();
bussInfo.setBussId(issue.getBussId());
bussInfo.setBussStatus(BussStatus.SUCCESS);
//回调业务
bussInfoHandler.handlerBuss("主业务中取出的类型,就是你注册时放的类型(本例BussTypeConstant.BUSS_TYPE1),看自己怎么安排了",bussInfo);
以上就是全部的代码实现
下面补充一些原理讲解
- 单例模式(Singleton):虽然没有明确地使用单例模式,但
bussIdHandler
是一个静态的Map
,这意味着在整个应用程序中只有一个bussIdHandler
实例。这可以看作是单例模式的一种变体,因为它是全局唯一的。 - 策略模式(Strategy):
MyFunction<BussInfo,Boolean>
是一个函数式接口,它定义了处理BussInfo
的策略。通过addBussHandler
方法,可以向处理器列表中添加不同的处理策略。在handlerBuss
方法中,根据bussTp
选择一个处理策略来处理BussInfo
。这是策略模式的一个典型应用,即根据情况选择不同的算法或行为。 -
@PostConstruct是Java自带的注解,用于在项目启动或Servlet初始化时执行特定方法。从Java EE5规范开始,Servlet中增加了两个影响Servlet生命周期的注解,@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。
在Spring框架中,@PostConstruct注解的方法会在依赖关系注入完成后执行,以执行任何初始化操作。此方法必须在将类放入服务之前调用,即使类没有请求注入任何资源,用 @PostConstruct 注解的方法也必须被调用。
被@PostConstruct注解的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。这个注解常用于加载一些缓存数据、初始化类等场景