Jspx.net构架说明




作者:陈原   mail:cayu@gzec.com.cn   QQ:39793751    home: http://www.jspx.net

1.简介
天下没有完美的东西,难免会有一些遗憾。在软件的构架世界里也同样.微软.net的构架,使用简单方便,得利于统一和通用,这也是垄断的好处。而java的构架太多了到目前也不统一,算是百家争鸣的时候。有apache的,有sun的,在开源社区也有不少,我就不讨论了,选择适合自己,适合项目的就好。重要的是明白各个构架的设计思路,和优缺点才能够使用的得心应手,下边说一下我设计的这个构架。

2.jsp页面代码结构回想

    jsp出现的时候,我们想写一个动态有提交事件的页面我们会如何写呢?a方法.一个页面上使用html代码写一个form,然后在提交到另外一个页面。b方法.自己提交到自己页面,使用String submit = request.getParameter("submit");然后用submit变量来判断是否点击了提交。
    从简洁方便的角度来看b方法是比较好的。代码例子如下。


    <%@ page contentType="text/html; charset=gbk" language="java"%>
    <%
        String SubmitGoKind = request.getParameter("SubmitGoKind");
        String SubmitCopy = request.getParameter("SubmitCopy");
        String Submitdel = request.getParameter("Submitdel");
        //>---------------------------move

        if (SubmitGoKind != null)
        {
            //操作转向

        }
        //<---------------------------move

        //<---------------------------copy

        if (SubmitCopy != null)
        {
            //操作拷贝

        }
        //<---------------------------copy

        //>---------------------------delete

        if (Submitdel != null)
        {
            //操作删除

        }
        //<---------------------------delete


    %>
    <html>
    <body>
    <form id="form1" name="form1" method="post" action="?">
      <input type="submit" name="SubmitGoKind" value="转向" />
       <input type="submit" name="SubmitCopy" value="拷贝" />
       <input type="submit" name="Submitdel" value="删除" />
        <!--其他显示内容-->
    </form>
    </body>
    </html>


3.webwork2对构架的改变

    上边的代码结构我jsp页面开发时代我常使用。感觉是比较方便的了。struts随后出现改变了这种代码结构,但struts的标签太多,增加了程序的书写难度,也加大了开发时间,好处是代码有了一定的规范了,也实现了MVC,本人感觉比较得不偿失。但webwork2的出现带来了web构架的革新。bean封装后在使用webwork标签输出到页面,xml配置统一转向。 webwork如今也越来越强大。
    spring也是比较主流的一个构架,不但包括了web构架,还融合了各种接口。 我使用webwork2+spring的方式开发了几个项目。淡一个个人感受。

    1.spring的web构架部分不如webwork2的好。所以才有很多人使用webwork2+spring的方式,如果不使用webwork2,spring完全自己能独立的完成webwork2的很多工作。spring最常用的是ioc部分。
      spring的其他功能,一般在比较复杂的应用中才会用到,spring打包成很多的jar你使用那部分就安装那个部分。spring和他的粉丝们说他的功能很多,但本人感觉有点如同鸡肋食之无味,弃之可惜。

    2.webwork2的强大替代了早期的struts成为struts2,几乎成为了java在web构架部分的标准。但他还没有内置在jdk的安装包里边,所以struts2的缺点就是struts2太强大。里边什么七七八八的东西都有。
    比如一个上传,里边就支持很多个上传组件。表面上是你喜欢那个就使用那个。但实际开发中我们只需要一个完美的没有问题的上传组件就够了。还有ajax部分,ajax是一个越简单越小越好的东西。
    struts2却放入了dojo也不问问,别人喜欢不喜欢。dojo是一个js的web UI配合ajax能够得到美观的,功能强大的用户见面,提高用户使用的感受,但dojo最大的问题是慢,慢得老火。这种东西如果在内网或者
    网速快的地方用那是很好,但在中国好像网络还没快到这个地步,如果是简单的小网站,租用的空间,那不是慢得要死。当然你可以不使用dojo部分。这里不使用那里不使用,就成为了一种包袱。安装的时候
    太大。而我认为构架应该短小精干一些比较好。

