文档章节

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

杨尚川
 杨尚川
发布于 2014/02/01 06:48
字数 1494
阅读 309
收藏 7

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 杨尚川
 */
@Service
public 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 杨尚川
 */
@Service
public 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 杨尚川
 */
@Service
public 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

© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
APDPlat拓展搜索之集成ElasticSearch

APDPlat充分利用Compass的OSEM和ORM integration特性,提供了简单易用且功能强大的内置搜索特性。 APDPlat的内置搜索,在设计简洁优雅的同时,还具备了强大的实时搜索能力,用户只需用注解的...

杨尚川
2014/02/01
292
2
基于word分词提供的文本相似度算法来实现通用的网页相似度检测

实现代码:基于word分词提供的文本相似度算法来实现通用的网页相似度检测 运行结果: 检查的博文数:128 1、检查博文:192本软件著作用词分析(五)用词最复杂99级,相似度分值:Simple=0.96...

杨尚川
2015/05/28
1K
0
APDPlat拓展搜索之集成Solr

APDPlat充分利用Compass的OSEM和ORM integration特性,提供了简单易用且功能强大的内置搜索特性。 APDPlat的内置搜索,在设计简洁优雅的同时,还具备了强大的实时搜索能力,用户只需用注解的...

杨尚川
2014/02/01
662
0
APDPlat的系统启动和关闭流程剖析

APDPlat接管了Spring的启动关闭权,为各种运行其上的开源框架和类库的无缝集成提供了支持。 当然,大家都知道,一个JAVA EE Web应用的入口点是web.xml,APDPlat当然也不例外,我们看看APDPl...

杨尚川
2014/02/03
414
0
APDPlat如何自动建库建表并初始化数据?

APDPlat共支持10种数据库:DB2、DERBY、H2、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQLSERVER、SYBASE。 数据库的默认配置信息在文件APDPlatCore/src/main/resources/org/apdplat/db.p...

杨尚川
2014/02/08
360
0

没有更多内容

加载失败,请刷新页面

加载更多

崛起于Springboot2.X之5分钟解决单点登陆(53)

SpringBoot2.X心法总纲 1、pom文件依赖 <dependency> <groupId>com.majiaxueyuan</groupId> <artifactId>sso-core</artifactId> <version>1.2.2</version></dependency> 2、......

木九天
21分钟前
16
0
面向对象和面向过程的区别

面向过程 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。 缺点:没有面向对...

无名氏的程序员
23分钟前
12
0
OpenStack大事记

2010年7月19日,Rackspace、NASA、Citrix、Dell共同开发并发布OpenStack第一个版本。 2011年10月,Rackspace放弃OpenStack控制权,交由一新成立的OpenStack基金会管理。 2012年5月NASA宣布退...

大别阿郎
23分钟前
10
0
面向对象的简单应用—以交通工具(Vehicle)为列

请定义一个交通工具(Vehicle)的类其中有: 属性: 速度(speed)、 体积(size)等,方法:移动(move())、设置速度(setSpeed(int speed))、加速 speedUp()、减速 speedDown()等。最后在测试类 Ve...

INEVITABLE
34分钟前
9
0
通往艺术家之路

通往艺术家之路 并发编程中,开发者往往需要权衡锁的颗粒粗细,锁住的代码块太大呢会导致可能的线程堵塞,锁的颗粒太细呢又会因为频繁地加锁解锁导致系统用户态内核态的转换从而消费很多时间...

StupidZhe
35分钟前
24
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部