统一消息对象编程web框架使用简介

更正说明:由于框架后来改动,主启动模块“appCenter”主要用于框架启动,不在作为用户模块,因此本文章中的“appCenter”名称可改为任意用户模块名称,功能不变。本案例中的urlmap缓存配置可取消<afterMsgTable>项,其他不变。——20190102

     在之前的文章中我大概介绍过我的消息编程web框架,代码与流程比较简单,因此本来没有写这个入门的打算。因为今天偶然想了解下springmvc框架,找了个比较实用、详细的文章打算初步了解下应用过程。可是看了不到三分钟内实在无法看下去,因为它太复杂、太繁琐了。它定义了许多必须理解、学习、标准的类,什么handler、拦截器、控制器、moduleview等等,你要是应用它就必须先掌握这些类,而且也必须使用这些类。繁琐的准则和标准无疑会提高人们的学习成本 。框架本身是个工具,工具的特点是帮助人,而不是束缚人,是提高效率,而不是要花更大的成本去学习。想到这,我有了写这个消息对象编程web框架使用入门想法。现在我们看消息框架如何使用的。

    在我的消息框架里没有定义什么控制类、拦截类的,也就是没有基于功能的对象区分,你可以任意根据自己的需求去写,用在任意地方。前提是你的类继承消息对象基本模块TLBaseModule(或抽象类接口 IObjcec)就行。

   现在我们一步步的讲解如何三分钟实现一个简单的页面:hello 统一消息对象!

第一步、url到模块的映射

      这在一般mvc框架中为urldispatch,将url映射或分发到控制器。,我们当然可以实现自动的解析url映射,但是自动缺乏灵活性。在我的框架中,映射通过手工配置,这样控制灵活,比如不同的url映射到一个模块,而参数还不同,或者随时可取消、更改一个映射。我们来看具体配置 。实现url转换的模块是TLWUrlMap,它的配置文件是 urlmMap_config.xml。在我的设计中,每个模块单独自己的配置文件,这样配置文件会很多,但调理清晰,多不等于复杂。

<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
    <params>
        <verison value="1"/>
        <defaultClientUser value="webUser"/>
    </params>
    <url-mapping>
        <url value="/*">
            <msg destination="appCenter" default="index" index="index" />
        </url> 
        <url value="/index">
            <msg destination="appCenter" action="index"/>
        </url>
        <url value="/hello">
            <msg destination="appCenter" action="index"/>
        </url>
    </url-mapping>
</moduleConfig>

上面是配置文件 ,对于url-mapping下的每一项是一个url映射,因为我们是消息编程,每个url是映射到一个消息,这个消息自身包含了具体的模块和方法,例如

<url value="/index">
    <msg destination="appCenter" action="index" />
</url>

  这表示 url为/index 映射到目的模块为appCenter 、方法为index 的消息,urlmap模块将执行这个消息。为表示灵活性,上面还定义了/hello 也映射到同一消息,也就是说/index ,/hello 执行的是同一个模块。

