在前几章的基础上,我们开始介绍最重要的消息对象类模块TLBaseModule。TLBaseModule继承TLBaseObject。TLBaseModule可以说是个软件实现的CPU了,消息就是指令,它可以对指令检测、分析、执行、转发。从前面介绍的类关系图中看到TLBaseModule是最重要的类,所有其他的实用模块都继承它。对象消息编程框架基本就是这个类模板。
一、主要属性
1、protected ArrayList<TLMsg> initMsgTable
存储消息表。在对象初始化的时候自动执行表中的消息。
2、protected HashMap<String, ArrayList<TLMsg>> msgTable;
消息路由表 。Map格式:消息ID—》消息。
对于接收的消息,如果设置了消息ID,则在表中取出对应的消息而执行,一个消息ID可以对应多个消息,因此可以执行多个消息链。
3、protected HashMap<String, ArrayList<TLMsg>> beforeMsgTable
消息前期处理表。Map格式:action-》消息链list
对于接收到的消息,如果没有设置消息ID,则分析消息action,在执行action之前,执行beforeMsgTable里面的action对应的消息链。
4、protected HashMap<String, ArrayList<TLMsg>> afterMsgTable;
消息后期处理表。Map格式:action-》消息链list
在执行action之后,执行afterMsgTable表里面的action对应的消息链。
5、 protected Map<String, Object> modules
模块对象表 。格式:模块名-》模块实例
为了不用每次从工厂取出模块,对于使用过的模块可存在模块对象表里,提高效率。编程时一般不用考虑。
6、protected HashMap<String, String> params;
参数存储表。xml配置文件中的param项目自动存到该属性中,根据情况可设置。
7、protected TLObjectFactory moduleFactory
模块工厂实例。每次应用某个模块对象时,从模块工厂中获得,模块工厂负责模块的创建、配置。
二、主要方法
1、启动、配置
public void start(String configFile ,HashMap<String, String> params){
if(configFile !=null)
this.configFile=configFile;
if(params !=null)
this.params=params;
setConfig();
initProperty();
init();
runInitMsg();
}
该方法一般由模块工厂调用,模块工厂创建一个模块对象后,自动执行该方法,完成模块配置、属性设置、运行初始消息表。
2 处理消息
在TLBaseObject类中,我们实现了基本接口IObject的putMsg方法,在TLBaseModule模块中,主要实现了getMsg方法。getMsg主要完成通用的消息分析、转发、前后期消息处理及一般系统级别的通用方法。
public TLMsg getMsg(Object fromWho, TLMsg msg) {
msg=preGetMsg(fromWho,msg);
TLMsg returnMsg=mget( fromWho, msg);
return afterGetMsg(fromWho,msg,returnMsg);
}
protected TLMsg mget(Object fromWho, TLMsg msg){
TLMsg returnMsg ;
String destination = msg.getDestination();
if (destination != null && !destination.equals(name))
{
TLMsg tranferMsg=createMsg().setAction("msgTransfer").setParam("msg",msg);
msg=tranferMsg;
}
String msgId = msg.getMsgId();
if (msgId == null || msgId.isEmpty()) //检查是否设置msgid
{
returnMsg = doBeforAndAfterMsgTable("before", msg,null);
if (!ifDoNextMsg((TLMsg) returnMsg.getParam(MINFO_PRERESULT)))
return (TLMsg) returnMsg.getParam(MINFO_PRERESULT);
returnMsg= runAction(fromWho, returnMsg);
returnMsg=doBeforAndAfterMsgTable("after", msg,returnMsg);
} else
returnMsg = checkMsgId(msgId, fromWho, msg);
if (!ifDoNextMsg(returnMsg)) return returnMsg;
TLMsg nextMsg = msg.getNextMsg(); //取出param中下一个消息
if (nextMsg != null )
return putMsg(this, nextMsg);
else
return returnMsg;
}
preGetMsg(fromWho,msg) 、afterGetMsg(fromWho,msg,returnMsg)为空方法,如果需要公共的消息前后期处理,可覆盖该方法,如果没有需求,则不用考虑。主要方法为mget( fromWho, msg) 。消息处理流程如下图:
mget方法中过程如下;
首先对消息目的检测,如果目的不是本模块,则转出到对应目的模块。
如果消息设置了消息ID,则转到消息id处理,根据ID在消息路由表 msgTable中取出消息ID对应的消息进行处理、运行。一个消息ID可以对应多条消息。
如果没有设置消息id,则分析消息中的action。根据action执行 消息前期处理表beforeMsgTable。beforeMsgTable中一个action可以对应多条消息。
消息前期处理表运行完毕后,如果结果允许继续运行,则执行分析action的方法runAction(fromWho, returnMsg);
protected TLMsg runAction(Object fromWho, TLMsg msg){
TLMsg returnMsg= systemMsgs(fromWho, msg);
if(returnMsg ==null)
returnMsg = checkMsgAction(fromWho, msg);
return returnMsg;
}
在runAction(fromWho, returnMsg)方法中,首先执行系统定义的通用方法,如消息路由表动态设置、初始化等。
protected TLMsg systemMsgs(Object fromWho, TLMsg msg) {
String action =msg.getAction();
if (action==null)
action="";
TLMsg returnMsg ;
switch (action) {
case SETFACTORY:
moduleFactory= (TLObjectFactory) msg.getParam("moduleFactory");
returnMsg=msg;
break;
case INITACTION:
init();
runInitMsg();
returnMsg=msg;
break;
case INJECTMODULE:
injectModule(msg);
returnMsg=msg;
break;
case ADDMSGTABLE:
if (msgTable == null)
msgTable = new HashMap<>();
addMsgTable(msgTable, "msgId", msg);
returnMsg=msg;
break;
case ADDINITMSG:
addInitMsg(msg);
returnMsg=msg;
break;
case ADDBEFOREMSG:
if (beforeMsgTable == null)
beforeMsgTable = new HashMap<>();
addMsgTable(beforeMsgTable, "action", msg);
returnMsg=msg;
break;
case ADDAFTERMSG:
if (afterMsgTable == null)
afterMsgTable = new HashMap<>();
addMsgTable(afterMsgTable, "action", msg);
returnMsg=msg;
break;
case RELOADCONFIG:
setConfig();
initProperty();
putLog(name+" 配置重新加载",LogLevel.INFO);
returnMsg=msg;
break;
case"msgTransfer":
returnMsg=msgTransfer( fromWho, msg);
if(returnMsg==null)
returnMsg=msg;
break;
case "destroy":
destroy( fromWho, msg);
returnMsg=msg;
break;
default:
returnMsg=null;
}
return returnMsg ;
}
如果action不属于系统action,则执行用户定义的action。在方法中checkMsgAction(fromWho, msg)中执行用户自定义action。checkMsgAction(fromWho, msg)为抽象方法。所有继承TLBaseModule的用户模块所做的工作就是实现checkMsgAction(fromWho, msg)方法。格式如下;
@Override
protected TLMsg checkMsgAction(Object fromWho, TLMsg msg) {
TLMsg returnMsg=null;
switch (msg.getAction()) {
case "startLog":
returnMsg=startLog(fromWho,msg);
break;
case "transferLog":
returnMsg=transferLog(fromWho,msg);
break;
case "startLogOnRun":
returnMsg=startLogOnRun(fromWho,msg);
break;
case "addLogModule" :
returnMsg=addLogModule(fromWho,msg);
break;
case "fromFactoryGetModule" :
returnMsg= fromFactoryGetModule(fromWho,msg);
break;
default:
}
return returnMsg;
}
在checkMsgAction(Object fromWho, TLMsg msg)中,首先取出消息中的action,根据action的值运行对应的内部方法。action的值不是方法名,可以不与方法名一致。不同的action可以对应相同的方法。前面介绍过,每个action都有自己不同的前后期处理,方便编程的灵活性。例如同样是调用登录方法,对应游客发出的action,可以前期检测IP,而管理员发出的action,可不用检测,而这些改变不用修改原模块,在配置文件介绍中,我们可以看到可以灵活的设置前后期消息处理表。在我们后面介绍的数据库分表中,如果需要分表,则不用改变原模块代码,只需在配置文件加入一个前期消息,该消息启动分表模块,分析sql语句的分表字段来实现分表的处理,包括缓存也是这样处理的。下面来看数据库配置文件中一个表的配置
<tables>
<table name="user" dbtable="user" dbserver="dbserver1"
readserver="dbserver2" beforeTrigger="userTableSplitTrigger" />
<table name="userm" dbtable="userm" dbserver="dbserver1" beforeTrigger="cachetrigger"
afterTrigger="aftercachetrigger"/>
<table name="userw" dbtable="userw" dbserver="dbserver2" beforeTrigger="cachetrigger"
afterTrigger="aftercachetrigger" />
</tables>
这个配置中 对于表user,有前触发器beforeTrigger="userTableSplitTrigger" 来分表,这个触发器的实现就是通过action的前期消息处理实现的。如果取消分表,则删除配置即可。user表是个虚表,分成了两个实表 userm和userw,分别位于不同的server上。对于这两个实表,配置了前后触发器cachetrigger、aftercachetrigger用于表的数据缓存。对于分表和缓存,都是通过前后期消息处理实现。对表user的SQL编程处理是透明的,没有更改原模块代码。这里我们没有像Spring那样,通过框架的功能强行改变模块的行为,完全是模块自身灵活的结果。
3、获取模块
在我们的规范中,定义模块由统一创造,因此这里由模块工厂负责对象的实例化、初始化。每个模块需要其他模块的时候,由模块工厂中获取。相关的几个方法如下;
//获取单例模块。本地没有则从工厂中获取,模块名为名称
protected Object getModule(String moduleName)
//从工厂已经存在的模块中获取模块,不创建模块
protected Object getModuleInFactory(String moduleName)
//从工厂获取模块,创建模块
protected Object getModuleFromFactory(String moduleName)
//创建新模块,不工厂注册
protected Object getNewModule(String newModuleName,String moduleName)
//同一个类创建新模块。从本地获取模块,如果没有则从工厂中获取
protected Object getModule(String newModuleName,String moduleName)
这些方法有细微的差别,有些模块是单例模式,有些模块要每次创建新的。默认情况模块是单例模式(配置中可设置模式,消息中也可指定模式),模块创建后存在工厂中,可被多人使用。
这些方法一般不用特意使用,在TLBaseModule中新增加了一个putMsg的多态方法。
protected TLMsg putMsg(String moduleName, TLMsg msg) {
IObject module;
if(msg.getParam("newModule")!=null && msg.getParam("newModule").equals("true"))
{
module= (IObject) getNewModule(moduleName,moduleName);
}
else
module = (IObject) getModule(moduleName);
if(module!=null)
return putMsg(module, msg);
else
return null;
}
我们可以直接给一个模块名称发送消息,在方法内部自动获取模块实例。
4、其他
(1) 继承类都要实现该模块的构造函数,构造函数用于工厂的创建用。如下:
public TLLog(){
super();
}
public TLLog(String name ){
super(name);
}
public TLLog(String name , TLObjectFactory modulefactory){
super(name,modulefactory);
}
(2)在系统消息处理中,有公共的方法,如果动态增加初始消息表、路由表等
(3)模块中自带日志函数
protected void putLog(String content ,LogLevel logLevel){
putLog( content , logLevel, null);
}
protected void putLog(String content ,LogLevel logLevel,String action){
putMsg("log", createMsg().setAction("setLog")
.setParam("module",name)
.setParam("logLevel",logLevel)
.setParam("class",this.getClass())
.setParam("action",action)
.setParam("content",content) );
}
putLog 给log模块发送消息,通用包下有log模块,也可以自定义log模块。包下的log模块是对log4j封装。