4.jspx.net设计目的
      本人做的几个项目自己感觉spring+struts2的确能够为我们解决不少问题,功能是够强,但包也太多,站空间也太大了。所以我一直想简化一下struts2.
抛弃spring中不使用的那些orm接口。
   要实现的有一下几点.
1.运行速度,目前到2.1版本后速度已经不是问题。
2.简化不需要的和不常用的功能,同时要方便,有必要的时能够方便的加入。那些没必要的包就不要加入了。
3.简化标签来做web界面,多了太难记了。使用默认使用freemaker模版语言就可以了,可以使用其他的模版语言,但现在不提供支持。感觉没必要一个页面用几种语言来写。除非另外的模版语言的优秀程度在一定程度上超过了freemaker。
4.使用UTF-8为默认编码,这样实现多语言支持也比较方便。
5.上传部分使用cos修改版本。内置这个版本上传组件支持缓存,多编码,限制大小,是否覆盖,ajax上传状态返回,而且很稳定,够用了吧!
6.程序构架模仿spring+struts2这样比较容易学习。
7.orm部分简单化,hibernate功能太多,以及映射问题造成应用陷阱。sober不做缓存处理。只是一个jdbc扩展适配器功能,主要简化参数传递。
8.连接池部分内置一个简单连接池,处理mysql的8小时问题,并且能够支持高并发。
9.xml配置分两部分ioc配置和web转向配置。web转向配置支持不配置方式(类似asp,php的转向方式)。
设计主要针对快速开发,和成品型软件,比如论坛,新闻系统,和行业软件设计,也可以应用在应用软件上。
10.目前本构架已经平台化了。从构架初始化到数据库支持和页面的产生,可以说,数据库类别的网站和应用软件都很够用了。


5.设计思路
总体设计还是MVC构架,将程序分成View,Action,DAO 和 显示模版页面部分。xwork是struct2的底层,主要模仿了xwork的事件处理.并提供了拦截器,验证部分在拦截器中实现,保证系统的安全性。

1.Bean数据实例对象。接口必须为 Serializable主要考虑到其他使用的时候方便扩展。


//Sober会更具◎标签自动创建表
@Table(name = "数据库表名称", caption = "描述信息")
public class Bean implements Serializable
{
    @Id(auto = true,type="serial")
    @Column(caption = "ID号", length = 50, notNull = true)
    private String id;

    @Column(caption = "标题", length = 4, option = "0:普通;1:投票;2:辩论;3:活动", valid="inArray[0,1,2,3]",notNull = true, defaultValue = "0")
    private int threadType = 0;

    @Column(caption = "标题", length = 200, notNull = true)
    private String title = "";


    public String getId()
    {
        return id;
    }

    public void setId(String id)
    {
        this.id = id;
    }

    public int getThreadType()
    {
        return threadType;
    }

    public void setThreadType(int threadType)
    {
        this.threadType = threadType;
    }

    public String getTitle()
    {
        return title;
    }

    public void setTitle(String title)
    {
        this.title = title;
    }

}

2.View提供动态数据显示功能,为了代码清晰view部分只完成查询输出的部分。


package com.jspx.example;

import com.jspx.txweb.support.TemplateSupport;
import java.util.List;
import java.util.ArrayList;

public class HelloListAction extends TemplateSupport
{

    public HelloListAction()
    {

    }
    //这里可以提供给模版调用从而实现模版和程序的交互
    public List<String> getList()
    {
        List<String> list = new ArrayList<String>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        list.add("five");
        list.add("six");
        list.add("seven");
        list.add("eight");
        list.add("nine");
        list.add("ten");
        return list;
    }
    //如何你在配置配置了执行方法,先执行你的执行方法在执行execute

    public String execute() throws Exception
    {
        return SUCCESS;
    }
}

