责任链模式
2025-01-22 08:19:30    3.9k 字   
This post is also available in English and alternative languages.

1. 生活场景

1.1. 办公流程

个人事由需要请假一个月,需要由 直接领导、部门经理、二级中心总监、一级中心总监、集团人事 挨个审批。

在这个流程中,需要上级全部同意,一人不同意,就直接打回,不用再往后传递请求了。


1.2. 采购流程

IT部采购10台Mac电脑,需要20万资金,采购部总监直接审批。

技术部采购一台五轴联动加工中心,需要800万资金,数额巨大,要直接找厂长审批

不同的采购金额,由不同的人审批。


1.3. 纸牌流程

打牌,地主出牌以后,下家B 看手里有没有能出的牌,要不起就把出牌请求转发给 下家C,再要不起就转发给 下家。

整个过程中,牌作为一个请求沿着一条链路在传递,每一位玩家都可以处理请求;上一位处理后,下一位接着处理。


2. 责任链模式

它允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

维基百科中这样描述:

责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。
每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。
该模式还描述了往该处理链的末尾添加新的处理对象的方法。


2.1. 纯的责任链

一个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个;

要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后又将责任向下传递的情况。

而且在纯的职责链模式中,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况。


2.2. 不纯的责任链

在一个不纯的职责链模式中允许 某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收。


3. 实战

站内广告业务中,根据不同的业务划分了很多场景;不同的业务场景有不同的参数校验规则。

通过编排校验参数,形成一个校验链路。

比如:

  • check-a:request、城市编码、会员编码、商品编码、cookie、业务参数

  • check-b:request、城市编码、会员编码 业务参数

  • check-c:request、城市编码、业务参数

最简单,是写三个check方法,每个方法里面对参数进行校验,如果规则比较少也是可行;

而之前采用的是,抽象类-子类继承模式,父类与子类通过 super.check() 进行request数据传递,形成一条校验链路。

例如这样子:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public abstract class Check<T extends BaseRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(Check.class);

/**
* check request is null
*
* @param request
* @param context
* @return null/failure-false success-true
*/
public boolean check(T request, PrivateContext context) {
if (null == request) {
LOGGER.error("request is null");
return false;
}
return true;
}
}

public class CityIdCheck extends Check<Request> {

private static final Logger LOGGER = LoggerFactory.getLogger(CityIdCheck.class);

@Override
public boolean check(Request request, PrivateContext context) {
boolean result = super.check(request, context);
if (!result) {
return result;
}

if (StringUtils.isEmpty(request.getCityCode())) {
result = false;
LOGGER.error("custNo:{},sceneId:{},cityCode is empty", new Object[]{request.getCustNumber(), request.getCityCode()});
}
return result;
}
}

public class TerminalCheck extends CityIdCheck {

private static final Logger LOGGER = LoggerFactory.getLogger(TerminalCheck.class);

@Override
public boolean check(Request request, PrivateContext context) {

boolean result = super.check(request, context);

if (!result) {
return result;
}

if (StringUtils.isEmpty(request.getTerminal()) || TerminalEnum.getValByKey(request.getTerminal()) == 0) {
result = false;
LOGGER.error("termina is empty");
}

return result;
}
}

以上代码,通过父子继承,实现一条校验链路。

随着时间推移,业务需求的扩张,校验的规则越来越多。总是通过父子继承去实现一条校验链路,显的非常呆板。

虽然校验规则在变,但业务参数就那么多个,虽然有时候会新增。

于是准备对其进行优化,目标:第一是模块化,第二是可配置编排。

采用 不纯的责任链模式 来实现。


3.1. 责任链模式

3.1.1. 定义抽象类

首先定义一个抽象类,其中成员变量有 后继处理handler。

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
34
35
public abstract class AbstractCheckHandler<T extends BaseRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCheckHandler.class);

/**
* 定义后继处理对象
*/
protected AbstractCheckHandler nextHandler;

/**
* 没有后继对象,则处于责任链末端
*
* @return true-有后继对象,false-没有后继对象
*/
public boolean hasNext() {
return Objects.nonNull(this.nextHandler);
}

