侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

APDPlat中领域模型的自描述机制与事件通知机制

2022-07-02 星期六 / 0 评论 / 0 点赞 / 65 阅读 / 21712 字

APDPlat中的Model抽象类是所有领域对象的基类,对领域模型的CRUD操作都会触发事件,监听这些事件是实现实时搜索、业务审计、权限验证、模型预处理等功能的基础。 我们先看看Model的设计要点

APDPlat中的Model抽象类是所有领域对象的基类,对领域模型的CRUD操作都会触发事件,监听这些事件是实现实时搜索、业务审计、权限验证、模型预处理等功能的基础

 

我们先看看Model的设计要点:

 

1、模型的自描述机制。

2、统一的事件通知接口。

 

Model类定义了一个抽象方法getMetaData(),子类需要实现该方法来描述领域模型的具体业务含义,如下所示:

 

public abstract String getMetaData();

  

IndexLog类的实现如下:

 

    @Override    public String getMetaData() {        return "重建索引日志";    }

 

 

领域模型的每个数据字段也需要一个@ModelAttr注解,该注解指出了该字段的业务含义,看IndexLog的一个字段注解:

 

    @ModelAttr("开始处理时间")    protected Date startTime;

  

自描述机制最大的作用就是自动化,良好的可读性以及可维护性,将代码就是文档的理念发挥到极致。

 

接下来我们看看事件通知机制,当领域模型发生CRUD操作的时候,我们能收到事件,是如何做到的呢?看看Model的定义:

 

@MappedSuperclass@EntityListeners(value = ModelListener.class)public abstract class Model implements Serializable{

 

 

这里在Model中加入了@EntityListeners注解以及@MappedSuperclass注解,所以所有的模型都成了事件源。

 

这里的问题在于,@EntityListeners只能指定一个类,而无法指定对象,APDPlat所期望的是将对象的创建与维护任务尽量交给Spring的IOC容器。

 

所以,需要定义统一的事件通知接口,让ModelListener类成为观察者维护以及事件通知的核心,如下所示:

 

/** * 模型监听事件调度器 * 可注册与反注册多个ModelHandler的实现 * 相应事件发生的时候,调度器负责转发给所有注册的ModelHandler * @author 杨尚川 * */public class ModelListener {    private static final APDPlatLogger LOG = new APDPlatLogger(ModelListener.class);    private static final List<ModelHandler> modelHandlers = new LinkedList<>();        public static void addModelHandler(ModelHandler modelHandler){        LOG.info("注册模型事件处理器:"+modelHandler.getClass().getName());        modelHandlers.add(modelHandler);    }    public static void removeModelHandler(ModelHandler modelHandler){        LOG.info("移除模型事件处理器:"+modelHandler.getClass().getName());        modelHandlers.remove(modelHandler);    }        @PrePersist    public void prePersist(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.prePersist(model);        }    }    @PostPersist    public void postPersist(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.postPersist(model);        }    }    @PreRemove    public void preRemove(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.preRemove(model);        }    }    @PostRemove    public void postRemove(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.postRemove(model);        }    }    @PreUpdate    public  void preUpdate(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.preUpdate(model);        }    }    @PostUpdate    public void postUpdate(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.postUpdate(model);        }    }    @PostLoad    public void postLoad(Model model) {        for(ModelHandler modelHandler : modelHandlers){            modelHandler.postLoad(model);        }    }}

 

 

统一的事件通知接口设计成了一个抽象类(用户只需处理自己感兴趣的事件,而不用处理所有的事件),实现者只需要将自身注册到ModelListener,当事件发生的时候,就能获得通知,这样的设计就是开闭原则(OCP)所倡导的。

 

下面看3个具体的观察者实现,这里需要注意的是,这3个观察者是在Spring容器初始化完毕之后(@PostConstruct)调用ModelListener的静态方法addModelHandler来进行注册的。

 

    /**     * 注册模型处理器     */    @PostConstruct    public void init(){        ModelListener.addModelHandler(this);    }

 

 

1、对领域模型进行预处理:

 

