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

286 阅读4分钟

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

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

策略模式

业务场景:

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

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

代码

首先定义一个接口EventHandler

public interface EventHandler {
    /**
     * 获取type 类型
     *
     * @return
     */
    String getType();

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

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

@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) {
        //省略其他处理逻辑
    }    
}    

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

@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详细数据,添加用户数据等。 它是在一条链上进行多次处理,该场景就可以使用责任链模式。

代码

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) {
        //省略其他处理逻辑
    }    
}     

在业务中使用

@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,重试,发送失败消息记录等。

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

代码

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的事件通知机制就可以了。

代码

// 定义一个事件
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,要想让异步生效还需要增加如下配置。

@Configuration
@EnableAsync
public class AsyncTaskConfig {
    @Bean
    public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
}