1. 框架简介

    1. 框架结构

      框架的整体结构可以简单的从横、纵两个方向来理解:
        横向分为核心、行业定制两个维度;
        纵向分为公共、扩展两个维度。
      如下表:

        核心 行业定制
      目录 core 目录 app
      公共 core[wade-core.jar] source/src/com.wade.core.data;
      source/src/com.wade.core.context;
      source/src/com.wade.core.config;
      source/src/com.wade.core.file;
      source/src/com.wade.core.cache;
      source/src/com.wade.core.log;
      source/src/com.wade.core.util;
      source/src/com.wade.core.bean;
      source/src/com.wade.core.db;
      source/src/com.wade.core.service;
      source/src/com.wade.core.BaseContext;
      source/src/com.wade.core.BaseCache;
      source/src/com.wade.core.BaseUtil;
      source/src/com.wade.core.BaseBean;
      source/src/com.wade.core.BaseEntity;
      source/src/com.wade.core.BaseFactory;
      source/src/com.wade.core.BaseService;
      source/src/com.wade.core.BaseException;
      app[wade-app.jar] source/src/com.wade.app.bean;
      source/src/com.wade.app.view.page;
      source/src/com.wade.app.view.component;
      source/src/com.wade.app.view.servlet;
      source/src/com.wade.app.AppContext;
      source/src/com.wade.app.AppCache;
      source/src/com.wade.app.AppUtil;
      source/src/com.wade.app.AppBean;
      source/src/com.wade.app.AppEntity;
      source/src/com.wade.app.AppService;
      source/src/com.wade.app.AppFactory;
      source/src/com.wade.app.AppException;
      source/src/com.wade.app.AppPage;
      source/src/com.wade.app.AppSafePage;
      source/src/com.wade.app.AppComponent;
      source/src/com.wade.app.AppFormComponent;
      source/src/com.wade.app.AppTempComponent;
      source/src/com.wade.app.AppServlet;
      view[wade-view.jar] source/src/com.wade.view.page;
      source/src/com.wade.view.component;
      source/src/com.wade.view.servlet;
      source/src/com.wade.view.BasePage;
      source/src/com.wade.view.BaseSafePage;
      source/src/com.wade.view.BaseComponent;
      source/src/com.wade.view.BaseFormComponent;
      source/src/com.wade.view.BaseTempComponent;
      source/src/com.wade.view.BaseServlet;
      source/jcl;
      source/ecl;
      扩展 jlcu[wade-jlcu.jar] source/src/com.wade.jlcu; demo[wade-quickstart.war] source/src/com.ailk.quickstart;
      desktop[wade-desktop.war] source/src/com.wade.desktop;    
    2. 目录结构

      下面以横向来介绍下框架的目录结构:

      1. 核心层-公共-core [wade-core.jar]

        该层包括了一些常用的基础功能,com.wade.core 下的目录下根据不同功能定义接口及默认实现,目录结构如下图:

      2. 核心层-公共-view [wade-view.war]

        View 层单独出来主要是为了前后台的完全解耦,支持不同 MVC 框架的结合,框架提供默认的MVC框架是 Tapestry,其目录结构如下图:

      3. 核心层-公共-扩展 jlcu [wade-jlcu.jar]

        Java 逻辑流程控制,该扩展主要是针对逻辑流的支持,稍后会详细讲解它的开发与关现;其目录结构如下图:

      4. 核心层-公共-扩展 desktop [wade-desktop.war]

        Desktop 是仿 Windows 客户体验的一个实现,可作为 “工作台”的一个轻量级解决方案,稍后会附上效果截图;其目录结构如下图:

      5. 行业定制层-公共-app[wade-app.jar]

        该层针对移动行业做了简单的包装,如数据库路由、业务组件、Session、缓存等;其目录结构如下图:

  2. 搭建开发环境

    1. 前期准备

      开发工具:Eclipse
      Web 容器:Tomcat 或 WebLogic,这里只针对 Tomcat 来说明
      数据库:MySQL 或 Oracle
      JDK:Jdk6
      CVS:192.168.102.253:2401/wade/quickstart

  3. 运行示例 quickstart

    1. Tomcat 容器配置修改

      1. Tomcat/conf/context.xml 里添加数据库 JNDI 配置:

        <Resource name="jdbc/sdcends"
        	auth="Container"
        	type="javax.sql.DataSource"  
        	driverClassName="oracle.jdbc.driver.OracleDriver"
        	url="jdbc:oracle:thin:@127.0.0.1:1521:mydb"
        	username="uop_cen"
        	password="123"
        	maxActive="30"
        	maxIdle="5"
        	maxWait="10000"/>
        <Resource name="jdbc/sdcrm1ds"
        	auth="Container"
        	type="javax.sql.DataSource"  
        	driverClassName="oracle.jdbc.driver.OracleDriver"
        	url="jdbc:oracle:thin:@127.0.0.1:1521:mydb"
        	username="uop_cen"
        	password="123"
        	maxActive="30"
        	maxIdle="5"
        	maxWait="10000"/>
        
      2. Tomcat/conf/server.xml 里添注入 quickstart 应用:

        <Context path="/demo"
        	docBase="D:\apps\quickstart\web"
        	debug="5"
        	privileged="true"
        	reloadable="true"
        	Reload="true"/>
        
    2. 本地化配置修改

      quickstart/etc/common/global.properties
      修改键:hessian.defaultsvc=/demo/hessian
      【注】这里的强调部分的内容必须与 Tomcat/conf/server.xml 里添加的 path 相同

    3. 执行数据库脚本

      quickstart/doc/mysql.sql 或 quickstart/doc/oracle.sql

    4. Ant 编译及运行效果

      1. 用 Ant 执行 quickstart/build/common/build.xml 文件

      2. 启动 Tomcat 后访问 url:http://localhost:8080/demo

  4. 开发示例

    1. 新建工程

      只需两个步骤就能创建一份可以运行的新工程

      1. 第一步 修改 build 配置,Ant 创建新工程

        1. quickstart/build/common/build.properties 里添加如下配置:

          #new project
          NEW_PROJ=../../../   #新工程创建在哪个目录
          OLD_NAME1=quickstart #不需要修改
          OLD_NAME2=Quickstart #不需要修改
          OLD_NAME3=QUICKSTART #不需要修改
          NEW_NAME1=demo       #新工程名称,注意大小写
          NEW_NAME2=Demo       #新工程名称,注意大小写
          NEW_NAME3=DEMO       #新工程名称,注意大小写
          
        2. quickstart/build/common/build.xml里修改如下配置:

          <target name="all" depends="newproject"/>

          用Ant执行完该build.xml后目将生成demo目录,与quickstart并级;如下图:

      2. 第二步 Ant 编译新工程,部署 Web 应用

        执行 demo/build/build.xml 后,即可在 Tomcat 里部署,部署可参看“运行示例”的说明。

    2. 界面展现层开发(view)

      1. 第一步 创建html、page及页面类java

        1. html 示例 quickstart/web/common/example/Hello.html:

          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
          <html xmlns="http://www.w3.org/1999/xhtml">
          <head jwcid="@Head">
          </head>
          <body jwcid="@Body">
          	Hello World!
          </body>
          </html>
          
        2. page 示例 quickstart/web/common/example/Hello.page:

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE page-specification PUBLIC
          	"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
          	"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
          <page-specification class="com.ailk.quickstart.view.common.example.Hello">
          </page-specification>
          
        3. quickstart/src/com/ailk/quickstart/view/common/example/Hello.java:

          package com.ailk.quickstart.view.common.example;
          import com.ailk.quickstart.core.common.QuickstartPage;
          public abstract class Hello extends QuickstartPage {
           
          }
          
      2. 第二步 注册页面并添加菜单

        1. 在 quickstart/web/WEB-INF/quickstart.application 中添加如下行:

          <page name="Hello" specification-path="/common/example/Hello.page"/>
          
        2. 在 quickstart/etc/common/menu.xml 里添加如下行:

          <menu id="quickstart" name="应用示例">
              ......
              <item name="HelloWorld" path="Hello" icon="temp/ico14.png"></item>
              ......
          </menu>
          
      3. 第三步 发布应用,访问页面

        访问 Hello 的效果如下图:

    3. 数据访问层开发(Dao&Bean)

      1. 第一步 创建 Dao 及 Bean

        Dao 示例:quickstart/src/com/ailk/quickstart/bean/common/HelloDAO.java
        package com.ailk.quickstart.bean.common;

        /**
         * 示例,客户信息管理的SQL语句
         * $Id: CustDAO.java,v 1.1.1.1 2012-03-25 14:08:04 liaos Exp $
         */
         
        import com.wade.app.AppEntity;
        import com.wade.core.context.IContext;
        import com.wade.core.data.IData;
        import com.wade.core.data.IDataset;
        import com.wade.core.db.SQLParser;
        import com.wade.core.db.util.Pagination;
         
        /**
         * 封装对TF_F_CUST表的增、删、改、查的SQL操作
         *
         * @author liaos
         */
        public class HelloDAO extends AppEntity {
        	/**
        	 * 构造函数
        	 *
        	 * @param ctx
        	 */
        	public HelloDAO(IContext ctx) {
        		super(ctx);
        	}
         
        	/**
        	 * 构造函数
        	 *
        	 * @param ctx
        	 * @param dbname
        	 */
        	public HelloDAO(IContext ctx, String dbname) {
        		super(ctx, dbname);
        	}
         
        	/**
        	 * 分页查询客户信息
        	 *
        	 * @param params
        	 * @param pagination
        	 * @return
        	 */
        	public IDataset queryCusts(IData params, Pagination pagination) {
        		SQLParser parser = new SQLParser(params);
        		parser.addSQL("select cust.CUST_ID, cust.CUST_NAME, cust.SEX, cust.AGE, to_char(cust.BIRTH_DATE,'yyyy-MM-dd') BIRTH_DATE," +
        			"cust.QQ, cust.FETION, cust.MOBILE_ID, cust.PSPT_TYPE_CODE, cust.PSPT_ID, " +
        			"cust.CONSTELLATION, cust.ZODIAC, cust.EMAIL, cust.BLOG_ADDR, cust.STATUS, " +
        			"cust.ADDRESS, to_char(cust.CREATE_TIME, 'yyyy-MM-dd') CREATE_TIME, cust.REMARK");
        		parser.addSQL("from TF_F_CUST cust where 1 = 1");
        		parser.addSQL("and CUST_ID=:CUST_ID");
        		parser.addSQL("and CREATE_TIME<=to_date(:START_DATE, 'yyyy-MM-dd')");
        		parser.addSQL("and CREATE_TIME>=to_date(:END_DATE, 'yyyy-MM-dd')");
        		parser.addSQL("order by cust.CUST_ID");
         
        		return queryList(parser, pagination);
        	}
           
        }
        
      2. Bean 示例:quickstart/src/com/ailk/quickstart/bean/common/HelloBean.java
        package com.ailk.quickstart.bean.common;

        /**
         * 示例,客户信息管理的原子逻辑
         * $Id: HelloBean.java,v 1.1.1.1 2012-03-25 14:08:04 liaos Exp $
         */
        import com.wade.app.AppBean;
        import com.wade.app.AppException;
        import com.wade.core.context.IContext;
        import com.wade.core.data.DataUtil;
        import com.wade.core.data.IData;
        import com.wade.core.data.IDataset;
        import com.wade.core.data.impl.DataMap;
        import com.wade.core.db.util.Pagination;
        import com.wade.core.util.TimeUtil;
         
        /**
         * 封装对TF_F_CUST表的增、删、改、查的原子逻辑
         *
         * @author liaos
         *
         */
        public class HelloBean extends AppBean {
         
        	private static final String _TABLE_CUST	 = "TF_F_CUST";
         
        	/**
        	 * 分页查询客户信息
        	 *
        	 * @param ctx
        	 * @param param
        	 * @param pagination
        	 * @return
        	 */
        	public IDataset queryCusts(IContext ctx, IData param, Pagination pagination) {
        		HelloDAO dao = new HelloDAO(ctx);
        		return dao.queryCusts(param, pagination);
        	}
         
        	/**
        	 * 根据cust_id查询客户信息
        	 *
        	 * @param ctx
        	 * @param param
        	 * @return
        	 */
        	public IData queryCust(IContext ctx, IData param) {
        		HelloDAO dao = new HelloDAO(ctx);
        		return dao.queryByPK(_TABLE_CUST, param);
        	}
         
        	/**
        	 * 根据cust_id删除客户信息
        	 *
        	 * @param ctx
        	 * @param param
        	 */
        	public void deleteCust(IContext ctx, IData param) {
        		HelloDAO dao = new HelloDAO(ctx);
        		dao.delete(_TABLE_CUST, param);
        	}
           
        	/**
        	 * 批量删除客户信息
        	 * @param ctx
        	 * @param param
        	 */
        	public void deleteCusts(IContext ctx, IData param) {
        		HelloDAO dao = new HelloDAO(ctx);
        		dao.deletes(_TABLE_CUST, DataUtil.splitStr(param.getString("CUST_IDS", ""), "CUST_ID"));
        	}
         
        	/**
        	 * 根据cust_id修改客户信息,全量修改
        	 *
        	 * @param ctx
        	 * @param param
        	 */
        	public IData updateCust(IContext ctx, IData param) {
        		HelloDAO dao = new HelloDAO(ctx);
        		dao.update(_TABLE_CUST, param);
        		return param;
        	}
         
        	/**
        	 * 根据cust_id修改客户信息,增量修改
        	 *
        	 * @param ctx
        	 * @param param
        	 */
        	public IData saveCust(IContext ctx, IData param) {
        		HelloDAO dao = new HelloDAO(ctx);
        		dao.save(_TABLE_CUST, param);
        		return param;
        	}
           
        	/**
        	 * 创建客户资料,并返回CUST_ID
        	 * @param ctx
        	 * @param param
        	 * @return
        	 */
        	public IData insertCust(IContext ctx, IData param) {
        		HelloDAO dao = new HelloDAO(ctx);
        	   
        		String cust_id = String.valueOf(System.currentTimeMillis());
        		param.put("CUST_ID", cust_id);
        		param.put("CREATE_TIME", TimeUtil.getSysDate());
        		param.put("STATUS", "1");
        	   
        		if (dao.insert(_TABLE_CUST, param)) return param;
        		else return new DataMap();
        	}
         
        }
        
    4. 服务层开发(service)

      1. 第一步 添加服务定义配置

        Service 示例:quickstart/etc/common/service/Hello.xml

        <?xml version="1.0" encoding="gb2312"?>
        <service>
        	<global>
        		 <name>HelloWorld</name>
        		 <path>com.ailk.quickstart.bean.common.example.HelloBean@queryCust</path>
        	</global>
        	<inparams>
        		 <param>
        			 <code>CUST_ID</code>
        			 <name>客户标识</name>
        		 </param>
        	</inparams>
        	<outparams>
        		 <param>
        			 <code>CUST_ID</code>
        			 <name>客户标识</name>
        		 </param>
        		 <param>
        			 <code>CUST_NAME</code>
        			 <name>客户姓名</name>
        		 </param>
        		 <param>
        			 <code>SEX</code>
        			 <name>性别</name>
        		 </param>
        		 <param>
        			 <code>AGE</code>
        			 <name>年龄</name>
        		 </param>
        	</outparams>
        </service>
        
      2. 第二步 客户端的服务调用

        1. 还是利用之前的 Hello.java,我们添加一个查询功能,添加后代码如下:

          package com.ailk.quickstart.view.common.example;
           
          import org.apache.tapestry.IRequestCycle;
          import com.ailk.quickstart.core.common.QuickstartPage;
          import com.wade.core.data.IData;
          import com.wade.core.data.IDataset;
          import com.wade.core.service.Service;
           
          public abstract class Hello extends QuickstartPage {
          	public abstract void setCusts(IDataset custs);
                  
          	/**
          	 * 根据查询条件,调用客户列表查询服务,需要传入分页参数
          	 *
          	 * @param cycle
          	 */
          	public void queryCusts(IRequestCycle cycle) {
          		 IData condition = context.getData("cond", true);
          		 IData svcdata = Service.create("QCS_QryCusts").call(context, condition, context.getPagination());
          		 Service.isSuccess(svcdata, true);
          		 setCusts((IDataset) Service.getData(svcdata));
          	}
          }
          
        2. 还需要修改下Hello.html及Hello.page文件,添加查询按钮及custs属性定义,修改后文件如下:

          hello.html

          <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
          <html xmlns="http://www.w3.org/1999/xhtml">
          <head jwcid="@Head">
          </head>
          <body jwcid="@Body">
          <form jwcid="@Form">
          	<button jwcid="bquery@Submit" listener="ognl:listeners.queryCusts">执行查询</button>
          	Hello,World!
          	<span jwcid="@Insert" value="ognl:custs"/>
          </form>
          </body>
          </html>
          

          hello.page

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE page-specification PUBLIC
            "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
            "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
          <page-specification class="com.ailk.quickstart.view.common.example.Hello">
          	<property-specification name="custs" type="com.wade.core.data.IDataset"/>
          </page-specification>
          
  5. 常见错误