/**
* 设置后继者
*
* @param nextHandler
*/
public void setNextHandler(AbstractCheckHandler nextHandler) {
this.nextHandler = nextHandler;
}

/**
* 抽象请求处理方法
*
* @param request request
*/
public abstract boolean checkRequest(T request);

}

下面是继承该抽象类,子类实现自己的校验逻辑。


3.1.2. 校验request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class RequestCheck<T extends CpcBaseRequest> extends AbstractCheckHandler<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(RequestCheck.class);

@Override
public boolean checkRequest(T request) {

LOGGER.info("check request");

//process
boolean result = null != request;

if (hasNext() && result) {
return this.nextHandler.checkRequest(request);
}
return result;
}
}

3.1.3. 校验cityCode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class CityCheck<T extends CpcBaseRequest> extends RequestCheck<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(CityCheck.class);

@Override
public boolean checkRequest(T request) {

LOGGER.info("check cityCode");

boolean result = StringUtils.isNotBlank(request.getCityId());

if (this.hasNext() && result) {
return this.nextHandler.checkRequest(request);
}
return result;
}
}

3.1.4. 校验商品编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class GoodsInfoCheck extends RequestCheck<RecommendRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(GoodsInfoCheck.class);

@Override
public boolean checkRequest(RecommendRequest request) {

LOGGER.info("check goodsInfo");

boolean result = StringUtils.isNotBlank(request.getGoodsId());

if (hasNext() && result) {
//转发请求
return this.nextHandler.checkRequest(request);
}
return result;
}
}

3.1.5. 校验会员编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Component
public class CustNumCheck<T extends CpcBaseRequest> extends RequestCheck<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(CustNumCheck.class);

@Override
public boolean checkRequest(T request) {

LOGGER.info("check custNum");

boolean result = StringUtils.isNotBlank(request.getCustno());

if (hasNext() && result) {
//转发请求
return this.nextHandler.checkRequest(request);
}
return result;
}
}

3.1.6. 校验code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class ConferenceCodeCheck extends RequestCheck<RecommendRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(ConferenceCodeCheck.class);

@Override
public boolean checkRequest(RecommendRequest request) {

LOGGER.info("check ConferenceCode");

//process
boolean result = StringUtils.isNotBlank(request.getConferenceCode());

if (hasNext() && result) {
//转发请求
return this.nextHandler.checkRequest(request);
}
return result;
}
}

3.1.7. 编排

实现完具体的校验处理以后,就需要对其进行编排。

链路1:request - 城市编码 - 商品编码 - 榜单编码

链路2:request - 城市编码 - 会员编码 - 商品编码

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@Component
public class CheckComponent {

/**
* 普通通用检查链路
* <p>
* request - 城市编码 - 商品编码 - 榜单编码
*/
public void processGenericCheckLink(RecommendRequest request) {
AbstractCheckHandler genericCheckLink = getGenericCheckLink();
System.out.println(genericCheckLink.checkRequest(request));
}

protected AbstractCheckHandler getGenericCheckLink() {
AbstractCheckHandler cityCheck = new CityCheck();
AbstractCheckHandler goodsInfoCheck = new GoodsInfoCheck();
AbstractCheckHandler conferenceCodeCheck = new ConferenceCodeCheck();
AbstractCheckHandler requestCheckHandler = new RequestCheck();

requestCheckHandler.setNextHandler(cityCheck);
cityCheck.setNextHandler(goodsInfoCheck);
goodsInfoCheck.setNextHandler(conferenceCodeCheck);

return requestCheckHandler;
}

/**
* 推荐业务检查链路
* <p>
* request - 城市编码 - 会员编码 - 商品编码
*/
public void processRecommendCheckLink(RecommendRequest request) {
AbstractCheckHandler recommendCheckLink = getRecommendCheckLink();
System.out.println(recommendCheckLink.checkRequest(request));
}

protected AbstractCheckHandler getRecommendCheckLink() {
AbstractCheckHandler cityCheck = new CityCheck();
AbstractCheckHandler custNumCheck = new CustNumCheck();
AbstractCheckHandler goodsInfoCheck = new GoodsInfoCheck();
AbstractCheckHandler requestCheckHandler = new RequestCheck();

requestCheckHandler.setNextHandler(cityCheck);
cityCheck.setNextHandler(custNumCheck);
custNumCheck.setNextHandler(goodsInfoCheck);

return requestCheckHandler;
}
}

