现在我们将消息对象框架进行具体应用。前面讲过对于消息编程框架,首先启动模块工厂,由工厂来创建、启动其他模块。实际应用中,我把第一个启动的模块称为appCenter作为主模块,这完全是个人喜好。启动一个固定模块有以下两个原因:
1、可以通过这个模块做程序的初始化。
2、这个模块可以作为一个程序信息调度中心。可以转发其他模块的信息。
当然我们可以通过工厂启动任何模块。
在实际应用中,不同的环境有不同的环境变量,主要是针对配置文件的路径设置。比如我发现android环境、普通java环境、web环境的配置路径都不同。因此实际中,根据环境情况不同定义不同的模块工厂,目的主要还是适应不同的环境。定义的模块工厂继承系统的模块工厂。我没有认真研究对于java是否也许可定义一种适应所有环境的工厂。下面看我写的一个应用包:
cn.tianlong.java包下是具体应用、应用案例。其中servletutils 是web框架 ,servletdemo是针对web框架的demo。tlobjdemo是普通java平台应用,里面的base是根据环境自定义的工厂、模块模板,原因上面讲过。在base包下,我们定义了工厂模块、配置文件模块、通用模块和一个主模块APPcenter。所有模块都是对基本包里模块的继承。如MyModule如下:
public abstract class MyModule extends TLBaseModule {
public MyModule(String name) {
super(name);
}
public MyModule (String name,TLObjectFactory moduleFactory) {
super(name,moduleFactory);
}
@Override
protected void init() {
}
public MyModule() {
super();
}
protected Object setConfig(){
String configdir =Myfactory.getConfigDir();
if(configFile ==null)
configFile=configdir+name+"_config.xml";
super.setConfig();
return mconfig;
}
}
可以看到MyModule就是对标准对象模板的一个简单继承。主要目的是设置配置文件,因为在标准对象模板TLBaseModule中并没有指定文件命名规范。这里定义配置文件名为:模块名_config.xml。其他应用模块继承MyModule后,则自动定义配置文件。
Myfactory也是对标准工厂的一个继承,源码如下:
public class Myfactory extends TLObjectFactory {
protected static String configDir;
private static Myfactory instance;
protected String packageName;
protected ArrayList<TLMsg> factoryBoot ;
private Myfactory(String name) {
super(name);
}
@Override
protected Object setConfig() {
if(configFile==null)
configFile = configDir +"appConfig.xml";
MyAppConfig appConfig = new MyAppConfig(configFile);
appConfig.init(configFile);
modules.put("appConfig", appConfig);
packageName= (String) appConfig.getParams().get("package");
modulesClass = (HashMap) appConfig.getModules();
factoryBoot=appConfig .getFactoryBoot();
doModuleClass();
return null;
}
protected void doModuleClass(){
for (String key : modulesClass.keySet()) {
HashMap<String,String> modules=modulesClass.get(key);
for (String key1 : modules.keySet()) {
if(key1.equals("classfile")){
String classfile=addPackage(modules.get(key1));
modules.put(key1,classfile);
}
else {
if(key1.equals("configfile")){
String configfile =configDir+File.separator+modules.get(key1);
modules.put(key1,configfile);
}
}
}
}
}
protected String addPackage(String name) {
if(name==null)
return null;
String firstCha = (String) name.subSequence(0, 1);
if (firstCha.equals(".")) {
return packageName + name;
} else
return name;
}
public static Myfactory getInstance(String configDir ,String configFile) {
Myfactory.configDir =configDir;
if (instance == null){
instance = new Myfactory(MODULEFACTORY);
if(configFile!=null)
configFile=configDir +configFile;
instance.start(configFile,null);
}
return instance;
}
public void boot() {
doMsgList(factoryBoot,null,null);
}
public static Myfactory getInstance() {
return instance;
}
public static String getConfigDir(){
return configDir;
}
定义的工厂目的是为了根据当前环境而进行相关设置,读取配置。对于模块工厂除了标准的模块属性以外,还自定义了一个属性factoryBoot ,主要放置工厂建立时启动的模块。最开始设计标准工厂模块时,工厂的责任主要是创建模块,它没有初始启动其他模块的责任,在后来涉及多线程的时候,我发现有些组件必须在主线程中启动。当我们用putMsg(模块名,msg)发生消息的时候,如果模块没有启动,则从工厂创建,如果在子线程中创建,对于某些模块将发生错误,如ehcache,数据库连接池。通过factoryBoot启动的模块将在主线程中。
模块工厂定义了程序的主配置文件appConfig.xml。这个配置文件也是主模块APPcenter的配置文件。为了方便 和简化,将模块工厂中的一些配置也放到主配置文件里面。下面来看配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<appConfig>
<params>
<verison value="1"/>
<package value="cn.tianlong.java.tlobjdemo"/>
<poolNumber value="100"/>
</params>
<modules>
<!-- 系统模块 -->
<module name="appCenter" classfile=".person.MyAppCenter" configfile="appConfig.xml"/>
<module name="mconfig" classfile="cn.tianlong.tlobjcet.base.TLModuleConfig" singleton="true" />
<module name="log" classfile="cn.tianlong.tlobjcet.base.TLLog"
configfile="log_config.xml"/>
<module name="monitorConfig" classfile="cn.tianlong.tlobjcet.base.TLMonitorConfigModule"
configfile="monitorConfig_config.xml"/>
<module name="broadcast" classfile="cn.tianlong.tlobjcet.base.TLMsgBroadCast" />
<module name="msgMap" classfile="cn.tianlong.java.servletutils.TLWClientMsgMap" />
<module name="serviceMsgMap" classfile="cn.tianlong.tlobjcet.network.http.server.TLWebServiceMsgMap"
configfile="msgMap_config.xml"/>
<module name="msgbus" classfile="cn.tianlong.tlobjcet.base.TLMsgBus" />
<module name="webServerClient" classfile="cn.tianlong.tlobjcet.network.http.client.TLWebServerClient" />
<module name="HSServer" classfile="cn.tianlong.tlobjcet.network.http.server.HttpServiceServer"
port="8081"/>
<module name="httpClient" classfile="cn.tianlong.tlobjcet.network.http.client.HttpJsonClient"
port="8081" hostname="127.0.0.1"/>
<module name="webSeviceClient" classfile="cn.tianlong.tlobjcet.network.http.client.TLWebSeviceClient"/>
<module name="database" classfile="cn.tianlong.tlobjcet.db.TLDataBase"
ip="127.0.0.1" configfile="database_config.xml"/>
<module name="dbserver" classfile="cn.tianlong.tlobjcet.db.TLDBServer" />
<module name="dbtable" classfile="cn.tianlong.tlobjcet.db.TLTable" />
<module name="dbview" classfile="cn.tianlong.tlobjcet.db.TLDBView" />
<module name="fileCache" classfile="cn.tianlong.tlobjcet.cache.TLFileCache" />
<module name="EhCache" classfile="cn.tianlong.tlobjcet.cache.TLEhcache" />
<module name="cachetrigger" classfile="cn.tianlong.tlobjcet.db.TLDBTriggerForCache" />
<module name="aftercachetrigger" proxyModule="cachetrigger" />
<module name="c3p0Conn" classfile="cn.tianlong.tlobjcet.db.TLC3P0connect" singleton="false" />
<module name="cache" proxyModule="EhCache" inSession="false" configfile="filecache_config.xml"/>
<module name="sessionData" classfile="cn.tianlong.tlobjcet.base.TLSessionData" />
<!-- 用户自定义模块 -->
<module name="house" classfile=".person.House" singleton="true" />
<module name="light" classfile=".person.Light" singleton="ture"/>
<module name="xiaoming" classfile=".person.Person" />
<module name="wife" classfile=".person.Person" />
<module name="userTableSplitTrigger" classfile=".dbdemo.UserTableSplitTrigger" />
<module name="userAfterTrigger" classfile=".dbdemo.userAfterTrigger" />
<module name="serv1C3p0" classfile="cn.tianlong.tlobjcet.db.TLC3P0connect" />
<module name="serv2C3p0" classfile="cn.tianlong.tlobjcet.db.TLC3P0connect" />
<module name="myservice" proxyModule="webSeviceClient" serverAddress1 ="http://localhost:8080/tltest/tlobject/service"/>
<module name="userModle" classfile=".dbdemo.userModle" />
<module name="dbserver1" inSession="true" proxyModule="dbserver" configfile="database_config.xml"/>
<module name="dbserver2" inSession="true" proxyModule="dbserver" configfile="database_config.xml"/>
<module name="myTaskManger" classfile=".task.MyTaskManger" configfile="msgtask_config.xml" />
<module name="dbtask1" classfile=".task.dbtask1" />
<module name="dbtask2" classfile=".task.dbtask1" />
<module name="serviceModle" classfile=".http.serviceModle" />
<module name="userModle" classfile="cn.tianlong.java.servletdemo.userModle" />
<module name="servletDbTest" classfile="cn.tianlong.java.servletdemo.servletDbTest" />
<module name="myServiceAuth" classfile=".http.myServiceAuth" />
</modules>
<msgTable>
<msgid value="setup" >
<msg action="getModule" destination="moduleFactory" moduleName="wife"/>
<msg action="getModule" destination="moduleFactory" moduleName="xiaoming"/>
</msgid>
</msgTable>
<initMsg>
<msg msgid="setup" />
</initMsg>
<factoryBoot>
<factoryBoot>
<msg action="getModule" moduleName="log" usePreReturnMsg="false" />
<msg action="getModule" moduleName="cache" usePreReturnMsg="false" />
<msg action="getModule" moduleName="dbserver1" usePreReturnMsg="false" />
<msg action="getModule" moduleName="dbserver2" usePreReturnMsg="false" />
</factoryBoot>
</factoryBoot>
</appConfig>
这个配置文件与前面介绍的标准模块配置文件基本是一样的,因为APPcenter模块也是标准的消息模块。唯一有个不同是,将模块工厂的配置也加到里面了,仅仅是为了方便。否则单要给模块工厂另建一个文件,当然也是可以的。
配置文件里面的<modules>、 <factoryBoot>两个目录是用于工厂配置,其他项目用于APPcenter模块配置。 <factoryBoot>是可选项,对于不需要工厂初始启动的模块,可以不用。
<modules>是必须有的,所有通过工厂创建的模块都要在此添加。<modules>里的子项很容易理解,模块名字,模块对应的类,模块的配置文件,如果不指定文件则默认是 Myfactory模块中定义的配置文件。模块名字,模块对应的类是必须的,其他为可选的,对于不需要单独配置文件的模块,可将少数参数配置这里。如模块httpClient定义了端口和地址。在 <params>目录下,定义了一个package项,指定当前的包,对当前包下的类,类可用,开始,否则类必须是全名。 当前配置的包为"cn.tianlong.java.tlobjdemo",则模块<module name="userModle" classfile=".dbdemo.userModle" /> 就是当前包下的。
配置文件里面的模块根据自己的需要而定,这个配置文件其他案例也用,因此模块比较烦杂。
singleton 为模块单例设置。默认下模块为单例模式,如果不是单例,既工厂每次创建新模块,则设置singleton=false
usePreReturnMsg用于消息链结果是否采纳。对了消息链处理,后面的消息是否采用前面消息的返回结果,usePreReturnMsg=false 表明不采纳上个消息的结果,否则上个消息的结果将成为下个消息处理的一个参数。
同一个类可以创建不同的模块,如
<module name="serv1C3p0" classfile="cn.tianlong.tlobjcet.db.TLC3P0connect" />
<module name="serv2C3p0" classfile="cn.tianlong.tlobjcet.db.TLC3P0connect" />
也可以用模块代理的方式实现同一类创建不同的模块,如
<module name="dbserver1" inSession="true" proxyModule="dbserver" configfile="database_config.xml"/>
<module name="dbserver2" inSession="true" proxyModule="dbserver" configfile="database_config.xml"/>
dbserver1 、dbserver2 代理dbserver模块。创建dbserver1时,用 dbserver 的 类参数。
以上是普通应用环境的框架简单介绍,我们发现框架很简单,主要是因为我们的许多工作已经在标准模板类中做了。下章介绍根据这个环境开发的案例。