背景:
之前说过,订单消息通知的设计,主要通过多态+模板方法+策略实现
今天说的是通过自定义注解,做法是在子类上加上对应业务逻辑的枚举。
与之前设计的区别
1.同
定义抽象类:实现通用的方法
定义一个接口:让子类实现业务逻辑
子类存于map:都是把子类统一存于map,备用
2.异
抽象类:之前设计模式,抽象定义了抽象方法,之类业务处理实现抽象方法,现在没有,子类直接实现接口
,二者区别是什么:抽象方法及模板方法模式,子类与抽象类中通用实现方法有直接关系,抽象类中默认普通方法需要依赖抽象方法,有直接关系,或者说子类对消息或通知类参数,无法直接处理,需要抽象类普通方法处理一下,然后给子类处理。
接口:之前接口中处理定义业务接口外,还有一个标识区分每种子类的方法,子类各自实现,注解实现是通过子类注解中类型标识区分
map实现:之前是只是利用多态,注解实现通过:多态+反射。还有map存值不同:之前value只有一个值,注解value是个list。好乱,抛开实际业务讲设计就是扯淡,为什么是个list,因为相同的注解会存在于多个子类,即相同类型的业务通过多个子类来实现,为什么相同类型业务多个子类实现,这需要参数实际类型,或者设计方案,可能不需要这样做。选择之前类似订单的设计可能更好理解。
先看需求
视频直播-对接目睹,在目睹管理台创建直播,需要回调到后台落表数据,下图为通知参数,主要分为两层
1.namespace 代码第一层 处理的是 直播or回放
2.第二层 action,在第一层下细分,create or update or delete
设计时,第一层为不同业务项,子类实现,第二层,场景较多,可以通过定义枚举,通过
switch (actionEnum) {
case CREATE:
todo business
case UPDATE_PAGE:
todo business
代码
回调入口:
@RestController
@RequestMapping(value = "/cb/mudu/v1/")
public class NotifyReceiverController {
@Resource
private NotifySetting notifyAdaptor;
@Resource
private NotifyReceiver notifyReceiver;
@PostMapping("/notify")
public BaseResult<String> onReceiver(@RequestBody NotifyEventContext data) {
notifyReceiver.receive(data);
return BaseResult.success("ok");
}
// ----------- notify setting --------------
@PostMapping("/notify/url")
public BaseResult<Boolean> setNotifyUrl(String url) {
return BaseResult.success(notifyAdaptor.setNotifyUrl(url));
}
@GetMapping("/notify/url")
public BaseResult<String> getNotifyUrl() {
return BaseResult.success(notifyAdaptor.getNotifyUrl());
}
}
通知承接类
@Component
@Slf4j
public class NotifyReceiver {
@Resource
private CompositeNotifyListener listener;
public void receive(NotifyEventContext context) {
log.info("Receive the nofity: {}", context);
listener.handler(context);
}
}
实例初始化及处理分发类
Component
@DependsOn("springContextUtil")
@Slf4j
public class CompositeNotifyListener {
private final Map<String, List<NotifyListener>> handlerMap = new ConcurrentHashMap<>();
private ExecutorService executor = Executors.newFixedThreadPool(4, new ThreadFactory() {
private AtomicInteger threadIndex = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "NotifyExecutorThread_" + this.threadIndex.incrementAndGet());
}
});
@PostConstruct
private void register() {
Map<String, NotifyListener> map = SpringContextUtil.getBeans(NotifyListener.class);
if (map == null) {
log.info("No notify listener to register");
return;
}
map.forEach((k, v) -> {
NamespaceHandler handler = v.getClass().getAnnotation(NamespaceHandler.class);
String namespace = handler.value().getNamespace();
List<NotifyListener> list = handlerMap.get(namespace);
if (list == null) {
list = new ArrayList<NotifyListener>();
handlerMap.put(namespace, list);
}
list.add(v);
});
}
public void handler(NotifyEventContext context) {
List<NotifyListener> listeners = handlerMap.get(context.getNamespace());
if (CollectionUtils.isEmpty(listeners)) {
log.warn("Not listener to handler the namespace#{} event");
return;
}
listeners.stream().forEach(l -> executor.execute(() -> {
try {
l.handler(context);
} catch (Exception e) {
log.error("Execute the handler#{} exception", l.getClass(), e);
}
}));
}
}
接口
public interface NotifyListener {
void handler(NotifyEventContext context);
}
具体处理类
@Component
@NamespaceHandler(value = NamespaceEnum.ACTIVITY)
@Slf4j
public class ActivityListener extends AbstraceActivity implements NotifyListener {
@Resource
private LiveService liveService;
@Resource
private AuthorManagerService authorManagerService;
@Resource
private MuduConfigProperties config;
@Resource
private LiveSessionService liveSessionService;
@Autowired
private RedisManager redisManager;
@Autowired
private RemoveTagService removeTagService;
@Override
public void handler(NotifyEventContext context) {
if (context == null || !NamespaceEnum.ACTIVITY.getNamespace().equalsIgnoreCase(context.getNamespace())) {
log.warn("Not belong to the {} namespace: {}", NamespaceEnum.ACTIVITY.getNamespace(),
context.getNamespace());
return;
}
ActionEnum actionEnum = ActionEnum.get(context.getAction());
if (actionEnum == null) {
log.warn("Not found the action#{}, so not handle", context.getAction());
return;
}
switch (actionEnum) {
case CREATE:
Activity act = fetchActivity(context.getId());
if (Objects.isNull(act)) {
log.error("Not found the activity#{} from mudu", context.getId());
break;
}
String authKey = setActivityAuth(context.getId(), config.getCustomVerifyUrl());
log.info("mudu create_notify live: {}", JSON.toJSONString(act));
AuthorManager authorManager = authorManagerService.selectByManager(new Long(act.getManager()));
ResLiveDTO saveVO = ActivityConvertor.toRes(act, authKey);
if (authorManager != null) {
saveVO.setAuthorId(authorManager.getAuthorId());
}
liveService.saveLive(saveVO);
break;
case UPDATE_PAGE:
Activity upAct = fetchActivity(context.getId());
if (Objects.isNull(upAct)) {
log.error("mudu update_notify exception failed to obtain activity, token may have expired");
break;
}
log.info("mudu update_notify upAct: {}", JSON.toJSONString(upAct));
ResLiveDTO upVO = ActivityConvertor.toRes(upAct, StringUtils.EMPTY);
liveService.updateLive(upVO);
break;
case DELETE:
liveService.deleteById(Long.parseLong(context.getId()));
log.info("mudu delete_notify context: {}", JSON.toJSONString(context));
break;
case OPEN:
liveService.closeOrOpenLive(Long.parseLong(context.getId()), IsShowEnum.SHOW.getValue());
break;
case CLOSE:
liveService.closeOrOpenLive(Long.parseLong(context.getId()), IsShowEnum.NOT_SHOW.getValue());
break;
case STREAM:
if (LiveStreamEnum.CLOSE.getValue() == context.getStatus()) {
log.info("The live broadcast is closed, and the video is moved to the media library. context: {}",
JSON.toJSONString(context));
Activity activity = null;
try {
activity = fetchActivity(context.getId());
log.info("Act:{}", activity);
} catch (Exception e) {
log.error("thread-error:", e);
}
try {
ResLiveDTO startVO = new ResLiveDTO();
startVO.setRoomId(Long.valueOf(context.getId()));
startVO.setLiveStatus(LiveStatusEnum.LIVE_END.getValue());
startVO.setCloseTime(new Date());
liveService.updateLive(startVO);
} catch (Exception e) {
log.error("update live failed for#{}:", context.getId(), e);
}
try {
liveSessionService.saveLiveSession(activity);
} catch (Exception e) {
log.error("Saving live session exception for#{}:", context.getId(), e);
}
}
if (LiveStreamEnum.OPEN.getValue() == context.getStatus()) {
log.info("live starting: {}", JSON.toJSONString(context));
ResLiveDTO startVO = new ResLiveDTO();
startVO.setRoomId(Long.valueOf(context.getId()));
startVO.setLiveStatus(context.getStatus());
liveService.updateLive(startVO);
//删除tag
try {
removeTagService.removeTagByActId(Integer.valueOf(context.getId()));
} catch (Exception e) {
log.error("remove tag failed by actId:{}", context.getId());
}
}
break;
default:
break;
}
}
}