3.1.8. 测试

main()方法进行测试

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
@Component
public class CheckMainApp {

private static final Logger LOGGER = LoggerFactory.getLogger(CheckMainApp.class);
private static final String APPLICATION_CONTEXT_FILE = "/spring/applicationContext.xml";

@Resource
private CheckComponent checkComponent;

public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(APPLICATION_CONTEXT_FILE);
CheckMainApp bean = applicationContext.getBean(CheckMainApp.class);
bean.process();
}

private void process() {
checkComponent.processGenericCheckLink(getRecommendRequest());

System.out.println();

checkComponent.processRecommendCheckLink(getRecommendRequest());
}

private RecommendRequest getRecommendRequest() {
RecommendRequest recommendRequest = new RecommendRequest();
recommendRequest.setGoodsId("123123");
recommendRequest.setConferenceCode("234234");
recommendRequest.setCityId("123");
recommendRequest.setCustno("");
return recommendRequest;
}
}

运行后看结果,和预期的一致。

1
2
3
4
5
6
7
8
9
10
[main] INFO  o.p.crp.plana.check.RequestCheck - 20 - check request
[main] INFO o.pattern.crp.plana.check.CityCheck - 21 - check cityCode
[main] INFO o.p.crp.plana.check.GoodsInfoCheck - 21 - check goodsInfo
[main] INFO o.p.c.p.check.ConferenceCodeCheck - 21 - check ConferenceCode
true

[main] INFO o.p.crp.plana.check.RequestCheck - 20 - check request
[main] INFO o.pattern.crp.plana.check.CityCheck - 21 - check cityCode
[main] INFO o.p.crp.plana.check.CustNumCheck - 21 - check custNum
false

如此一来,如果要新增一个校验链路,只要在 CheckComponent 类中进行编排即可。

距离 模块化、可配置编排 还差一点。


3.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
26
public class CheckLinkFactory {

private static final Logger LOGGER = LoggerFactory.getLogger(CheckLinkFactory.class);

public static AbstractCheckHandler getGenericCheckLink() {
AbstractCheckHandler cityCheck = new CityCheck();
AbstractCheckHandler goodsInfoCheck = new GoodsInfoCheck();
AbstractCheckHandler conferenceCodeCheck = new ConferenceCodeCheck();
AbstractCheckHandler requestCheckHandler = new RequestCheck();
requestCheckHandler.setNextHandler(cityCheck);
cityCheck.setNextHandler(goodsInfoCheck);
goodsInfoCheck.setNextHandler(conferenceCodeCheck);
return requestCheckHandler;
}

public static AbstractCheckHandler getRecommendCheckLink() {
AbstractCheckHandler cityCheck = new CityCheck();
AbstractCheckHandler custNumCheck = new CustNumCheck();
AbstractCheckHandler goodsInfoCheck = new GoodsInfoCheck();
AbstractCheckHandler requestCheckHandler = new RequestCheck();
requestCheckHandler.setNextHandler(cityCheck);
cityCheck.setNextHandler(custNumCheck);
custNumCheck.setNextHandler(goodsInfoCheck);
return requestCheckHandler;
}
}

如此一来,校验编排的代码就简洁多了:

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 CheckComponent {

/**
* 普通通用检查链路
* <p>
* request - 城市编码 - 商品编码 - 榜单编码
*/
public void processGenericCheckLink(RecommendRequest request) {
AbstractCheckHandler genericCheckLink = CheckLinkFactory.getGenericCheckLink();
System.out.println(genericCheckLink.checkRequest(request));
}

/**
* 推荐业务检查链路
* <p>
* request - 城市编码 - 会员编码 - 商品编码
*/
public void processRecommendCheckLink(RecommendRequest request) {
AbstractCheckHandler recommendCheckLink = CheckLinkFactory.getRecommendCheckLink();
System.out.println(recommendCheckLink.checkRequest(request));
}
}