urlmap映射的查询顺序是首先根据url全径检索,如果没有定义这个url的具体映射,则到/* 下检索。对于映射:

<url value="/*">
    <msg destination="appCenter" default="index" index="index" />
</url>

定义该目录下的映射关系,该目录所有的url统一映射到appCenter模块,默认是 index方法(如果没有输入路径),其中/index映射到index方法,如果在该目录下再加一个 url ,例如/user 要映射到usermodule方法,那我们可以定义映射 user="usermodule"。

对于配置中检索不到的url映射,框架输出无此url,不在继续执行。

定义完映射后,我们就可以编写对应的映射模块了。做这个配置半分钟足够了。

第二步、编写对应的应用模块

     上面url映射到appCenter模块中,我们现在编写模块实现。“appcenter ”为模块名字,具体的类名可以任意,只要我们在工厂配置中指定即可,我们定义qqWebCenter类。如果模块需要输入与输出,那么要继承TLWServModule,该对象封装了一些输入、输出方法,方便使用 。这里TLWAPPCenter也是继承TLWServModule。

public class qqWebCenter extends TLWAPPCenter {
    public qqWebCenter(){
        super();
    }
    public qqWebCenter(String name ){
        super(name);
    }
    public qqWebCenter(String name , TLObjectFactory modulefactory){
        super(name,modulefactory);
    }
    @Override
    protected TLMsg mycheckMsgAction(Object fromWho, TLMsg msg) {
        TLMsg returnMsg=null;
        switch (msg.getAction()) {
            case "index":
                index(fromWho,msg);
                break;
            default:
                putMsg("error",creatOutMsg().setAction("setError").setParam("content","no action"));
        }
        return returnMsg ;
    }

    protected void index(Object fromWho, TLMsg msg) {
        outData odata =  creatOutDataMsg("index");
        odata.addData("message","hello 统一消息编程!");
       putOutData(odata);
    }

}

       在qqWebCenter类中,我们定义了url映射到的方法 index(),在index方法中:

       首先我们创建了输出数据对象 odata  ,然后给这输出数据添加数据,也就是我们要输出的内容。最后输出数据。

      数据如何输出的呢,这里面没有体现,也没有体现对应的html模板,因为如何输出数据是输出接口的责任。我看许多框架,在模块方法里定义view模板,我认为逻辑是错误的。先不说模板写在代码里就太僵化,主要是数据模板与数据是对应关系,而不能与处理模块的方法成为对应关系。后面我们可以看到随着更换输出接口而产生不同的输出格式。现在数据如何与模板对应的呢,在上面创建数据对象的时候:

outData odata =  creatOutDataMsg("index");

  传入的参数“index” 为数据标识,或称为数据id,通过这个数据id来实现与模板的对应,数据id可任意定义。实现中,为保持数据唯一,数据Id默认是模块的名字及传入的参数组合,因此上面代码隐含定义了该数据的标识id为 :appCenter.index。

这个模块方法用了三条语句,一分钟吧。

第三步、配置模板

     完成了模块编码、用户数据,现在最后一步——输出模板。目前我通过封装velocity来输出页面。为什么用它来做输出接口?因为我还没有时间学习其他的,它可以就用么。因为输出接口是独立的,因此我们可以包装、配置任何输出接口。

   我们先编写模板,模板遵守velocity语法,这里就输出一个变量,因此模板很简单,模板起名appCenter.index.vm,名字与数据id一致是为了方便。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
$message
</body>
</html>

然后我们配置数据与模板的对应,在velocity的配置文件中velocity_config.xml(包装后的velocity接口配置文件)定义数据与模板的对应关系:

<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
    <params>
        <resource.loader    value="file"/>
        <input.encoding value="UTF-8"/>
        <output.encoding value="UTF-8"/>
        <resource.loader    value="file"/>
        <file.resource.loader.class value="org.apache.velocity.runtime.resource.loader.FileResourceLoader"/>
    </params>
    <templates>
        <dataid name="appCenter.index" template="appCenter.index.vm" />
    </templates>
</moduleConfig>
项目 <dataid name="appCenter.index" template="appCenter.index.vm" /> 指明数据appCenter.index对应的模板是 appCenter.index.vm。

 这个html模板和增加一条配置1分钟吧。

第四步、配置模块工厂

    在第二步时候我们编写了模块,模块需要加到模块工厂里才能被使用,在工厂配置文件中 增加一项:

<module name="appCenter"  classfile=".application.qinqin.web.qqWebCenter" configfile="appConfig.xml"/>

该项指出appCenter对应的类,模块工厂会根据调用自动创建。注:这里我们用的是框架启动主模块appCenter,这是框架必备的。url不一定要映射到启动主模块里,任何模块都可以。

 仅仅增加一行配置,半分钟。

好了,我们来看输出结果:

我们没有定义继承什么控制类、view类 ,也没有需要任何servlet、jsg代码,三分钟轻松实现了。

现在我们在进一步!

上面页面没有用户输入,现在我们提供一个用户输入 username ,页面输出:username 说 “hello 统一消息对象!”。

为简便,我们不在写提交页面,直接在url里写上用户变量 /index?username=xiaoming 。

对于用户输入变量的获得 可以用以下两个方式:

1、我们在模块里可主动获取     

  String username =  getUserData("username");

2、我们在urlmap配置里面定义变量,由框架自动获取。

我们现选择第二种,在配置里面定义,修改urlmap配置

        <url value="/index">
            <msg destination="appCenter" action="index" clientVars="username" />
        </url>

在url 映射消息中增加一个项ClientVars,定义用户输入的变量名,如果多个变量,则用分号;间隔。

修改模块方法增加获取变量代码:

    protected void index(Object fromWho, TLMsg msg) {
        String username = (String) msg.getParam("username");
        outData odata =  creatOutDataMsg("index");
        odata.addData("username",username);
        odata.addData("message","hello 统一消息编程!");
       putOutData(odata);
    }

 通过urlmap自动获得的变量,自动添加到函数参数 msg中,直接提取即可。

最后修改模板增加username变量:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
$username 说:$message
</body>
</html>

现在我们看输出结果:

非常完美,轻松实现了!

革命继续!

我们现在用的html页面输出,要改成json格式输出,因为公司开发出了手机端app,传输数据是json。前面我们说过输出是接口的事情,与数据模块无关,因此我们更改接口就可,不用更改任何代码,更改urlmap配置:

 <url value="/index">
            <msg destination="appCenter" action="index" clientVars="username" clientUser="tokenUser" />
        </url>

在配置中 <defaultClientUser value="webUser"/>定义默认用户类型。在上面url映射中,我们增加了一个 clientUser项,告诉该url对应的用户是tokenUser类型,从而改变了用户类型,在模块工厂中 tokenUser类型对应的client接口是json输出,现在我们看结果:

很神奇,没有更改任何代码,输出自动为json格式了。

我们再继续下!

回到开始的html页面,根据业务发展流量增大,页面需要缓存。在原模块代码中加缓存要改代码,不仅麻烦,而且违反业务逻辑。我们还是通过更改配置项来实现。

<?xml version="1.0" encoding="UTF-8" ?>
<moduleConfig>
    <params>
        <verison value="1"/>
        <defaultClientUser value="webUser"/>
    </params>
    <params>
        <verison value="1"/>
        <defaultClientUser value="webUser"/>
    </params>
    <beforeMsgTable>
        <action value="toServlet">
            <msg action="getCacheBeforeUrlMap" destination="servletCache" useInputMsg="false"  usePreReturnMsg="false"></msg>
        </action>
    </beforeMsgTable>
    <afterMsgTable>
        <action value="toServlet">
            <msg action="writeCacheAfterUrlMap" destination="servletCache" useInputMsg="false"  usePreReturnMsg="false"></msg>
        </action>
    </afterMsgTable>
    <url-mapping>
        <url value="/*">
            <msg destination="appCenter" default="index" index="index" />
        </url>      
        <url value="/index">
            <msg destination="appCenter" action="index" clientVars="username"/>
        </url>
        <url value="/hello">
            <msg destination="appCenter" action="index"/>
        </url>
    </url-mapping>
</moduleConfig>

 在urlmap配置中,我们增加了beforeMsgTable、afterMsgTable 两项,意思是在执行模块方法前、后,先执行该项的消息。相当于springmvc的拦截器。beforeMsgTable 用于读缓存,afterMsgTable用于写缓存。在消息目的模块 servletCache的配置文件中,我们配置模块对应的缓存项:

<?xml version="1.0" encoding="UTF-8" ?>
<params>
    <verison value="1"/>
    <cacheClass value="fileCache" />
</params>
<moduleConfig>
       <modulesForCache>
        <module  name="appCenter" index="appCenter.index"/>    
    </modulesForCache>
    <caches>
        <cache name="appCenter.index" cacheKey="index" cacheValueName="content" valueType="string" exptime="180" />   
    </caches>
</moduleConfig>

modulesForCache 项中,name指明要缓存的模块  ,index="appCenter.index"  定义方法 index对应的缓存名字

在caches项中,是对缓存具体的配置,定义了缓存的名称、缓存值的类型、过期时间。

在params项中,cacheClass定义了所用缓存工具为fileCache,为文件缓存。我们可以更改为其他缓存,如ehache。目前我只包装了这两个缓存工具。

下面我们看实现,刷新两次页面,看输出日志:

第一次访问,框架调用了 velocity 模板接口velocityOutInterface输出页面。

第二次访问时启动servletCache,直接读取缓存 输出,输出接口是 directOutInterface。通过缓存输出时间缩小的到6毫秒。第一次输出为107毫秒,一方面是没有缓存,另一方面还是模块第一次实例化。第二次访问时,模块已经在内存实例化。

增加、取消缓存更改配置即可。而更改配置无需重新启动server,所有的配置动态更新,在前面文章我阐述过。

  现在我们总结下消息框架的开发过程:

1、映射url

     在在映射中自动提取输入变量,设置用户类型,各种前后拦截(所有模块都可以)

2、编写处理模块     

3、编写模板,如果需要。

对比springmvc ,没有必须继承、使用的类、遵守的规则,我们无需过于的学习框架本身。同时开发快捷,配置灵活。

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值