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快速体验 APDPlat入门指南 APDPlat专题文章 APDPlat是Application Product Development Platform(应用级产品开发平台)的缩写。 APDPlat提供了应用容器、多模块架构、代码生成、安装程序、认证...
在APDPlat中,数据库备份恢复的设计与实现是一个关键功能,它旨在简化数据库的维护工作,通过提供web接口支持手工操作和定时调度。以下是该系统的一些核心知识点: 1. **多数据库支持**:APDPlat设计了一个统一的`...
在APDPlat中,这些设计图可能包括类图、包图、用例图、序列图、状态图、活动图等,每种图都有其特定的用途。 1. **类图**:类图展示了系统的静态结构,包括类、接口以及它们之间的关系,如继承、实现和关联。在...
【开源项目案例访谈整理版:APDPlat1】是一个关于APDPlat开源项目的详细介绍,APDPlat是一个专为JAVA开发者设计的应用级产品开发平台。该项目自2008年起由杨尚川发起,至今仍在持续维护,并在2012年4月9日在GitHub上...
3、中文分词算法 之 词典机制性能优化与测试 4、中文分词算法 之 基于词典的正向最小匹配算法 5、中文分词算法 之 基于词典的逆向最小匹配算法 5、Java开源项目cws_evaluation:中文分词器分词效果评估
该平台是一款基于Java的APDPlat应用级产品开发平台,由2902个文件组成,涵盖1750个GIF图像、282个Java源文件、269个PNG图像、187个JavaScript文件、156个CSS样式表、34个JSP页面、31个XML配置文件、30个PSD设计文件...
中文分词词典 适合最大正向匹配算法使用 共计548389条词语
描述中提到的“检测lucene索引库是否正常,测试搜索”意味着该工具的主要功能包括检查索引的结构、元数据和内容,以及执行搜索查询来验证索引的可用性。这可能涉及到对索引的段信息进行检查,确认文档的数量、大小和...
在压缩包文件`ysc-APDPlat-5e7a864`中,我们可以预期找到以下内容: - `app.js`或类似文件,这是EXTJS应用的主要入口点,包含了应用的配置和初始化逻辑。 - `controller`目录,存放了各种控制器文件,比如产品级别...
在给定的代码中,使用了`org.apdplat.word.segmentation.Word`类,表明使用了APDPlat分词库。 2. **构建词频矩阵**:对于每个文本,统计每个词的出现次数,生成一个词频向量。在代码中,`...
4. APDPlat:APDPlat是一个应用级产品开发平台,提供各种模块以加速基于B/S多层架构的信息管理系统开发。 5. ArduBlock:作为Arduino平台的图形化编程工具,ArduBlock降低了非编程人员使用硬件实现创新想法的门槛。...
中文分词,word分词,附件是编译好的word-1.2.jar版本 maven依赖: <groupId>org.apdplat <artifactId>word <version>1.2 </dependency>
##HtmlExtractor是为大规模分布式环境设计的,采用主从架构,主节点负责维护抽取规则,从节点向主节点请求抽取规则,当抽取规则发生变化,主节点主动通知从节点,从而能实现抽取规则变化之后的实时动态生效。...