统一对象消息编程(13)—对象消息编程框架应用4

现在我们在大致分析db案例。在dbdemo包下是测试数据库的demo。我们看入口类Main代码:

public class Main extends TLBaseModule {
    static long startTime;
    private static boolean resultflag = false;
    private static int i = 0;

    public Main(String main, TLObjectFactory myfactory) {
        super(main, myfactory);
    }
    public static void main(String[] args) {
        TLMsg returnmsg = null;
        String configdir = System.getProperty("user.dir") + "\\config\\person\\";
        Myfactory myfactory = Myfactory.getInstance(configdir,null);
        myfactory.boot();
        Main m = new Main("main", (TLObjectFactory) myfactory);
        m.registInfactory(myfactory);
        TLMsg tmsg = new TLMsg().setAction("getTable")
                .setParam("tableName", "user");
        returnmsg = m.putMsg("database", tmsg);
        TLTable tb = (TLTable) returnmsg.getParam(TLObjectFactory.FACTORY_MODULEINSTANCE);
   //  m.insert(tb);
      m.query(tb);
        // serverstart.i=0;
        //   m.testview(dataBase);
    //   m.batch(tb);
   //    m.find(tb);
     //   m.delete(tb);
        // m.total(tb);
        //  m.findall(tb);
        //  m.userModle();
    }

为节省篇幅,上面是入口Main函数。这个main函数与前面介绍的startserver的基本类似。先初始化模块工厂,工厂boot。这时候一个不同是,没有启动appCenter,而是直接获得了一个表模块,代码如下:

TLMsg tmsg = new TLMsg().setAction("getTable")
        .setParam("tableName", "user");
returnmsg = m.putMsg("database", tmsg);
TLTable tb = (TLTable) returnmsg.getParam(TLObjectFactory.FACTORY_MODULEINSTANCE);

我们说过appCenter也是一个普通模块,启动它的目的是为了某些程序初始化。如果不需要初始化可以不用启动的。这里就是直接给database 发送一个消息 ,消息的内容是获得一个表名为user的表模块。我们给database发送消息,之前并没有创立database模块,这都是自动完成的。如果工厂发现database模块已经存在就直接使用,如果没有则创建,对模块使用者是透明的。获得表后,我们就可以对这表进行各种操作,下面就是表的各种操作测试:

//  m.insert(tb);
   m.query(tb);
  // serverstart.i=0;
   // m.testview(dataBase);
  // m.batch(tb);
 //  m.find(tb);
 // m.delete(tb);
   //  m.total(tb);
   //  m.findall(tb);
   //  m.userModle();

下面我们看其中的query(tb),这个函数是调用queryfunc来完成查询的:

private void queryfunc(TLTable tb, int number) {
    startTime = System.currentTimeMillis();
    String sql = "select * from  table  where  name like ?";
    LinkedHashMap<String, Object> sqlparams = new LinkedHashMap<>();
 //   sqlparams.put("number", number);
    sqlparams.put("name", "%dong%");
    TLMsg querymsg = new TLMsg().setAction("query")
            .setParam("sql", sql)
            .setParam("resultType", "arrayList")
            .setParam("params", sqlparams)
            .setParam("isWait", "false") //异步多线程
            .setParam("resultFor", this)
            .setParam("resultAction", "getResult");
 //   querymsg.setParam("cacheName", "table_user");
 //   querymsg.setParam("cacheKey", "" + number);
   TLMsg returnMsg= putMsg(tb, querymsg);
}

我们的表TLTable是对apache的 dbutils封装,sql操作语法就是dbutils的语法。首先定义Sql语句,定义查询参数,然后创建查询消息querymsg,最后把查询消息发送给表:putMsg(tb, querymsg)。但这里没有返回结果处理,为什么呢?因为我们在查询消息中定义了异步查询:.setParam("isWait", "false") ,这告诉表,我的查询是异步的,我不等你结果了,你的结果返回给"resultFor" 定义的模块,这里设置的是自己this,返回的结果用"resultAction"定义的行为处理,这里定义的是"getResult",我们看看getResult方法:

private void getResult(Object fromWho, TLMsg msg) {
    List datas = (List) msg.getParam("result");
    if (datas.isEmpty()) {
        System.out.println("getResult 返回没有数据");
        return;
    }
    System.out.println("getResult 返回 数据----------------");
    for (int i = 0; i < datas.size(); i++) {
        Object[] unit = (Object[]) datas.get(i);
        for (int j = 0; j < unit.length; j++) {
           System.out.print(this.i + "  " + unit[j] + "  ");
            this.i++;
        }
        System.out.println("");
    }
    Long nowTime = System.currentTimeMillis();
    Long runtime = nowTime - startTime;
    System.out.println("getResult 运行时间:" + runtime);
}

getResult方法接收到表返回来的结果,对结果的处理。我们看看运行结果:

我们看到getResult收到了结果并打印输出。这里发现getResult调用了两次,为什么呢?因为我们的查询语句 String sql = "select * from table where name like ?";  其中的查询条件name 不是表的分表字段,所以查询的时候对所有表都查询了,看日志输出:

信息: 模块: userm 方法:query 内容:select * from  userm  where  name like ? 进程id: Thread-2
八月 08, 2018 11:21:34 上午 cn.tianlong.tlobjcet.base.TLLog setLog0
信息: 模块: userw 方法:query 内容:select * from  userw  where  name like ? 进程id: Thread-1

我们发出的是一个对user表的查询,由于user表分为两个表userm、userw,因此通过分表触发器发出两个对实际表的查询 。我们开始指定了查询为异步,因此两个查询在不同的线程里同时查询。查询结果都返回到getResult。因此我们看到getResult被调用了两次。这里user表分表字段是number ,number 小于100的在userw表,大于100的在userm表。如果直接通过number查询,则分表触发器可直接指定查询表了,不用所有的表都查询。我们看分表触发器UserTableSplitTrigger设计:

public  class UserTableSplitTrigger extends TLDBBaseTriggerForSplitTable {

