晚上睡不着,打开电脑翻到了一些当年在金蝶时写的笔记,还是让它们出来透透气吧,要不连我都忘记自己曾经写过这些东西了。金蝶是我第一个东家,也是我技术能力提升最快的地方,真心祝福金蝶能高飞。
由于现场开发环境与研发中心的差异,搭建环境会有差异,为了保证最后环境的搭建成功,我们分步骤进行搭建,以方便其中的一步如果出错,方便好定位问题
1.1 使用BOS启动客户端
启动BOS,新建一个工程
加载EAS Jar包(D:\kingdee\eas\server\lib\下的所有目录的jar包,有些麻烦,所以可以考虑把所有jar包拷贝到一个目录下)
这里我把所有的jar包都拷贝到了我自己建的目录allJars
然后BOS加载
添加外部JAR
设置JVM参数
-DEAS_HOME=D:\kingdee\eas (EAS_HOME相当于JDK_HOME)
-DEAS_SERVER=tcp://localhost:11034 (服务端的IP)
-Dlog4j.configuration=file:D:\kingdee\eas\client\deploy\client\log4j.properties (Log4j日志配置文件路径,可以没有这个参数)
Ok,jar包加载了,启动参数设置了后还需要修改几个地方
D:\kingdee\eas\client\deploy\client\vmoptions.properties
增加客户端启动的元数据加载路径,这部非常关键
OK,现在可以启动试试了
OK,搞定(这只是万里长征的第一步)
1.2部署自己的代码和元数据到环境中
这一步也就是让你的代码和元数据要优先于EAS原有代码和元数据的加载
代码优先的设置方法:
虚拟W盘,命令:subst w:你要虚拟的目录 (相应的去掉W盘的命令是 subst w: /d)
W盘建立好后,在W盘创建dev目录,并建立两个文件server-user.liblist和client-user.liblist
这两个文件用于记录优先加载的目录是那些,因为我开发的代码都会编译到D:\MyCode\EAS_01\bin,所以我制定客户端优先加载这个目录,服务端是一样的道理
然后我们设置元数据的优先加载方式
客户端,修改D:\kingdee\eas\client\deploy\client\vmoptions.properties,修改部分见红线,因为我的元数据会发布到D:\kingdee\apusic\metas;,所以这里可以设置为优先加载
服务端
修改D:\kingdee\eas\server\bin\\common.pm
这个时候,基本都差不多了,我们尝试下看我们的单据是否能够被优先加载(这里有点文档思路写的有点跳跃,大家将就着看把,时间紧迫,顾不得字斟句酌了,J)
1.3 启动EAS,看我们的单据能否使用
首先用administrator登陆
增加到EAS菜单中去
重新用用户登陆,可以看到我们的菜单项了
2006年3月
EAS是MVC架构的么?逐一分析,先看看EAS是否具备M、V、C这三个元素
Modle--xxxControllerBean,在ControllerBean中除了定义了对实体的CRUD(addnew,getValue,update,delete)操作外,还包括实体相关的业务操作方法,如submit,audit,freeze等,应该算的上标准的Modle,下边是摘抄的一些对Model的定义
模型(Model):就是业务流程/状态的处理以及业务规则的制定。业务流程的处理过程对其它层来说是黑箱操作,模型接受视图请求的数据,并返回最终的处理结果。业务模型的设计可以说是MVC最主要的核心。目前流行的EJB模型就是一个典型的应用例子,它从应用技术实现的角度对模型做了进一步的划分,以便充分利用现有的组件,但它不能作为应用设计模型的框架。它仅仅告诉你按这种模型设计就可以利用某些技术组件,从而减少了技术上的困难。对一个开发者来说,就可以专注于业务模型的设计。MVC设计模式告诉我们,把应用的模型按一定的规则抽取出来,抽取的层次很重要,这也是判断开发人员是否优秀的设计依据。抽象与具体不能隔得太远,也不能太近。MVC并没有提供模型的设计方法,而只告诉你应该组织管理这些模型,以便于模型的重构和提高重用性。我们可以用对象编程来做比喻,MVC定义了一个顶级类,告诉它的子类你只能做这些,但没法限制你能做这些。这点对编程的开发人员非常重要。
业务模型还有一个很重要的模型那就是数据模型。数据模型主要指实体对象的数据 保存(持续化)。比如将一张订单保存到数据库,从数据库获取订单。我们可以将这个模型单独列出,所有有关数据库的操作只限制在该模型中。
View-xxxEditUI,EditUI也应该算是标准的View,用于数据展示、数据采集、触犯用户事件等
视图(View)代表用户交互界面,对于Web应用来说,可以概括为HTML界面,但有可能为XHTML、XML和Applet。随着应用的复杂性和规模性,界面的处理也变得具有挑战性。一个应用可能有很多不同的视图,MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以及用户的请求,而不包括在视图上的业务流程的处理。业务流程的处理交予模型(Model)处理。比如一个订单的视图只接受来自模型的数据并显示给用户,以及将用户界面的输入数据和请求传递给控制和模型。
Controller-???,EAS的控制器在哪里,我一直没想明白,控制器的主要作用是接受客户请求,然后将模型与视图匹配在一起,共同完成用户的请求。
但我没有在EAS的框架中找到类似的东东,大胆的想了一下,我觉得不光是EAS没有Controller,可能所有C/S架构的都不太可能会有这样的东东,C/S架构不同于B/S,B/S的View和Modle同处于服务器端,可以进行协作处理,选择什么样的模型,选择什么样的视图,你可以在服务端完成,而C/S架构不是这样架构的,所以EAS并不是标准的MVC体系架构
下边这个集成体系是一个典型单据编辑界面的集成体系
HouseForRentEditUI为自定义的单据编辑界面
不同的父类UI有着不同的功能,这个可以从单据上的Action可以看到
下图为CoreBillEditUI的功能列表
下图为EditUI的功能列表
下图为CoreUI的功能列表
另外,单据的状态有以下几种
public class OprtState
{
public OprtState()
{
}
public static String ADDNEW = "ADDNEW";
public static String VIEW = "VIEW";
public static String EDIT = "EDIT";
public static String COPYADDNEW = "COPYADDNEW";
public static String EXITING = "EXITING";
}
CoreUIObject
处理一些UI底层的功能,类似与JDK中的Component
比如:
列表界面体系
EAS框架(CoreUI,EditUI,CoreBillEditUI)实现了一些最基本的功能
AbstractXXXUI做了什么事情呢?
1.构造界面的代码
2.构造事件
3.构造事务
4.让实现更单纯化
一个单据的编辑界面主要的几个方法:
1.onLoad()
2.loadField()
3.Action....2006年5月
DAO(Data Access Object):
为什么使用DAO:通过这种模式将底层数据访问操作与高层业务逻辑分离开。
底层数据访问操作包括那些:如创建、更新或者删除数据
下图IORMappingDAO为EAS定义的DAO 接口
看下DAO模式下的一个时序图
EAS中DAO接口(IORMappingDAO)的实现只有一个ORMappingDAO,它实现了对数据对象和数据库层间的增删改查的底层业务操作
数据对象的操作并没有封装在数据对象内,而是通过DAO对象来完成的,这样可以避免不同的数据对象都实现类似的数据访问操作.
关于数据对象,有不同的定义方式,有如下的把数据属性以类变量的形式出现的
public class Customer implements java.io.Serializable { // member variables int CustomerNumber; String name; String streetAddress; String city; ... // getter and setter methods... ... }
但EAS做法不是这样的,EAS把数据对象封装到数据Info中,数据Info实际上是封装了一个Map,通过这个Map来管理数据对象中所有类别的数据,当然获取数据和修改数据的方式还是通过setter和getter,只不过内部的实现是一个Map的数据修改和数据获取
DAO接口定义数据操作的方法,然后有具体的子类来根据不同的需要实现,可以是JDBC方式的DAO,也可以是通过O/R Mapping工具的DAO
一个实体会生成下边几个类
业务实现: com.kingdee.eas.fi.mipe.HouseForRent
业务接口: com.kingdee.eas.fi.mipe.IHouseForRent
值对象: com.kingdee.eas.fi.mipe.HouseForRentInfo
值对象集合: com.kingdee.eas.fi.mipe.HouseForRentCollection
控制器: com.kingdee.eas.fi.mipe.app.HouseForRentController
控制器Bean: com.kingdee.eas.fi.mipe.app.HouseForRentControllerBean
控制器Home: com.kingdee.eas.fi.mipe.app.HouseForRentControllerRemoteHome(已经作废)
控制器LocalHome:com.kingdee.eas.fi.mipe.app.HouseForRentControllerLocalHome(已经作废)
控制器Remote: com.kingdee.eas.fi.mipe.app.HouseForRentControllerRemote(已经作废)
控制器Local: com.kingdee.eas.fi.mipe.app.HouseForRentControllerLocal(已经作废)
其中,开发人员真正需要实现的类是com.kingdee.eas.fi.mipe.app.HouseForRentControllerBean
我们这里首先看下各个类的体系架构
1.业务实现: com.kingdee.eas.fi.mipe.HouseForRent
HouseForRent继承于IHouseForRent,为客户端和服务端所共有,相当于一个字典,记录着服务端那些方法可以被调用
2.业务接口: com.kingdee.eas.fi.mipe.IHouseForRent
见1
3.值对象: com.kingdee.eas.fi.mipe.HouseForRentInfo
实际上Info是EAS构造的一个类似于HashMap的东东,用于记录Key-Value,Info和数据库表的字段有相应的对应
比如:在HouseForRent实体中定义了一个number字段,在HouseForRentInfo中就会有
/**
* Object:房产项目(出租)'s 编码property
*/
public boolean getNumber()
{
return getString("number");
}
public void setNumber(String item)
{
setBoolean("number", item);
}
4.值对象集合: com.kingdee.eas.fi.mipe.HouseForRentCollection
Info的集合
5.控制器: com.kingdee.eas.fi.mipe.app.HouseForRentController
ControllerBean的接口
6.控制器Bean: com.kingdee.eas.fi.mipe.app.HouseForRentControllerBean
真正的业务实现是在这个里边的
7.控制器Home: com.kingdee.eas.fi.mipe.app.HouseForRentControllerRemoteHome(已经作废)
8.控制器LocalHome:com.kingdee.eas.fi.mipe.app.HouseForRentControllerLocalHome(已经作废)
9.控制器Remote: com.kingdee.eas.fi.mipe.app.HouseForRentControllerRemote(已经作废)
10.控制器Local: com.kingdee.eas.fi.mipe.app.HouseForRentControllerLocal(已经作废)
public class EASBizException extends NumericException
{
private static final String MAINCODE = "10";
public static final NumericExceptionSubItem CHECKTEXTLENGTH1 = new NumericExceptionSubItem("10", "CHECKTEXTLENGTH1");
public static final NumericExceptionSubItem CHECKTEXTLENGTH2 = new NumericExceptionSubItem("11", "CHECKTEXTLENGTH2");
public static final NumericExceptionSubItem CHECKINT = new NumericExceptionSubItem("20", "CHECKINT");
public static final NumericExceptionSubItem CHECKBLANK = new NumericExceptionSubItem("22", "CHECKBLANK");
public static final NumericExceptionSubItem CHECKDUPLICATED = new NumericExceptionSubItem("23", "CHECKDUPLICATED");
public static final NumericExceptionSubItem CHECKDUPNUMBER = new NumericExceptionSubItem("31", "CHECKDupNumber");
public static final NumericExceptionSubItem CHECKPARENTDELETED = new NumericExceptionSubItem("41", "CHECKPARENTDELETED");
public static final NumericExceptionSubItem CHECKPARENTUNEFFECTED = new NumericExceptionSubItem("42", "CHECKPARENTUNEFFECTED");
public static final NumericExceptionSubItem CHECKPARENTBEUSED = new NumericExceptionSubItem("43", "CHECKPARENTBEUSED");
public static final NumericExceptionSubItem CHECKNAMEBLANK = new NumericExceptionSubItem("221", "CHECKNAMEBLANK");
public static final NumericExceptionSubItem CHECKNUMBLANK = new NumericExceptionSubItem("222", "CHECKNUMBLANK");
public static final NumericExceptionSubItem CHECKNAMEDUP = new NumericExceptionSubItem("231", "CHECKNAMEDUP");
public static final NumericExceptionSubItem CHECKNUMDUP = new NumericExceptionSubItem("232", "CHECKNUMDUP");
public static final NumericExceptionSubItem CHECKEXIST = new NumericExceptionSubItem("321", "CHECKEXIST");
public static final NumericExceptionSubItem DBDEADLOCK = new NumericExceptionSubItem("400", "DBDEADLOCK");
public static final NumericExceptionSubItem RPCCOMMERR = new NumericExceptionSubItem("401", "RPCCOMMERR");
public static final NumericExceptionSubItem OLDNEWACCOUNTSTART = new NumericExceptionSubItem("402", "OLDNEWACCOUNTSTART");
/**
* construct function
* @param NumericExceptionSubItem info
* @param Throwable cause
* @param Object[] params
*/
public EASBizException(NumericExceptionSubItem info, Throwable cause, Object[] params)
{
super(info, cause, params);
}
/**
* construct function
* @param NumericExceptionSubItem info,Object[] params
*/
public EASBizException(NumericExceptionSubItem info, Object[] params)
{
this(info, null, params);
}
/**
* construct function
* @param NumericExceptionSubItem info,Throwable cause
*/
public EASBizException(NumericExceptionSubItem info, Throwable cause)
{
this(info, cause, null);
}
/**
* construct function
* @param NumericExceptionSubItem info
*/
public EASBizException(NumericExceptionSubItem info)
{
this(info, null, null);
}
/**
* getMainCode function
*/
public String getMainCode()
{
return MAINCODE;
}
}
下边是一个典型的EAS枚举
public class BillStatusEnum extends IntEnum
{
public static final int TEMPSAVED_VALUE = 1;
public static final int SUBMITED_VALUE = 2;
public static final BillStatusEnum TempSaved = new BillStatusEnum("TempSaved", TEMPSAVED_VALUE);
public static final BillStatusEnum Submited = new BillStatusEnum("Submited", SUBMITED_VALUE);
/**
注意到了么,枚举类的构造方法修饰为private,也就是说我们无法在外部构造一个BillStatusEnum类的实例,只能通过BillStatusEnum.TempSaved或BillStatusEnum.Submited来获取已定义好的实例,这样我们就保证了枚举的选择范围始终是我们预期的,这个就是所谓的typesafe enum模式
* construct function
* @param integer billStatusEnum
*/
private BillStatusEnum(String name, int billStatusEnum)
{
super(name, billStatusEnum);
}
/**
* getEnum function
* @param String arguments
*/
public static BillStatusEnum getEnum(String billStatusEnum)
{
return (BillStatusEnum)getEnum(BillStatusEnum.class, billStatusEnum);
}
/**
* getEnum function
* @param String arguments
*/
public static BillStatusEnum getEnum(int billStatusEnum)
{
return (BillStatusEnum)getEnum(BillStatusEnum.class, billStatusEnum);
}
/**
* getEnumMap function
*/
public static Map getEnumMap()
{
return getEnumMap(BillStatusEnum.class);
}
/**
* getEnumList function
*/
public static List getEnumList()
{
return getEnumList(BillStatusEnum.class);
}
/**
* getIterator function
*/
public static Iterator iterator()
{
return iterator(BillStatusEnum.class);
}
}
早期非Typesafe Enum的方式
public class Season {
public static final int SPRING = 0;
public static final int SUMMER = 1;
public static final int AUTUMN = 2;
public static final int WINTER = 3;
}
Locale CHINESE = new Locale("L2");
MetaDataLoaderFactory.setClientMetaDataPath(UIConfig.getMetaDataDir());
LoginHelper.login("yaya", "", "eas", "wens1018", CHINESE);步骤:1.设置元数据路径2.调用登陆接口性能测试脚本也是在此基础上写业务测试脚本的
看到"门面"这个词,大家一定都觉得很熟悉。不错,这个词正是借用了我们日常生活中的"门面"的概念。日常生活中的"门面",正是我们买东西的地方。因此可以这么说,"门面"就是这么一个地方,它们跟各种商品的生产商打交道,收集商品后,再卖给我们。换句话说,如果没有"门面",我们将不得不直接跟各种各样的生产商买商品;而有了"门面",我们要买东西,直接跟"门面"打交道就可以了。
--理解为商店的门面,翻译的太牵强,不如想象为医院的前台,有看到过一个更有好玩的比喻:
FACADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样 MM也可以用这个相机给我拍张照片了。
Facade模式正是这样一个"门面":我们本来需要与后台的多个类或者接口打交道,而Facade模式是客户端和后台之间插入一个中间层——门面,这个门面跟后台的多个类或接口打交道,而客户端只需要跟门面打交道即可。
--这个解释的很好,Facade模式是client与server交互过程的一个优化,是简化"客户"与"服务"交互的一种手段,注意这是一个交互优化。
使用Facade模式可以说是后台设计和编码人员的一个必备素质。我不止碰到过一个这样的后台开发人员,他们认为只要把后台功能完成了就万事大吉,而没有站在后台使用者的角度来看一看自己写出来的代码。其实,我们写出来的后台代码是要给别人使用的,所以我们提供给使用者的接口要越简单越好,这不单是对使用者好,同时对开发者也是好处多多的,至少你的接口简单了,你和使用者的交流就容易了。
而Facade模式中的Facade类正是这样一个用户接口,它和后台中的多个类产生依赖关系,而后台的客户类则只跟Facade类产生依赖关系。为什么要这么做?其中的原因十分简单:后台的开发者熟悉他自己开发的各个类,也就容易解决和多个类的依赖关系,而后台的使用者则不太熟悉后台的各个类,不容易处理和它们之间的依赖;因此,后台的开发者自己在Facade类中解决了与后台多个类之间的依赖,后台的使用者只需要处理和Facade类的依赖即可。
--使用Facade模式更多的是为了第三方调用起来简单快速,把复杂的事务逻辑封装起来
好了,闲话少说。我们下面就以几个具体的例子来看一看Facade模式是怎么使用的。实际编程中,能使用到Facade模式的情况有很多,以下就分两种情况来具体说一说Facade模式的使用。可能还会有其他的情况,大家在实践中也可以加以补充。
第一种情况,客户类要使用的功能分布在多个类中,这些类可能相互之间没有什么关系;客户在使用后台的时候,必须先初始化要使用到的功能所在的类,然后才能使用。这时候,适合将这些功能集中在一个Facade类里,还可以替用户做一些初始化的工作,以减轻用户的负担。
例如,以商店为例。假如商店里出售三种商品:衣服、电脑和手机。这三种商品都是由各自的生产厂商卖出的,如下:
public class CoatFactory
{
public Coat saleCoat()
{
……
return coat;
}
……
}
然后是电脑的厂家类:
public class ComputerFactory
{
public Computer saleComputer()
{
……
return computer;
}
……
}
最后是手机商类:
public class MobileFactory
{
public Mobile saleMobile()
{
……
return mobile;
}
……
}
如果没有商店,我们就不得不分别跟各自的生产商打交道,如下:
//买衣服
CoatFactory coatFactory = new CoatFactory();
coatFactory.saleCoat();
//买电脑
ComputerFactory computerFactory = new ComputerFactory();
computerFactory.saleComputer ();
//买手机
MobileFactory mobileFactory = new MobileFactory();
mobileFactory.saleMobile();
对我们顾客来说,和这么多的厂家类打交道,这显然是够麻烦的。
这样,我们就需要创建一个商店类了,让商店类和这些厂家打交道,我们只和商店类打交道即可,如下:
public class Store
{
public Coat saleCoat()
{
CoatFactory coatFactory = new CoatFactory();
return coatFactory.saleCoat();
}
public Computer saleComputer()
{
ComputerFactory computerFactory = new ComputerFactory();
return computerFactory.saleComputer();
}
public Mobile saleMobile()
{
MobileFactory mobileFactory = new MobileFactory();
return mobileFactory.saleMobile ();
}
}
好了,现在我们要买东西,不用去跟那么多的厂家类打交道了。
Store store =new Store();
//买衣服
store.saleCoat();
//买电脑
store.saleComputer();
//买手机
store.saleMobile();
呵呵,这样对我们客户类来说,是不是简单多了。
第二种情况客户要完成的某个功能,可能需要调用后台的多个类才能实现,这时候特别要使用Facade模式。不然,会给客户的调用带来很大的麻烦。请看下面的例子。
我经常看到后台编码人员,强迫它们的使用者写出如下的代码:
……
String xmlString = null;
int result = 0;
try
{
xmlString = gdSizeChart.buildDataXML(incBean);
String path = "D:/Eclipse3.0/workspace/PLMSuite/AppWeb/PM/productSpecification/gridfile.xml";
File f = new File(path);
PrintWriter out = new PrintWriter(new FileWriter(f));
out.print(xmlString);
out.close();
System.out.println("\r\n\r\n sumaryAction" + xmlString + "\r\n\r\n");
request.setAttribute("xmlString", xmlString);
}
catch(Exception ex)
{
ex.printStackTrace();
}
这段代码前面即省略号省略掉的一部分是客户类调用后台的一部分代码,是一个相对独立的功能。后面这一部分也是一个相对独立的功能,而后台代码设计人员却把这个功能留给客户类自己来实现。
我就很怀疑,让客户类做这么多事情,到底要你的后台做什么?你还不如直接把所有的事情都给客户类做了得了。因为,你后台做了一半,剩下的一部分给客户类做,客户类根本就不明白怎么回事,或者说他不清楚你的思路,这样做下去更加困难。可能这点逻辑对你来说,很简单。但使用者不明白你的思路啊,他不知道来龙去脉,怎么往下写?
如果在这里有一个Facade类,让它来做不该由客户类来做的事,是不是简单多了呢?如下是一个Facade类:
public class Facade
{
public static void doAll(PE_MeasTableExdBean incBean, HttpServletRequest request)
{
……
request.setAttribute("xmlString", Facade.getFromOut(incBean));
}
private static String getFromOut(PE_MeasTableExdBean incBean)
{
try
{
xmlString = gdSizeChart.buildDataXML(incBean);
String path = "D:/Eclipse3.0/workspace/PLMSuite/AppWeb/PM/productSpecification/gridfile.xml";
File f = new File(path);
PrintWriter out = new PrintWriter(new FileWriter(f));
out.print(xmlString);
out.close();
System.out.println("\r\n\r\n sumaryAction" + xmlString + "\r\n\r\n");
return xmlString;
}
catch(Exception ex)
{
ex.printStackTrace();
return null;
}
}
}
那么客户类的调用就是下面的样子:
Facade.doAll(incBean,request);
这样,客户是不是轻松多了?值得注意的是,Facade类中的getFromOut方法其实不应该在Facade类中,本文为了简单起见而放在了这个类中,对Facade类来说是不符合单一职责原则的。
最后总结一下第二种情况的模式。后台为实现某一个功能有如下类:
public class ClassA
{
public void doA()
{
……
}
……
}
public class ClassB
{
public void doB()
{
……
}
……
}
public class ClassC
{
public void doC()
{
……
}
……
}
如果客户类需要这样调用:
……
ClassA a = new ClassA();
a.doA();
ClassB b = new ClassB();
b.doB();
ClassC c = new ClassC();
c.doC();
……
那么就适合做一个Facade类,来替客户类来完成上述的功能,如下:
public class Facade
{
public void doAll()
{
ClassA a = new ClassA();
a.doA();
ClassB b = new ClassB();
b.doB();
ClassC c = new ClassC();
c.doC();
}
}
则客户类的调用如下:
……
Facade Facade = new Facade();
Facade.doAll();
……
类com.kingdee.bos.sql.test.BVT_KSQL用来对KSQL进行测试包含了大多数测试用例,每次修改之后都要运行一下以检验KSQL正确性。
类java.util.Collection.TransUtil封装了对词法分析、语法分析、翻译程序的调用,当需要翻译KSQL时需要调用的java.util.Collection.TransUtil中Translate方法,需要注意Translate方法有不同的版本,功能稍有区别。
类com.kingdee.bos.sql.formater.SQLFormatter和其子类负责KSQL到不同数据库平台SQL的翻译,不同的子类分别负责相应数据库平台的SQL翻译,Formatter中不同的方法分别负责翻译不同的SQL,如果要完成某功能就要先找到负责翻译的Formatter方法,目前主要支持DB2 UDB、DB2 AS400、MS_SQL_Server、Oracle。
转载请注明出处:http://blog.csdn.net/pan_tian/article/details/7773802
===EOF===