背景:

之前说过,订单消息通知的设计,主要通过多态+模板方法+策略实现

今天说的是通过自定义注解,做法是在子类上加上对应业务逻辑的枚举。

与之前设计的区别

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


java做通知公告功能 java 通知接口设计_注解

代码

回调入口:

@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);
	    }
	}));

    }

}

java做通知公告功能 java 通知接口设计_设计模式_02

 接口

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;
	}

    }

}