    public UserTableSplitTrigger() {
        super();
    }
    public UserTableSplitTrigger(String name ) {
        super(name);
    }
    public UserTableSplitTrigger(String name , TLObjectFactory modulefactory){
        super(name,modulefactory);
    }

    @Override
    protected TLMsg selectTable(Object fromWho,TLMsg msg){
        TLMsg nmsg=(TLMsg) msg.getParam(DOWITHMAG);
        String isWait = (String) nmsg.getParam("isWait");
        if(isWait!=null && isWait.equals("false"))
            nmsg.setWaitFlag(false);
        LinkedHashMap<String ,Object> tparams= (LinkedHashMap<String, Object>) nmsg.getParam("params");
        if(tparams==null ||tparams.get("number")==null)
        {
            ArrayList<Object> totaldatas=new ArrayList<>();
            TLMsg wreturn =changeTable("userw",nmsg);
            TLMsg mreturn= changeTable("userm",nmsg);
            List mdatas;
            if(mreturn!=null && mreturn.getParam("result")!=null)
            {
                mdatas = (List) mreturn.getParam("result");
                totaldatas.addAll(mdatas);
            }
            List wdatas ;
            if(wreturn!=null && (List) wreturn.getParam("result")!=null)
            {
                wdatas = (List) wreturn.getParam("result");
                totaldatas.addAll(wdatas);
            }
           return createMsg().setParam("result",totaldatas).setParam(DONEXTMSG,"false");
        }
        else
        {
            int numb= (int) tparams.get("number");
            if(numb==0)
                return null ;
            if(numb >=100)
                return changeTable("userm",nmsg);
            else
                return changeTable("userw",nmsg);
        }
    }

}

其中根据numb值改变表,如果sql中没有设置numb,则对所有表查询。这分表触发器是如何作用到user表上的呢,我们看数据库的配置database_config.xml中:

<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>

对于表设置了前操作触发器 beforeTrigger="userTableSplitTrigger" ,在表操作前首先执行这个触发器, afterTrigger="aftercachetrigger" 表示表操作后执行该触发器。我们看配置文件里面的触发器配置目录:

<triggers>
    <trigger name="userTableSplitTrigger"   delete="onDelete"  insert="onInsert" query="onQuery"  update="onUpdate" />
    <trigger name="cachetrigger"    query="getCache"  />
    <trigger name="aftercachetrigger"    query="writeCache" delete="deleteCache" update="deleteCache" />
</triggers>