3.3. 优化二

再进行优化,实在不想看到那么一大坨,校验编排的代码;

而且这种编排方式太硬了、还是太死,应该做成配置的那种。

结合 自定义注解、Spring BeanPostProcessor,实现可配置的链路。

自定义注解

1
2
3
4
5
6
7
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CheckHandlerAnn {

}

继承 spring BeanPostProcessor 接口,在bean初始化之后,获取标记了 CheckHandlerAnn 注解的 check handler。

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
34
35
36
37
38
39
40
41
42
43
44
45
@Component
public class MappingBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

private static final Logger LOGGER = LoggerFactory.getLogger(MappingBeanPostProcessor.class);

private ApplicationContext applicationContext;

/**
* checkHandler Class Name : checkHandler Class
*/
private Map<String, Class<?>> checkHandler = new HashMap<>(32);

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return null;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
processCheckAnn(bean.getClass());
return bean;
}

/**
* 获取标注 {@link CheckHandlerAnn} 注解的类
*
* @param aClass
*/
private void processCheckAnn(Class<?> aClass) {
CheckHandlerAnn annotation = AnnotationUtils.findAnnotation(aClass, CheckHandlerAnn.class);
if (null != annotation) {
checkHandler.put(aClass.getName(), aClass);
LOGGER.info("put class:{}", aClass.getName());
}
}

public Map<String, Class<?>> getCheckHandler() {
return checkHandler;
}
}

和之前一样的抽象类

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
34
35
36
public abstract class AbstractCheckHandlerTwo<T extends CpcBaseRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCheckHandlerTwo.class);

/**
* 定义后继处理对象
*/
protected AbstractCheckHandlerTwo nextHandler;

/**
* 没有后继对象,则处于责任链末端
*
* @return true-有后继对象,false-没有后继对象
*/
public boolean hasNext() {
return Objects.nonNull(this.nextHandler);
}

/**
* 设置后继者
*
* @param nextHandler
*/
public void setNextHandler(AbstractCheckHandlerTwo nextHandler) {
this.nextHandler = nextHandler;
}

/**
* 抽象请求处理方法
*
* @param request request
* @return true -> success , false -> fail
*/
public abstract boolean checkRequest(T request);

}

继承的子类,在类上标记 CheckHandlerAnn 注解。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
@CheckHandlerAnn
@Component
public class CityCheckTwo<T extends CpcBaseRequest> extends RequestCheckTwo<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(CityCheckTwo.class);

@Override
public boolean checkRequest(T request) {

LOGGER.info("check cityCode");

boolean result = StringUtils.isNotBlank(request.getCityId());

if (this.hasNext() && result) {
return this.nextHandler.checkRequest(request);
}
return result;
}
}

@CheckHandlerAnn
@Component
public class ConferenceCodeCheckTwo extends RequestCheckTwo<RecommendRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(ConferenceCodeCheckTwo.class);

@Override
public boolean checkRequest(RecommendRequest request) {

LOGGER.info("check ConferenceCode");

//process
boolean result = StringUtils.isNotBlank(request.getConferenceCode());

if (hasNext() && result) {
//转发请求
return this.nextHandler.checkRequest(request);
}
return result;
}
}

@CheckHandlerAnn
@Component
public class CustNumCheckTwo<T extends CpcBaseRequest> extends RequestCheckTwo<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(CustNumCheckTwo.class);

@Override
public boolean checkRequest(T request) {

LOGGER.info("check custNum");

boolean result = StringUtils.isNotBlank(request.getCustno());

if (hasNext() && result) {
//转发请求
return this.nextHandler.checkRequest(request);
}
return result;
}
}

