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+" 实时创建索引,修改");
}
}
}