    定义了触发器在表哪个操作上执行。userTableSplitTrigger在所有操作上都执行。query="onQuery" 意思是当表执行query行为时,先执行触发器的onQuery行为。

对于触发器cachetrigger ,仅仅定义了当表执行query的时候 执行getCache行为。对于aftercachetrigger触发器,当表查询、更新后执行,目的是写或更新缓存。

这里看到对表user的分表、缓存都是通过配置设定的,没有更改表TLTable的代码,对TLTable表是透明的。如果取消分表或缓存,那么从配置文件中删掉配置就可以了。从这里看到程序是模块的自由组合,没有互相的依赖。

那么这些触发器是如何作用表上的呢,前面知道,我们通过数据库database来获得一个表的,那么看数据库是如何创建一个表的,在通用包db包下TLDataBase的模块: 

private TLMsg getTable(Object fromWho, TLMsg msg) {
    String tablename= (String) msg.getParam("tableName");
    TLTable tableobj= (TLTable) dbObjs.get(tablename);
    if(tableobj==null)
    {
        tableobj=createTable(tablename);
        dbObjs.put(tablename,tableobj);
        HashMap<String, String> tableparams=tables.get(tablename);
        addTrigger(  tableobj,tableparams);
    }
    return createMsg().setParam(INSTANCE,tableobj);
}

数据库首先看表模块存在不,不存在则创建一个新表模块,然后对这个表加上触发器 addTrigger操作,代码:

private void addTrigger(TLBaseModule tbobj, HashMap<String,String>viewparams){
    if(viewparams==null)
        return ;
    for(String key:triggerName.keySet()){
        String triggersStr=viewparams.get(key);
        if(triggersStr!=null && !triggersStr.isEmpty())
        {
            String[] triggersNames=triggersStr.split(";");
            HashMap<String ,String>tparams;
            for(int n=0 ;n<triggersNames.length;n++)
            {
                tparams=triggers.get(triggersNames[n]);
                addTriggerMstTable(tbobj,triggersNames[n],triggerName.get(key),tparams);
            }
        }
    }
}
private void addTriggerMstTable(TLBaseModule tbobj,String triggerName,String type, HashMap<String ,String>tparams){
   String[] actions={"delete","update","insert","query","batch"};
   for(int i=0 ; i< actions.length ;i++){
       if(tparams.get(actions[i])!=null)
       {
           insertTriggerMsgTable(tbobj,triggerName,type,actions[i],tparams.get(actions[i])) ;
       }
   }
 }
private void insertTriggerMsgTable(TLBaseModule tbobj,String triggerName,String type,String onAction,String trigAction){
    TLMsg trmsg=new TLMsg().setDestination(triggerName)
            .setAction(trigAction);
    TLMsg bmsg=createMsg().setAction(type).setParam("action",onAction)
            .setParam("msg",trmsg);
    putMsg(tbobj,bmsg);
}

最后在insertTriggerMsgTable里,给表发出加触发器的消息,这里面type定义了模块行为,我们看type是如何定义的,在database的初始化中init中:

@Override
protected void init() {
   triggerName.put("beforeTrigger",ADDBEFOREMSG);
    triggerName.put("afterTrigger",ADDAFTERMSG);
}

从这里就看到了,根据触发器的类型,给表模块发送不同的消息ADDBEFOREMSG或ADDAFTERMSG。表是一个标准TLBaseModule模块,我们看标准模块是如何处理这两个消息,在系统消息处理方法systemMsgs中:

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;

从这里就看到了,前触发器就是模块的前期消息处理表,后期触发器就是消息后期处理表。

      至此为止,通过表触发器设置这个案例,我们看到了通过标准模块的功能,可以灵活组件模块、更变程序行为。没有通过复杂java语句,比如反射、注解之类来实现。老实说,我对java语言也是刚接触不深,可以说边学边写,很多常用的函数也记不住,基本是百度,包括现在map遍历还每次百度,对于反射、注解也没有深入了解,所有代码中只有工厂创建模块的时候用了基本反射。我觉得注解的发明完全是只考虑了编程的方便,而忽视了对象的独立、完整性,内心就对它排斥导致看不下去。我我一看spring的那些注解就头疼,所以没兴趣学下去。任何一种框架、工具的目的都是为了解脱复杂工作、提高效率,如果一个工具需要一本厚厚的书才能懂得如何用,那么这个工具基本属于失败的。试想如果手机、汽车都配备一本厚厚的书让大家学习如何使用,那会怎样。我们的消息对象框架中,其实重点就一个类TLBaseModule,熟悉、使用非常容易。可以用在任何情况。如果你的对象不希望别人别对它动手动脚,那么继承它就行。然后告诉对方,只要告诉我干什么,我自己做,其他的你别管,当然他也管不了。写到这顺便唠叨下。

猜你喜欢

转载自blog.csdn.net/tianlong117/article/details/81503448