2.Action提供处理客户发出的请求和动作,为了代码简洁推荐继承相应的View这样可以降低代码量。
可以看出这一个类实现了FriendLink一般情况需要处理的所以动作。
@Operate就相当早期使用的  request.getParameter("submit") != null;来判断是否运行函数。


package jspx.jcms.admin;

import com.jspx.txweb.annotation.Validate;
import com.jspx.txweb.annotation.Operate;
import com.jspx.validator.impl.ValidatorDataType;
import com.jspx.utils.RequestUtil;
import com.jspx.utils.StringUtil;
import com.jspx.sober.criteria.expression.Expression;
import jspx.jcms.env.JcmsIoc;
import jspx.jcms.view.FriendLinkView;
import jspx.service.friedlink.FriendLink;
import jspx.user.table.UserSession;

/**
 * 下边演示的是FriendLink实现的添加,删除,编辑,排序
 × @标签将在后边说明,这里只是大体构架
 */

public class FriendLinkManageAction extends FriendLinkView
{

    public FriendLinkManageAction()
    {

    }


    //校验infoType为检查后保存到FieldInfo中的信息类型

    //dataTypeValidator为sioc配置 formId 为检验配置的ID,

    //提交表单中 post 不为空才运行校验

    @Validate(infoType = ValidatorDataType.error, name = "dataTypeValidator", formId = "addfriendlinkForm", submit = "submit")
    //添加

    @Operate(submit = "submit")
    public void save() throws Exception
    {
        FriendLink friendLink = (FriendLink) getBean(FriendLink.class);
        if (friendLinkDAO.save(friendLink) != null)
        {
            addActionMessage("保存成功");
        } else
        {
            addActionMessage("保存失败");
        }
    }

    //提交表单中 post 不为空才运行校验

    @Validate(infoType = ValidatorDataType.error, name = "dataTypeValidator", formId = "updatefriendlinkForm", submit = "submit")
    //编辑

    @Operate(submit = "submit")
    public void update() throws Exception
    {
        FriendLink friendLink = (FriendLink) getBean(FriendLink.class);
        friendLink.setNamespace(JcmsIoc.namespace);
        UserSession userSession = (UserSession) getUserSession();
        friendLink.setPutman(userSession.getManId());
        if (friendLinkDAO.update(friendLink))
        {
            addActionMessage("保存成功");
        } else
        {
            addActionMessage("保存失败");
        }
    }


    //校验infoType为检查后保存到FieldInfo中的信息类型

    //删除,多选方式

    @Operate(submit = "operation", operation = "delete")
    public void delete() throws Exception
    {
        String[] ids = getArray("id", true);
        if (ids == null || ids.length < 1)
        {
            addFieldInfo("id", "参数错误,必须先勾选");
        }
        if (hasFieldInfo()) return;
        boolean del = friendLinkDAO.createCriteria(FriendLink.class).add(Expression.in("id", ids)).add(Expression.eq("namespace", JcmsIoc.namespace)).delete(false);
        if (del)
        {
            addActionMessage("保存成功");
        } else
        {
            addActionMessage("保存失败");
        }
    }


    //排序

    @Operate(submit = "operation", operation = "updateSortDate")
    public void updateSortDate() throws Exception
    {
        String[] ids = getArray("id", true);
        if (ids == null || ids.length < 1)
        {
            addFieldInfo("id", "参数错误,必须先勾选");
        }
        if (hasFieldInfo()) return;
        boolean del = friendLinkDAO.updateSortDate(ids);
        if (del)
        {
            addActionMessage("保存成功");
        } else
        {
            addActionMessage("保存失败");
        }
    }

    //校验infoType为检查后保存到FieldInfo中的信息类型

    //dataTypeValidator为sioc配置 formId 为检验配置的ID,

    //提交表单中 post 不为空才运行校验

    //提交表单中 post 不为空才运行 execute 方法