@CheckHandlerAnn
@Component
public class GoodsInfoCheckTwo extends RequestCheckTwo<RecommendRequest> {

private static final Logger LOGGER = LoggerFactory.getLogger(GoodsInfoCheckTwo.class);

@Override
public boolean checkRequest(RecommendRequest request) {

LOGGER.info("check goodsInfo");

boolean result = StringUtils.isNotBlank(request.getGoodsId());

if (hasNext() && result) {
//转发请求
return this.nextHandler.checkRequest(request);
}
return result;
}
}

@CheckHandlerAnn
@Component
public class RequestCheckTwo<T extends CpcBaseRequest> extends AbstractCheckHandlerTwo<T> {

private static final Logger LOGGER = LoggerFactory.getLogger(RequestCheckTwo.class);

@Override
public boolean checkRequest(T request) {

LOGGER.info("check request");

//process
boolean result = null != request;

if (hasNext() && result) {
return this.nextHandler.checkRequest(request);
}
return result;
}
}

通过枚举,编排校验链路

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
34
public enum CheckLinkEnums {

/**
* 普通通用检查链路
* <p>
* request - 城市编码 - 商品编码 - 榜单编码
*/
GENERIC_CHECK_LINK(Arrays.asList(
RequestCheckTwo.class.getName(),
CityCheckTwo.class.getName(),
GoodsInfoCheckTwo.class.getName(),
ConferenceCodeCheckTwo.class.getName())),

/**
* 推荐业务检查链路
* <p>
* request - 城市编码 - 会员编码 - 商品编码
*/
GET_RECOMMEND_CHECK_LINK(Arrays.asList(
RequestCheckTwo.class.getName(),
CityCheckTwo.class.getName(),
CustNumCheckTwo.class.getName(),
GoodsInfoCheckTwo.class.getName()));

private List<String> checkLink;

public List<String> getCheckLink() {
return checkLink;
}

CheckLinkEnums(List<String> checkLink) {
this.checkLink = checkLink;
}
}

校验编排的具体实现,在程序启动后就执行。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Component
public class CheckComponentTwo {

private static final Logger LOGGER = LoggerFactory.getLogger(CheckComponentTwo.class);

private Map<String, AbstractCheckHandlerTwo> checkHandlerList = new HashMap<>(32);

@Resource
private MappingBeanPostProcessor mappingBeanPostProcessor;

/**
* 处理 {@link CheckLinkEnums} 中编排的配置
*/
public void choreographyCheckHandler() throws InstantiationException, IllegalAccessException {

CheckLinkEnums[] values = CheckLinkEnums.values();

if (ArrayUtils.isEmpty(values)) {
LOGGER.warn("check link enums is empty!");
return;
}

LOGGER.info("CheckLinkEnums#value:{}", Arrays.toString(values));

for (CheckLinkEnums enums : values) {
List<String> checkLink = enums.getCheckLink();
analysisHandler(enums.name(), checkLink);
}
}

/**
* 实例化 check责任链
*
* @param name check链-key
* @param handerlClass check链-handler
* @throws IllegalAccessException
* @throws InstantiationException
*/
private void analysisHandler(String name, List<String> handerlClass) throws IllegalAccessException, InstantiationException {

List<AbstractCheckHandlerTwo> checkHandlers = new ArrayList<>();

for (int i = 0; i < handerlClass.size(); i++) {
String handlClazz = handerlClass.get(i);

//获取 check handler object
if (!mappingBeanPostProcessor.getCheckHandler().containsKey(handlClazz)) {
return;
}

Class<?> checkHandlerClass = mappingBeanPostProcessor.getCheckHandler().get(handlClazz);

AbstractCheckHandlerTwo abstractCheckHandler = (AbstractCheckHandlerTwo) checkHandlerClass.newInstance();

checkHandlers.add(abstractCheckHandler);

if (i != 0) {
checkHandlers.get(i - 1).setNextHandler(abstractCheckHandler);
}
}
checkHandlerList.put(name, checkHandlers.get(0));
}

public Map<String, AbstractCheckHandlerTwo> getCheckHandlerList() {
return checkHandlerList;
}
}

