今天下班比较早,带着我可爱滴小多多溜溜公园,买买菜,生活总是这样该多么的美好,“猿”也需要拥抱生活,享受生活,技术源自生活这一点都没错,当你真正领会到了“hello world”的真正含义,你就离大牛不远了。
闲话少说,生活中公众号消息推送、微博粉丝消息推送、报纸的订阅等等都是观察者模式一种类型。这也是我在项目中运用设计模式(观察者+策略模式)来完成“变态需求”。今天要利用ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。
概念
在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
ApplicationEvent:相当于被观察者(subject)、或者是一个主题
EventListener:相当于观察者(observer)
applicationContext.publishEvent(new ReportEvent(this,chatObject))发布事件,触发事件,EventListener运行。
需求
从数据库中从查询一个项目数据,分别生成火力报表、水力报表、网关报表。
类图
代码
ReportStrategy接口类
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
/**
* 说明:处理一个项目数据生成报表,并保存fastDFS中
* @author lixiaolong
* @create 2019-03-15 15:51
*/
public interface ReportStrategy {
void dealData(ChatObject chatObject);
}
ReportStrategy实现类一
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 火力报表实现类
* @author lixiaolong
* @create 2019-03-15 15:54
*/
@Service
@Slf4j
public class FireReportStrategy implements ReportStrategy {
@Override
public void dealData(ChatObject chatObject) {
/**
* 1.生成pdf报表文件,上传FastDFS文件服务器上
* 2.更新数据。。。。
* 等等业务
*/
log.info("生成火力报表:{}",chatObject);
}
}
ReportStrategy实现类二
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 水力报表实现类
* @author lixiaolong
* @create 2019-03-15 15:54
*/
@Service
@Slf4j
public class WaterReportStrategy implements ReportStrategy {
@Override
public void dealData(ChatObject chatObject) {
/**
* 1.生成pdf报表文件,上传FastDFS文件服务器上
* 2.更新数据。。。。
* 等等业务
*/
log.info("生成水力报表:{}",chatObject);
}
}
ReportStrategy实现类三
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 网关报表实现类
* @author lixiaolong
* @create 2019-03-15 15:54
*/
@Service
@Slf4j
public class GatewayReportStrategy implements ReportStrategy {
@Override
public void dealData(ChatObject chatObject) {
/**
* 1.生成pdf报表文件,上传FastDFS文件服务器上
* 2.更新数据。。。。
* 等等业务
*/
log.info("生成网关报表:{}",chatObject);
}
}
ReportStrategeyConfig配置类
package com.duoduo.observer02;
import com.duoduo.strategy.AlibabaDeviceStrategey;
import com.duoduo.strategy.AphoneDeviceStrategey;
import com.duoduo.strategy.DeviceStrategey;
import com.duoduo.strategy.G5DeviceStrategey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author lixiaolong
* @create 2019-03-12 20:01
*/
@Component
public class ReportStrategeyConfig {
private static final Map map = new ConcurrentHashMap<>();
@Autowired
private ApplicationContext applicationContext;
/**
* 初始化映射关系Map,key-设备类型,1:火力报表;2:水力报表;3:网关报表
* value-各类报表的实现类
* 映射关系可以从数据库中读取、或者配置文件中读取
*/
static {
map.put(1,FireReportStrategy.class);
map.put(2,WaterReportStrategy.class);
map.put(3,GatewayReportStrategy.class);
}
public ReportStrategy getDeviceStrategey(Integer type){
return (ReportStrategy)applicationContext.getBean(map.get(type));
}
}
ReportEvent事件类
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* 火力报表
* @author lixiaolong
* @create 2019-03-15 15:35
*/
@Getter
public class ReportEvent extends ApplicationEvent {
private ChatObject chatObject;
public ReportEvent(Object source, ChatObject chatObject) {
super(source);
this.chatObject = chatObject;
}
}
ReportListener事件监听类
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* @author lixiaolong
* @create 2019-03-15 15:48
*/
@Component
public class ReportListener {
@Autowired
private ReportStrategeyConfig reportStrategeyConfig;
/**
* 执行火力报表-观察者一
* @param reportEvent
*/
//@Order(1)
@EventListener
@Async
public void fireReportListener(ReportEvent reportEvent){
ChatObject chatObject = reportEvent.getChatObject();
reportStrategeyConfig.getDeviceStrategey(1).dealData(chatObject);
}
/**
* 执行水力报表-观察者二
* @param reportEvent
*/
//@Order(7)
@EventListener
@Async
public void waterReportListener(ReportEvent reportEvent){
ChatObject chatObject = reportEvent.getChatObject();
reportStrategeyConfig.getDeviceStrategey(2).dealData(chatObject);
}
/**
* 执行网关报表-观察者三
* @param reportEvent
*/
//@Order(3)
@EventListener
@Async
public void gatewayReportListener(ReportEvent reportEvent){
ChatObject chatObject = reportEvent.getChatObject();
reportStrategeyConfig.getDeviceStrategey(3).dealData(chatObject);
}
}
ListenerAsyncConfiguration类,利用线程池提升性能
package com.duoduo.observer02;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* @author lixiaolong
* @create 2019-03-15 16:25
*/
@Configuration
@EnableAsync
@Slf4j
public class ListenerAsyncConfiguration implements AsyncConfigurer {
/**
* 获取异步线程池执行对象
* @return
*/
@Override
public Executor getAsyncExecutor() {
//使用Spring内置线程池任务对象
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数
taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
}
}
测试类
package com.duoduo.observer02;
import com.duoduo.model.ChatObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author lixiaolong
* @create 2019-03-15 16:14
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ReportEventTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void contextLoads() {
ChatObject chatObject = new ChatObject("ekko", "hello world");
applicationContext.publishEvent(new ReportEvent(this,chatObject));
}
}
执行结果(异步执行)
总结
总的来说,当拿到新需求,不要急着动手,先把它梳理清楚,怎么优雅高效的完成,对于自己也是一个提升,技术这条路,只有不断的学习,不断的思考,把学到的运用到项目上,也是很有成就感,谢谢各位看官的观摩谢谢!
github地址:https://github.com/itekko/design_pattern