    @Operate(submit = "operation", operation = "updateTop")
    public void updateTop() throws Exception
    {
        String[] ids = getArray("id", true);
        if (ids == null || ids.length < 1)
        {
            addFieldInfo("id", "参数错误,必须先勾选");
        }
        if (hasFieldInfo()) return;
        boolean del = friendLinkDAO.updateSortType(ids, 2);
        if (del)
        {
            addActionMessage("保存成功");
        } else
        {
            addActionMessage("保存失败");
        }
    }


    //校验infoType为检查后保存到FieldInfo中的信息类型

    //dataTypeValidator为sioc配置 formId 为检验配置的ID,

    //提交表单中 post 不为空才运行校验

    //提交表单中 post 不为空才运行 execute 方法

    @Operate(submit = "operation", operation = "clearSortType")
    public void clearSortType() throws Exception
    {
        String[] ids = getArray("id", true);
        if (ids == null || ids.length < 1)
        {
            addFieldInfo("id", "参数错误,必须先勾选");
        }
        if (hasFieldInfo()) return;
        if (friendLinkDAO.updateSortType(ids, 0))
        {
            addActionMessage("保存成功");
        } else
        {
            addActionMessage("保存失败");
        }
    }

    /**
     * execute方法为默认执行必须执行, Operate 可以设置不执行execute
     * @return
     * @throws Exception
      编辑的时候查询出当前编辑的对象
     */

    public String execute() throws Exception
    {
        FriendLink friendLink = (FriendLink) friendLinkDAO.get(FriendLink.class, getString("id", true));
        if (friendLink == null)
        {
            friendLink = new FriendLink();
        }
        put("friendLink", friendLink);
        return SUCCESS;
    }
}

3.DAO数据部署,你可以使用你喜欢的ORM也可以使用我开发的sober.如果你对hibernate熟悉,你可以理解为是hibernate
 
如果你对ibatis熟悉你也把它当成ibatis来用, 在View中最好使用ioc注入DAO并且使用protected这样在Action中就不用写代码在注入了。


    protected TreeItemDAO treeItemDAO;
    @Ref(name = "treeItemDAO", namespace = "namespace")
    public void setTreeItemDAO(TreeItemDAO treeItemDAO)
    {
        this.treeItemDAO = treeItemDAO;
    }

 JdbcOperations
是sober提供的。FriendLinkDAO可以继承,public interface FriendLinkDAO extends SoberSupport这样就可以在DAO中使用Sober中提供的方法了。

public class FriendLinkDAOImpl extends JdbcOperations implements FriendLinkDAO




4.显示模版页面,就是你的html部分了,你喜欢什么样式,只需要写好Freemaker的模版就可以了。


5.运行流程说明

运行流程严格的来说和struts ,webork2都是不同的。struts ,webork2都还是使用的便宜运行方式,而jspx.net没有使用编译运行方式,换句话说jspx.net构架在运行的时候和php,asp是一样的运行方式。不会在tomcat,resin这些服务器下边编译生成java文件。这样对于java更好封装,调试也会更加方便。
1.客户访问URL,jsp服务器将地址转发给jspx.net转发器。
2.servlet转发器,或过滤转发器接收到URL后切分出命名空间和Action名称。(spring没有命名空间,sioc的命名空间和目录相对应,如果你感觉麻烦可以都放在global命名空间下,就和sping的一样了)
3.更具命名空间和Action名称,TXWeb将会载入配置中的View或Action.并且查找对应的目录(命名空间就是目录,action名称就是模版文件名称)。
4.找到模版后载入模版页面,分析Action标签,如果存在要求调入其他的类将会被载入。
5.使用Freemaker的模版解析功能,将要处理的对象都注入Freemaker中,使用Freemaker数据模版的方式将执行结果返回给浏览器。

思考一下,我们做了什么。使用Freemaker替代了jsp,其他的感觉并没有做什么,只是数据的流动,和规则有所改变,对于新手,如果你使用过Freemaker和spring可以说你更本不需要学习什么新东西。