/** * 辅助模型处理器 * @author 杨尚川 */@Servicepublic class AidModelHandler extends ModelHandler{    private static final APDPlatLogger LOG = new APDPlatLogger(AidModelHandler.class);    /**     * 注册模型处理器     */    @PostConstruct    public void init(){        ModelListener.addModelHandler(this);    }        /**     * 设置数据的拥有者     * 设置创建时间     * @param model      */    @Override    public void prePersist(Model model) {                User user=UserHolder.getCurrentLoginUser();        if(model instanceof SimpleModel){            SimpleModel simpleModel = (SimpleModel)model;            if(user!=null && simpleModel.getOwnerUser()==null && !model.getClass().isAnnotationPresent(IgnoreUser.class)){                //设置数据的拥有者                simpleModel.setOwnerUser(user);                LOG.debug("设置模型"+model+"的拥有者为:"+user.getUsername());            }        }        //设置创建时间        model.setCreateTime(new Date());        LOG.debug("设置模型"+model+"的创建时间");    }    /**     * 设置更新时间     * @param model      */    @Override    public void preUpdate(Model model) {        //设置更新时间        model.setUpdateTime(new Date());        LOG.debug("设置模型"+model+"的更新时间");    }}

 

 

2、业务操作审计

 

/** * 记录业务操作日志模型事件处理器 * @author 杨尚川 */@Servicepublic class OperateLogModelHandler extends ModelHandler{    private static final APDPlatLogger LOG = new APDPlatLogger(OperateLogModelHandler.class);    private static final boolean CREATE;    private static final boolean DELETE;    private static final boolean UPDATE;        static{        CREATE=PropertyHolder.getBooleanProperty("log.create");        DELETE=PropertyHolder.getBooleanProperty("log.delete");        UPDATE=PropertyHolder.getBooleanProperty("log.update");        if(CREATE){            LOG.info("启用添加数据日志");            LOG.info("Enable create data log", Locale.ENGLISH);        }else{            LOG.info("禁用添加数据日志");            LOG.info("Disable create data log", Locale.ENGLISH);        }        if(DELETE){            LOG.info("启用删除数据日志");            LOG.info("Enable delete data log", Locale.ENGLISH);        }else{            LOG.info("禁用删除数据日志");            LOG.info("Disable delete data log", Locale.ENGLISH);        }        if(UPDATE){            LOG.info("启用更新数据日志");            LOG.info("Enable update data log", Locale.ENGLISH);        }else{            LOG.info("禁用更新数据日志");            LOG.info("Disable update data log", Locale.ENGLISH);        }    }        /**     * 注册模型处理器     */    @PostConstruct    public void init(){        ModelListener.addModelHandler(this);    }        @Override    public void postPersist(Model model) {        if(CREATE){            saveLog(model,OperateLogType.ADD);            LOG.debug("记录模型创建日志: "+model);        }    }        @Override    public void postRemove(Model model) {        if(DELETE){            saveLog(model,OperateLogType.DELETE);            LOG.debug("记录模型删除日志: "+model);        }    }        @Override    public void postUpdate(Model model) {        if(UPDATE){            saveLog(model,OperateLogType.UPDATE);            LOG.debug("记录模型修改日志: "+model);        }    }        /**     * 将日志加入BufferLogCollector定义的内存缓冲区     * @param model     * @param type      */    private void saveLog(Model model, String type){        //判断模型是否已经指定忽略记录增删改日志        if(!model.getClass().isAnnotationPresent(IgnoreBusinessLog.class)){            User user=UserHolder.getCurrentLoginUser();            String ip=UserHolder.getCurrentUserLoginIp();            OperateLog operateLog=new OperateLog();            if(user != null){                operateLog.setUsername(user.getUsername());            }            operateLog.setLoginIP(ip);            try {                operateLog.setServerIP(InetAddress.getLocalHost().getHostAddress());            } catch (UnknownHostException ex) {                LOG.error("无法获取服务器IP", ex);            }            operateLog.setAppName(SystemListener.getContextPath());            operateLog.setOperatingTime(new Date());            operateLog.setOperatingType(type);            operateLog.setOperatingModel(model.getMetaData());            operateLog.setOperatingID(model.getId());            //将日志加入内存缓冲区            BufferLogCollector.collect(operateLog);        }    }}

 

 

3、实时索引维护

 

/** * 实时索引模型处理器 * @author 杨尚川 */@Servicepublic class RealtimeIndexModelHandler extends ModelHandler{    private static final APDPlatLogger LOG = new APDPlatLogger(RealtimeIndexModelHandler.class);    @Resource(name = "indexManager")    private IndexManager indexManager;            /**     * 注册模型处理器     */    @PostConstruct    public void init(){        ModelListener.addModelHandler(this);    }        @Override    public void postPersist(Model model) {        if(model.getClass().isAnnotationPresent(Searchable.class)){                        indexManager.createIndex(model);            LOG.debug("为模型:"+model+" 实时创建索引,增加");        }    }        @Override    public void postRemove(Model model) {        if(model.getClass().isAnnotationPresent(Searchable.class)){            indexManager.deleteIndex(model.getClass(), model.getId());            LOG.debug("为模型:"+model+" 实时创建索引,删除");        }    }        @Override    public void postUpdate(Model model) {        if(model.getClass().isAnnotationPresent(Searchable.class)){            indexManager.updateIndex(model.getClass(),model);            LOG.debug("为模型:"+model+" 实时创建索引,修改");        }    }}

 

 

 

APDPlat托管在Github

广告 广告

评论区