main()方法测试

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
34
35
36
37
38
39
40
@Component
public class CheckTwoMainApp {

private static final Logger LOGGER = LoggerFactory.getLogger(CheckTwoMainApp.class);

private static final String APPLICATION_CONTEXT_FILE = "/spring/applicationContext.xml";

@Resource
private CheckComponentTwo checkComponentTwo;

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(APPLICATION_CONTEXT_FILE);
CheckTwoMainApp bean = applicationContext.getBean(CheckTwoMainApp.class);
bean.processCheckHandler();
}

private void processCheckHandler() throws IllegalAccessException, InstantiationException {

//编排
checkComponentTwo.choreographyCheckHandler();

AbstractCheckHandlerTwo genericCheckLink = checkComponentTwo.getCheckHandlerList().get("GENERIC_CHECK_LINK");
LOGGER.info("result:{}", genericCheckLink.checkRequest(getRecommendRequest()));

System.out.println();

AbstractCheckHandlerTwo getRecommendCheckLink = checkComponentTwo.getCheckHandlerList().get("GET_RECOMMEND_CHECK_LINK");
LOGGER.info("result:{}", getRecommendCheckLink.checkRequest(getRecommendRequest()));

}

private RecommendRequest getRecommendRequest() {
RecommendRequest recommendRequest = new RecommendRequest();
recommendRequest.setGoodsId("123123");
recommendRequest.setConferenceCode("234234");
recommendRequest.setCityId("123");
recommendRequest.setCustno("");
return recommendRequest;
}
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[main] INFO  o.p.c.planb.MappingBeanPostProcessor - 57 - put class:org.pattern.crp.planb.check.CityCheckTwo
[main] INFO o.p.c.planb.MappingBeanPostProcessor - 57 - put class:org.pattern.crp.planb.check.ConferenceCodeCheckTwo
[main] INFO o.p.c.planb.MappingBeanPostProcessor - 57 - put class:org.pattern.crp.planb.check.CustNumCheckTwo
[main] INFO o.p.c.planb.MappingBeanPostProcessor - 57 - put class:org.pattern.crp.planb.check.GoodsInfoCheckTwo
[main] INFO o.p.c.planb.MappingBeanPostProcessor - 57 - put class:org.pattern.crp.planb.check.RequestCheckTwo

[main] INFO o.p.crp.planb.CheckComponentTwo - 39 - CheckLinkEnums#value:[GENERIC_CHECK_LINK, GET_RECOMMEND_CHECK_LINK]

[main] INFO o.p.crp.planb.check.RequestCheckTwo - 22 - check request
[main] INFO o.p.crp.planb.check.CityCheckTwo - 23 - check cityCode
[main] INFO o.p.c.planb.check.GoodsInfoCheckTwo - 23 - check goodsInfo
[main] INFO o.p.c.p.check.ConferenceCodeCheckTwo - 23 - check ConferenceCode
[main] INFO o.pattern.crp.planb.CheckTwoMainApp - 40 - result:true

[main] INFO o.p.crp.planb.check.RequestCheckTwo - 22 - check request
[main] INFO o.p.crp.planb.check.CityCheckTwo - 23 - check cityCode
[main] INFO o.p.crp.planb.check.CustNumCheckTwo - 23 - check custNum
[main] INFO o.pattern.crp.planb.CheckTwoMainApp - 45 - result:false

简单说下逻辑。

  1. 首先实现各个细化的check类(CityCheckTwo、CustNumCheckTwo、GoodsInfoCheckTwo 等),并且添加 CheckHandlerAnn 类注解

  2. 在枚举中编排校验链路

  3. 程序启动,通过 Spring BeanPostProcessor 接口,在Bean实例化之后,获取所有标记 CheckHandlerAnn 注解的类,存到Map中。

  4. 调用 初始化编排的方法,按照 枚举中的配置链路配置,顺序的去Map中挨个找出,并实例化实现编排。

  5. 编排完成后,即可使用。

如此一来,实现了当初的目标,模块化、可配置编排。

以后如果有新业务,需要新增校验链路,只要去 CheckLinkEnums 新增一个枚举配置即可。