提示:如果你抛开构架的观点,使用这样的代码结构和最早的jsp页面下的代码结构有什么不同。其实是一样的 public String execute() throws Exception部分就如同html页面部分。
 @Operate(submit = "submit")
 public void update() throws Exception

就如同
 String submit = request.getParameter("submit");
 if (submit!= null)
 {
        //操作
 }



其他的处理交给DAO完成,一个基本的大体构架就是这样。



其他每一部分的使用详细请看相应文章。
web构架部分命名为TXWeb.意思是模版Web页面构架
ioc部分命名为sioc,主要是小。
sober为orm部分。


6.已经实现功能
1.sober和hibernateCriteria接口几乎一样,推荐使用这种方式,hibernate的get方法查询不到的时候返回错误,sober返回为null,sober对数据库的支持没有hibernate那么多,但主流数据库几乎都支持了,而且都是最新版本的数据库oracle9,db2 9, mysql 4以上,psql 8.以上,mssql2000 .
提供了保存验证。
2.TXWeb部分几乎实现了XWork.配置也几乎一样。但默认配置在sioc中载入bean,提供了ajax接口,方便ajax使用。
3.Sioc是spring的替代,只是ioc的功能。配置有点不一样,配置标明数据类型。数据类型的表示也是ajax调用的方式,所有使用的地方统一。
可以AOP方式创建。当然注入方式没有spring的那么复杂,简单快速就好。
4.验证部分,提供了ajax验证使用js方式和程序使用java方式。已嵌入到sober,可以在view和action中使用。
5.上传功能内置,提供ajax状态返回,支持编码,稳定。
6.其他功能组件,包括pja是一个linux下图形生成包,ubb解析包,不安全代码清除。和应用AOP加载器。类似web.xml中的监听,完成一些定时工作。
7.Freemaker使用的是简化版本,加入了几个接口函数,我编译的这个版本,修复了几个Freemaker的Bug,当然也可以使用官方的版本,只是少得用几个函数而已。

7.已做测试

2.1版本后是jspx.net构架的一个新开始,各个包功能都基本稳定,而且性能和并发上都提高了不少,以后考虑一起打包发放了。


8.后期计划
目前有几个比较遗憾的地方,Freemaker虽然好用,但不好扩展,想注入自己的函数方法作为默认方法还比较麻烦。也许我会考虑专门编译一个Freemaker来使用。



9.程序环境说明

当系统启动时,先执行JspxCoreListener程序,初始化环境。主要是找到 jspx.net.xml文件,jspx.net.xml所在的目录将作为系统默认目录,找不到的文件都会在这个目录里边找一下,如果找不到这个文件,本系统就不能够运行了。随后将在这个目录里边找到jspx.properties文件,这是一些基本信息配置文件。
jspx.properties的内容可以使用${xxx}的站位符方式在jspx.net.xml中使用。
说明几个重要的变量
#模板文件类型
#你的模板文件是什么后缀名的
templateSuffix=ftl
template_update_delay=3600
dateTimeFormat=yyyy-MM-dd HH:mm:ss
date_format=yyyy-MM-dd
time_format=HH:mm:ss
number_format=0.##########
##URL访问的时候使用什么后缀名
suffix=jsp
#freemaker 的静态类载入
staticModels=stringUtil:com.jspx.utils.StringUtil;dateUtil:com.jspx.utils.DateUtil;htmlUtil:com.jspx.utils.HtmlUtil;
#freemaker 的公共库
autoImports=j:jspxnet.ftl
#freemaker 自动包含
autoIncludes=autoinclude.ftl

#日志相关log4jPath=log4j.xml
error=true
fatal==true
info=true
debug=true

#开启为true关闭为false,推荐调试的时候都为true如果正式使用的时候出了error打开,其他的都关闭。
jspxError=true
jspxFatal==true
jspxInfo=true
jspxDebug=true
#--日志配置--end