设计模式在 SpringBoot 开发中的应用

在日常项目开发中活用设计模式,有时候可以做到事半功倍效果,提高代码设计的扩展性。在这篇文章中列举一些我在产线 spring 项目中,真实使用到的设计模式。由于 spring 框架的使用特殊性,我们必须对原生设计模式代码做一定的调整,做到活学活用。

策略模式

业务场景:

我们有一个系统,event hub,主要用来接受各种平台发来的事件,进而告警和管控。每个系统发来的事件不同,对于不同系统,每个系统我们都需要写一个解析器,最原始最直接的代码是各种 if else if,但扩展性和封装性都很差。

要解决这种 if…else if 场景,可以考虑使用策略模式。

代码

首先定义一个接口EventHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface EventHandler {
/**
* 获取type 类型
*
* @return
*/
String getType();

/**
* 对事件进行加工处理
*
* @param event
* @return
*/
JSONObject handleEvent(JSONObject event);
}

实现不同系统的处理器,通过实现 EventHandler 接口,可以扩展更多系统,大大增强了系统的扩展性。这里举两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class GrafanaEventHandler implements EventHandler {
@Override
public String getType() {
return "grafanaAlert";
}
@Override
public JSONObject handleEvent(JSONObject event) {
//省略其他处理逻辑
}
}
@Component
public class prometheusEventHandler implements EventHandler {
@Override
public String getType() {
return "prometheusAlert";
}
@Override
public JSONObject handleEvent(JSONObject event) {
//省略其他处理逻辑
}
}

在系统中调用生成的各种处理器

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 EventHandlerFactory implements InitializingBean, ApplicationContextAware {
private static final Map<String, EventHandler> HANDLER_MAP = new HashMap<>(16);

private ApplicationContext appContext;

/**
* 根据提交类型获取对应的处理器
*
* @param type 提交类型
* @return 提交类型对应的处理器
*/
public EventHandler getHandler(String type) {
return HANDLER_MAP.get(type);
}

@Override
public void afterPropertiesSet() {
// 将 Spring 容器中所有的 EventHandler 注册到 HANDLER_MAP
appContext.getBeansOfType(EventHandler.class)
.values()
.forEach(handler -> HANDLER_MAP.put(handler.getType(), handler));
}

@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
appContext = applicationContext;
}
}

@Service
public class EventCollectService {
@Autowired
private EventHandlerFactory eventHandlerFactory;
public void receiveAlertMessage(JSONObject body, HttpServletRequest request) {
String type = jsonObject.getString("type");
EventHandler eventHandler = eventHandlerFactory.getHandler(type);
if (eventHandler != null) {
jsonObject = eventHandler.handleEvent(jsonObject);
}
//省略其他处理逻辑

}
}

通过以上例子,我们可以看到策略模式在 spring 应用中的实现,符合高内聚,可扩展的设计原则。新接入一个系统,只用实现 EventHandler 接口,其他地方不用修改,改动地方少意味着风险就小,待测方法就少。

责任链模式

业务场景:

我们发送促销 email 场景中,需要做很多数据检查,添加 item 详细数据,添加用户数据等。 它是在一条链上进行多次处理,该场景就可以使用责任链模式。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface IHandler {
/**
* 执行逻辑,可以是过滤,可以是执行业务逻辑
*/
boolean execute(BusinessRequest request)throws HandlerException;
}

@Component
@Order(100)
public class FilterBehaviorEmailHandler implements IHandler {
@Override
public boolean execute(BusinessRequest request) {
//省略其他处理逻辑
}
}
@Component
@Order(200)
public class FilterExcludeEmailHandler implements IHandler {
@Override
public boolean execute(BusinessRequest request) {
//省略其他处理逻辑
}
}

在业务中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class EmailService {
private final List<IHandler> handlers;
public EmailService(List<IHandler> handlers) {
this.handlers = handlers;
}
public void sendEmail() {
//省略其他处理逻辑
for (IHandler handler : handlers) {
boolean flag = handler.execute(request);
if (!flag) {
break;
}
}
}
}

避免需要修改多个类的 order 顺序,建议使用三位数。新扩展一个 handle 更方便插入在任意位置。

模板方法模式

业务场景:

我们的 sns 系统是一个消息统一发送平台,主要发送 rest,email,teams,sms 等消息。每次发送一个消息,都有一些公共操作:判重(避免重复发送),过滤消息,生成唯一 id,记录 log,重试,发送失败消息记录等。

这种具有多个公共处理逻辑,逻辑统一编排的业务完全可以使用模板方法来解决。

代码

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
public abstract class AbstractChannel{
private boolean isOverdue(ChannelMessage channelMessage) {
//省略
}
private boolean isFilter(ChannelMessage channelMessage) {
//省略
}
private boolean isAllowedSend(ChannelMessage channelMessage) {
//省略
}
private boolean log(ChannelMessage channelMessage) {
//省略
}
abstract void handle(ChannelMessage channelMessage);

public void send(ChannelMessage channelMessage){
if(isOverdue(channelMessage)){return;}
if(isFilter(channelMessage)){return;}
if(isAllowedSend(channelMessage)){return;}
send(channelMessage);
log(channelMessage);
}
}
@Component
public class EmailChannel extends AbstractChannel{
@Override
void handle() {
//省略
}
}
@Component
public class SmsChannel extends AbstractChannel{
@Override
void handle() {
//省略
}
}
@Component
public class TeamsChannel extends AbstractChannel{
@Override
void handle() {
//省略
}
}

在业务中使用可以直接使用相应的 channel 实例调用 send,当然也可以结合策略模式更灵活的使用。

观察者模式

业务场景:

我们内部有这么个业务场景,需要根据活动用户的不同行为发送不同的促销邮件。不同促销邮件有大量的处理逻辑,如何封装?保持开闭原则,观察者模式是其中一种手段。

在 spring 应用中,我们没必要自己实现一套观察者模式,直接使用 Spring 的事件通知机制就可以了。

代码

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 class BrowseEvent extends ApplicationEvent {
private String message;

public BrowseEvent(Object source, String message) {
super(source);
this.message = message;
}

public String getMessage() {
return message;
}
}

// 定义一个事件监听者
@Component
public class BrowseEventListener implements ApplicationListener<BrowseEvent> {
@Async
@Override
public void onApplicationEvent(BrowseEvent event) {
System.out.println("receiver " + event.getMessage());
}
}

// 事件发布
@Component
public class EventDemoPublish {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

public void publish(String message) {
BrowseEvent demo = new BrowseEvent(this, message);
applicationEventPublisher.publishEvent(demo);
}
}

默认情况下,Spring 是同步执行 Event 的响应方法的。为了并行执行,我这里增加了@Async,要想让异步生效还需要增加如下配置。

1
2
3
4
5
6
7
8
@Configuration
@EnableAsync
public class AsyncTaskConfig {
@Bean
public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}