通过注解实现策略模式,解决不同消息不同类处理

项目中需要对不同类型的消息进行不同的处理,这个也是也最常见的订单创建问题是一致的。
最初级的办法就是使用 if…else 或者 switch 来实现逻辑,但是后面类型越来越多的时候,if…else 就显得有些过于臃肿也不易于维护了。最主要的可能就是不能体现实现者的技术能力。所以此时就需要使用策略模式来装一装了。

基础接口定义

策略模式最关键的一点就是所有的实现类都要实现同一个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.gugu.boy.spring.websocket.handler.processor;

import com.gugu.boy.spring.websocket.MessagePayload;
import org.springframework.web.socket.WebSocketSession;

/**
* 发送socket消息 template.
*
* @author zhoumeiqin
* @date 2019/10/14
*/
public interface PayloadProcessor {
/**
* 执行.
*
* @param session
* @param payload
*/
void process(WebSocketSession session, MessagePayload payload) throws Exception;
}
注解定义

定义注解,通过注解上的类型,来使实现类支持对应类型的消息处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.gugu.boy.spring.websocket.handler.processor;

import java.lang.annotation.*;

/**
* @author zhoumeiqin
* @date 2019/10/14
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ProcessorSupport {
int[] types();
}
实现类的定义
1
2
3
4
5
6
7
8
9
10
11
12
@Slf4j
@Component
@ProcessorSupport(types = {PayloadConst.Type.TASK_ASSIGN, PayloadConst.Type.APPROVAL_RESULT})
public class CommonPayloadProcessor implements PayloadProcessor {

@Override
public void process(WebSocketSession session, MessagePayload payload) throws Exception {
String context = JSON.toJSONString(payload);
TextMessage message = new TextMessage(context);
session.sendMessage(message);
}
}
1
2
3
4
5
6
7
8
9
10
11
@Slf4j
@Component
@ProcessorSupport(types = {PayloadConst.Type.SCENE_REFRESH, PayloadConst.Type.ALERT_ACTION})
public class PopularProcessor implements PayloadProcessor {
@Override
public void process(WebSocketSession session, MessagePayload payload) throws Exception {
String context = JSON.toJSONString(payload);
TextMessage message = new TextMessage(context);
session.sendMessage(message);
}
}
Context 实现

Context 的目的在于在程序启动的时候,将所有带有注解ProcessorSupportPayloadProcessor类型的实例管理起来,通过ProcessorSupport的 type 支持的类型,快速定位到对应的实例上,从而实现对应的逻辑。

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
package com.gugu.boy.spring.websocket.handler;

import com.github.greenyears.core.utils.AssertUtils;
import com.gugu.boy.spring.websocket.handler.processor.PayloadProcessor;
import com.gugu.boy.spring.websocket.handler.processor.ProcessorSupport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* 消息文本处理上下文.
*
* @author zhoumeiqin
* @date 2019/10/14
*/
@Slf4j
@Component
public class PayloadProcessorContext implements CommandLineRunner, ApplicationContextAware {
private volatile ApplicationContext applicationContext;
private static final Map<Integer, PayloadProcessor> processorMap = new ConcurrentHashMap<>();

public PayloadProcessorContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

@Override
public void run(String... args) throws Exception {
this.initialization();
}

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

/**
* 初始化.
*/
private void initialization() {
Collection<PayloadProcessor> processors = this.applicationContext.getBeansOfType(PayloadProcessor.class).values();
processors.forEach(processor -> {
Class<? extends PayloadProcessor> clazz = processor.getClass();
ProcessorSupport annotation = clazz.getAnnotation(ProcessorSupport.class);
if (annotation != null) {
int[] types = annotation.types();
for (int type : types) {
processorMap.put(type, processor);
}

}
});
}

/**
* 获取实例.
*
* @param type
* @return
*/
public static PayloadProcessor getInstance(Integer type) {
PayloadProcessor processor = processorMap.get(type);
AssertUtils.notNull(processor, "不支持的消息类型");
return processor;
}
}
使用方法

使用方法很简单的了,只需要将消息中的 type 属性拿到,通过PayloadProcessorContext.getInstance()即可以获取到PayloadProcessor.process()方法去实现就行了,是不是比if...else高大上美观?

1
2
3
4
5
6
7
8
9
10
11
public synchronized void sendMessage(MessagePayload payload) {
log.info("发送消息给所有人.: {}", JSON.toJSONString(payload));
PayloadProcessor processor = PayloadProcessorContext.getInstance(payload.getType());
surveySessions.forEach((key, value) -> {
try {
processor.process(value, payload);
} catch (Exception e) {
log.error("发送给用户消息失败: {}", JSON.toJSONString(value.getPrincipal()), e);
}
});
}