paulwong

#

给开发维护大型项目开发者的建议

假设你是正在开发和维护一个包含2000个类并使用了很多框架的Java开发人员。你要如何理解这些代码?在一个典型的Java企业项目小组中,大 部分能够帮你的高级工程师看起来都很忙。文档也很少。你需要尽快交付成果,并向项目组证明自己的能力。你会如何处理这种状况?这篇文字为开始一个新项目的 Java开发者提供了一些建议。

0. 不要试图一下子搞懂整个项目
好好考虑一下,为什么理解项目代码是第一位的?大部分情况是你被要求修复一个bug或者加强系统已有功能。你要做的第一件事情不是理解整个项目的架构。当对项目进行维护时,这样(理解整个项目架构)可能会对你造成巨大的压力。

即便是有着10年可靠编程经验的Java开发者可能也没有理解项目的核心工作机制,尽管他们可能已经在这个项目工作超过一年(假设他们并非原始开发人员)。比如,对于认证机制或事务管理机制。

他们是怎么做的?他们对于自己负责的部分非常了解,并且能够交付价值给小组。每天的交付价值远比了解一些以后还不确定有没有的东西重要的多。

1. 关注于尽快交付价值
那我是否定了你对于项目架构理解的热情了么?完全不。我只是要求你尽早的交付价值,一旦你开始一个项目,搭建了开发环境,你就不应该花一两周时间才交付什么,无论他的规模大小。假如你是一个有经验的程序员却两周都没有任何交付,你的经理怎么会知道你是真的在工作还是在看新闻。

所以交付可以使大家都轻松起来。不要认为你能够做有价值的交付前必须理解整个项目。这是完全错误的。加一段javascript的验证代码对业务就很有价值,经理能够通过你的交付达到对你的信任。这样能够向上级领导证明你的贡献以及员工价值。

日复一日,在你不断修复bug及增强功能之后,就能够慢慢开始理解项目架构。不要低估对系统方方面面理解时需要花费的时间。花3-4天理解认证机 制,2-3天理解事物管理。这些都是依靠之前的相似项目的经历,但关键还是要花时间才能透彻的理解。要在日常工作中挤出时间,不要向经理要求特定的时间来做这些。

找找项目是否有一些不断维护的单元测试用例。有效的单元测试用例是理解大型项目代码的很好途径。单元测试能够帮助理解代码片段,包括一个单元的外部接口(单元如何被调用以及返回内容)及其内部实现(调试单元测试比调试整个实际用例简单许多)。

你如果能够很好的理解一些内容,写一些笔记,或者画一些类图、时序图、数据模型图,以便你或日后其他的开发者维护。

2. 维护大型项目所必须的技能
你能从事当前的工作,必然已经具有良好的java技术。我们来谈谈能够让你在新项目中良好表现的其他技能。大部分时间,你在项目中的任务是修复bug和增强功能。

有两项很重要的技能能够协助你维护大型项目代码。

2.1 能够迅速发现需要的类
在任何维护活动中,无论是修复bug或增强功能,第一个动作就是识别出当前修复或增强的用例中调用的类。当你定位到需要修复或增强的类/方法,就已经完工了一半。

2.2 能够分析变更的影响
当你在完成必要的修改或增强工作后,最重要的就是要确认你的修改没有破坏代码的其他部分。你要用你的java技术及对其他框架的理解找出变更可能影响的部分。下面有两个简单的例子详细描述了最后提及的情况:

a)当类A的equals()方法变更后,调用一个保护A实例的List的contains()方法时就会被影响到。若Java知识不够,很难考虑到这样的影响。

b)在一个web项目中,我们假设“user id”保存在session中。一个新入程序员可能在“user id”中加入一些信息作为bug修复的方法,但是却不知道会影响到那些关联“user id”的用例。

当你提高了如上两个技能,尽管你对项目不是非常了解,但大部分的维护任务会变得简单很多。若你修复一个bug,你会定位并修复这个bug,并且保证变更不会破坏项目的其他部分。若你增强或加入一个特性,基本上你只需要模仿现有的特性使用相似的设计。

在一个在线银行项目中,为什么“查看账户摘要”和“查看交易历史”的设计需要巨大的差别呢?如果你理解了“查看账户摘要”的设计,完全可以模仿开发出“查看交易历史”的功能。

就修复bug和增强来说,你不必完全理解所有2000个类的工作内容和代码如何运行来推动系统。你若有上面的技能,就能很快定位需要修改的代码的部分,使用良好的java和框架技能修复,保证变更不会破坏项目的其他部分并交付,尽管你可能只知道一小部分项目的设计。

3. 使用工具找到需要的变更内容以及变更产生的影响
继续我们尽快交付的主题,你应当寻找那些能够通过尽量少的了解项目但能帮助你尽快实施交付的工具作为辅助。

3.1 迅速发现需要变更内容的工具
无论是修复bug还是系统增强,首先都要找到该用例调用的你需要修改的类及方法。基本有两种方式理解一个用例的工作方式,静态代码分析和运行时分析。

源码分析统计扫描所有代码并且展示类之间的关系。市场上有很多设备与工具。比如:Architexa, AgileJ, UModel, Poseidon等。

所有的静态代码分析工具缺点在于无法确切展示用例中类或方法的运行时调用情况。因此Java新加入了特性,如回调机制(callback patterns)。如静态分析工具无法推断出当页面提交按钮被点击时哪个Servlet被调用了。

运行时分析工具能够展示类和方法在用例运行时的状态。工具包括:MaintainJ, Diver,jSonde,Java Call Tracer等。这些工具可以捕获运行时的堆栈状态,并以此为一个用例生成序列图和类图。

序列图展示了该用例在运行时所有调用的方法。若你在修复一个bug,那这个bug很可能就是这些被调用的方法之一。
若你在增强已有功能,利用序列图理解调用流程然后再修改。可能是新增一个验证,修改DAO等。

若你在新增功能,找到一些相似的特性,利用序列图理解调用流程然后模仿开发新功能。

要小心挑选运行时分析工具。信息过多是这类工具的主要问题。选择一些提供简单过滤无效信息并能够方便的查看各种视图的工具。

3.2 迅速发现需要变更内容的工具
若单元测试有效,可以通过运行单元测试发现变更有没有破坏其他测试用例。有效维护并且覆盖大型企业应用的单元测试还是比较少的。下面有一些针对该情况的工具。

仍然是有两种技术静态代码分析和运行时分析可以使用。市场中有很多静态代码分析工具可用。如:Lattix, Structure101, Coverity, nWire and IntelliJ’s DSM。

给定一个变更后的类,上述工具均可识别对该类存在依赖的类的集合。开发者需要根据这些信息“猜测”可能产生影响的用例,因为这些工具无法展示运行时类之间的调用关系。

市场上的可以用于运行时影响分析的工具并不多,除了MaintainJ。MaintainJ先捕获在一个用例中调用的所有类和方法。当所有用例的上 述信息都被捕获之后,就很容易发现类的变更对用例的影响。MaintainJ能够有效工作的前置条件就是项目的所有用例都应当先运行一遍,以便能够获得运 行时的依赖关系。

总之,目前你在迅速准确分析变更影响方面,还是可以从工具中获得有限的帮助。首先根据需要实施一些影响分析,然后根据自己或小组其他高级成员评审来判断变更的影响。你可能需要上面提到的工具对你的判断进行反复确认。

4. 对上述内容的两个忠告
4.1 不要降低代码质量
为了快速交付,所以没有全盘理解架构,但绝不能以降低代码质量为条件。下面是一些你可能因为只考虑快速交付而引发的代码质量问题。

因为修改代码涉及到很多的依赖,所以新增代码相对而言风险较小。例如,有5个用例都调用了某个方法。为了改进某个用例,你需要修改这个方法的实现。 最简单的做法就是复制这个方法,重命名,然后在改进的用例中调用新方法。千万不要这么做。代码冗余绝对是非常有害的。尝试对方法进行包装或者重写,甚至是 直接修改,然后重新测试所有用例,通常停下来想一想,然后亲手去实施,是一个比较好的方式。
code quality 
( 伯乐在线配图)

另一个例子是将“private”方法改为“public”,使得别的类也可以调用。尽量不要将非必须的部分暴露出来。假如为了更好的设计需要重构,就应当着手去做。

大部分应用都有确定的结构和模式来实施。修复或增强程序时,确认你没有偏离这样的模式。若对约定不确定,请其他的高级开发者来审核你的变更。若你必须做一些违背约定的实施,尽量放置于一个规模较小的类中(一个200行代码的类中的私有函数应当不会影响应用的整体设计)

4.2 不要停止深入理解项目架构
按照文章列出的方式,假设你能够在对项目了解较少的情况下进行交付并以此持续下去,可能你会停止对项目架构的深入了解。这样从长远角度来说对你的职 业生涯没有帮助。当你的经验增加时,你应当承担比较大的模块任务。如构建一个完整的新特性或者修改项目的一些基础设计等较大的改进。当你能够做这些改进 时,你对项目的整体架构应该相当了解。文中列举的方法是让你在最短的时间内提升自己,而不是阻止你完整理解整个项目。

5. 结论
整篇文章集中在对项目进行必要了解的前提下进行快速交付。你可以在不降低代码质量的前提下这么做。
若修复一个bug,迅速定位并修复。有必要可以使用运行时分析工具。若新增一个特写,可以寻找相似特写,理解流程(有必要使用工具)并编写。

或许这些听起来很简单,但是实用吗?当然。但前提是你有良好的java技术以及对框架足够了解才能先修改代码,然后对变更影响进行分析。对变更影响的分析比实施变更需要更多的技巧。你可能需要高级开发人员协助你分析变更影响。

大约有50%的IT可操作预算用于简单的bug修复和功能增强。根据文中的建议,对于维护活动中的经费的节省应当还是很有帮助的。

作者 Choudary Kothapalli 也是 MaintainJ 项目的建立者。

posted @ 2012-04-15 20:37 paulwong 阅读(242) | 评论 (0)编辑 收藏

转一个J2EE开发时的包命名规则,养成良好的开发习惯

代码编写规范目的:能够在编码过程中实现规范化,为以后的程序开发中养成良好的行为习惯。
代码编写规范使用范围:J2EE项目开发。
包命名规范:
目的:包的命名规范应当体现出项目资源良好的划分

servlet类所在包命名规范:公司名称.开发组名称.项目名称.web.servlet
例如:net.linkcn.web.servlet

自定义标签类所在包命名规范:公司名称.开发组名称.项目名称.web.tags
例如:net.linkcn.web.tags

过滤器类所在包命名规范:公司名称.开发组名称.项目名称.web.filter
例如:net.linkcn.web.filter

Action类所在包命名规范:公司名称.开发组名称.项目名称.web.struts.action
例如:net.linkcn.web.struts.action

ActionForm类所在包命名规范:公司名称.开发组名称.项目名称.web.struts.form
例如:net.linkcn.web.struts.form

Javabean所在包命名规范:公司名称.开发组名称.项目名称.web.struts.service.impl
例如:net.linkcn.web.service.impl

Javabean实现接口命名规范:公司名称.开发组名称.项目名称.web.service
例如:net.linkcn.web.service

DAO类所在包命名规范:公司名称.开发组名称.项目名称.dao.impl
例如:net.linkcn.dao.impl

DAO类所实现的接口在包中命名规范:公司名称.开发组名称.项目名称.dao
例如:net.linkcn.dao

POJO类与hbm文件所在包命名规范:公司名称.开发组名称.项目名称.dao.hbm
例如:net.linkcn.dao.hbm

全局公共类、接口类所在包命名规范:公司名称.开发组名称.项目名称.global
例如:net.linkcn.global

全局工具类所在包命名规范:公司名称.开发组名称.项目名称.util
例如:net.linkcn.util

类命名规范
基本命名规范:

类、接口命名

命名规范:以大写字母开头,如果有多个单词,每个单词头字母大写
例如:StudentInfo
接口命名

命名规范:以大写字母"I"开头,如果有多个单词,每个单词头字母大写
例如:IStudentInfo

接口实现类命名:
命名规范:将实现的接口名称的首字母"I"去掉,以"Impl作为结尾",如果有多个单词,每个单词头字母大写。
例如:StudentInfoImpl

J2EE+SSH框架命名规范
servlet类命名:
命名规范:以Servlet单词结尾
例如:LoginServlet

POJO命名:
使用hibernate自动生成的类即可

DAO类命名:
使用hibernate自动生成的类即可

Action类命名:
命名规范:Action的命名以POJO名称来制定,POJO名称Action
例如:
一个POJO名称为Diary,其对应的action为DiaryAction

ActionForm类命名:
命名规范:ActionForm的命名以POJO名称来制定,POJO名称Form
例如:
一个POJO名称为Diary,其对应的actioForm为DiaryForm

业务逻辑接口命名:
命名规范:业务逻辑接口的命名以POJO名称来制定,IPOJO名称Service
例如:
一个POJO名称为Diary,其对应的业务逻辑接口为IDiaryService

业务逻辑实现类命名:
命名规范:业务逻辑接口实现类的命名以POJO名称来制定
例如:
一个POJO名称为Diary,对应的业务逻辑接口实现类名为DiaryServiceImpl

类变量命名:
命名规范:变量名首字母必须小写,如果该变量名有多个单词组成,后面的单 词首字母大写,单词与单词之间不要使用"_"做连接,变量名访问控制必须为私有, 可以对其增加setter与getter方法。
例如:
private int studentAge;
public int getStudentAge(){
return studentAge;
}
public void setStudentAge(int studentAge) {
this.studentAge=studentAge;
}

常量命名:
命名规范:所有字母大写,如果有多个单词组成,单词与单词之间以” _“隔开。而 且该变量必须是公共、静态、final类型
例如:public static final String USER_NAME=”userName“;

方法命名
命名规范:首字母必须小写,如果该变量名有多个单词组成,后面的单词首字母 大写,单词与单词之间不要使用"_"做连接。单词不要使用名词。
例如:public int checkLogin(String name,String pwd){}

注释规范:注释规范是整个开发规范中最为重要的组成部分,必须严格执行。

类的注释:
作用:注释整个类,简单概述该类作用。
书写规范:类的注释必须写在该类的声明语法之前。在注释中要描述该类的基 本作用,作者,日期,版本,公司名称,版权声明。
格式:

类的声明语法

例如:

public class AdminDAO

变量、常量注释:
作用:简单描述该变量的意义。
书写规范:变量注释必须写在变量定义之前,简单描述其代表的意义。
格式:


例如:


public int age;
方法注释:
作用:对该方法功能简单描述,其参数、返回值意义的注解。
书写规范:方法注释必须写在方法定义之前。该注释包括:方法其功能的简单 描述,方法的参数、返回值类型、返回值意义简单的描述。
格式:


例如:

public booleaneditAdminPassword(int adminId,String oldPassword,
String password) throws UserException,ServiceException;

Jsp页面命名:
命名规范:jsp页面名称要以小写字母开头,如果有多个单词组成,后面的单词以 大写字母开头。名称要体现出该页面的意义,最好能够与模块名称联系在一起。
例如:
login.jsp --登录页面
register.jsp --注册页面
message.jsp --客户留言页面

J2EE项目工程文件夹组织规范:
目的:规范学员web应用程序的资源组织形式,形成良好的文件组织习惯。文件的组织形式应当体现模块的划分。

根据eclipse工具的特征,项目的目录结构为:
src
----存放java文件
WebRoot
|--images --存放web程序所需的公共图片
|--css --存放web程序所需的公共样式表
|--js --存放web程序所需的公共js文件
|--commons --存放web程序所需的公共文件
|--功能模块文件夹(存放与某个功能模块相关的资源)
|--images --存放与该功能模块相关的图片
|--css --存放与该模块相关的样式表文件
|--js --存放与该模块相关的js文件
|--jsp、html页面
|--WEB-INF
|--classes
|--lib
|--tld文件

J2EE项目提交规范
项目完成时要将项目作为一个产品交付用户,良好的项目组织规范可以使用户可以方便的找寻项目中需要的资源,同时也是一个公司专业性的体现。项目提交时,要按照下列文件格式进行提交。

项目主文件夹:
作用:存放项目其他资源文件。
命名规范:时间_班级编号_第X小组。
例如:070706_GS2T18_第四小组。
项目主文件夹下面包括以下文件夹和文件:
|--src:保存.java文件。
|--database:保存数据库的脚本文件或者数据库备份文件。
|--source:保存eclipse工程中WebRoot目录下的所有文件。
|--depend:保存编译该程序必须依赖的其他jar文件。
|--javadoc:保存所有类生成的javadoc api文档。
|--war:保存程序的归档文件
|--xx.war:已经打包好的工程文件,可以直接运行。
|--project:保存开发项目原工程代码及文件。
|--产品说明书.doc:图文方式展现该产品使用方法。
|--build.xml:ant脚本,用于生成运行的war文件。
|--项目解说.ppt:进行项目讲解的ppt(ppt仅供在校模拟项目使用,不用于其他商业用途)
注:一个完整的项目中,数据库必须有一定量的有效的测试数据来支持该程序的运行

包的命名 
Java包的名字都是由小写单词组成。但是由于Java面向对象编程的特性,每一名Java程序员都可以编写属于自己的Java包,为了保障每个 Java包命名的唯一性,在最新的Java编程规范中,要求程序员在自己定义的包的名称之前加上唯一的前缀。由于互联网上的域名称是不会重复的,所以程序 员一般采用自己在互联网上的域名称作为自己程序包的唯一前缀。
例如: net.frontfree.javagroup

类的命名
类的名字必须由大写字母开头而单词中的其他字母均为小写;如果类名称由多个单词组成,则每个单词的首字母均应为大写例如TestPage;如果类名 称中包含单词缩写,则这个所写词的每个字母均应大写,如:XMLExample,还有一点命名技巧就是由于类是设计用来代表对象的,所以在命名类时应尽量 选择名词。   
例如: Circle

方法的命名
方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头。
例如: sendMessge

常量的命名
常量的名字应该都使用大写字母,并且指出该常量完整含义。如果一个常量名称由多个单词组成,则应该用下划线来分割这些单词。
例如: MAX_VALUE

参数的命名
参数的命名规范和方法的命名规范相同,而且为了避免阅读程序时造成迷惑,请在尽量保证参数名称为一个单词的情况下使参数的命名尽可能明确。

Javadoc注释
Java除了可以采用我们常见的注释方式之外,Java语言规范还定义了一种特殊的注释,也就是我们所说的Javadoc注释,它是用来记录我们代 码中的API的。Javadoc注释是一种多行注释,以结束,注释可以包含一些HTML标记符和专门的关键词。使用Javadoc 注释的好处是编写的注释可以被自动转为在线文档,省去了单独编写程序文档的麻烦。
例如:

在每个程序的最开始部分,一般都用Javadoc注释对程序的总体描述以及版权信息,之后在主程序中可以为每个类、接口、方法、字段添加 Javadoc注释,每个注释的开头部分先用一句话概括该类、接口、方法、字段所完成的功能,这句话应单独占据一行以突出其概括作用,在这句话后面可以跟 随更加详细的描述段落。在描述性段落之后还可以跟随一些以Javadoc注释标签开头的特殊段落,例如上面例子中的@auther和@version,这 些段落将在生成文档中以特定方式显示。

变量和常量命名
变量命名的方法采用匈牙利命名法,基本结构为scope_typeVariableName,它使用3字符前缀来表示数据类型,3个字符的前缀必须 小写,前缀后面是由表意性强的一个单词或多个单词组成的名字,而且每个单词的首写字母大写,其它字母小写,这样保证了对变量名能够进行正确的断句。例如, 定义一个整形变量,用来记录文档数量:intDocCount,其中int表明数据类型,后面为表意的英文名,每个单词首字母大写。这样,在一个变量名就 可以反映出变量类型和变量所存储的值的意义两方面内容,这使得代码语句可读性强、更加容易理解。byte、int、char、long、float、 double、boolean和short。
变量类型和首字母对照关系如下表:
数据类型/对象类型 / 变量前缀 / 备注
byte bye
char chr
float flt
boolean bln 做布尔变量时,使用bln
Integer/int int
String str
Single sng
short sht
Long/long lng
Double/double dbl
Currency cur
Variant bln astr obj vnt 做布尔变量用时,用bln,做字符串数组用时,用astr,做为对象使用时,用obj,不确定时,用vnt。
对于数组,在数据类型的前缀前再增加一个a,例如字符串数组为astr。对于在多个函数内都要使用的全局变量,在前面再增加“g_”。例如一个全局的字符串变量:g_strUserInfo。

在变量命名时要注意以下几点:
· 选择有意义的名字,注意每个单词首字母要大写。

· 在一段函数中不使用同一个变量表示前后意义不同的两个数值。

· i、j、k等只作为小型循环的循环索引变量。

· 避免用Flag来命名状态变量。

· 用Is来命名逻辑变量,如:blnFileIsFound。通过这种给布尔变量肯定形式的命名方式,使得其它开发人员能够更为清楚
的理解布尔变量所代表的意义。

· 如果需要的话,在变量最后附加计算限定词,如:curSalesSum。

· 命名不相包含,curSales和curSalesSum。

· Static Final 变量的名字应该都大写,并且指出完整含义。

· 如果需要对变量名进行缩写时,一定要注意整个代码中缩写规则的一致性。例如,如果在代码的某些区域中使用intCnt,而在另一些区域中又使用intCount,就会给代码增加不必要的复杂性。建议变量名中尽量不要出现缩写。

· 通过在结尾处放置一个量词,就可创建更加统一的变量,它们更容易理解,也更容易搜索。例如,请使用 strCustomerFirst和strCustomerLast,而不要使用strFirstCustomer和strLastCustomer。常 用的量词后缀有:First(一组变量中的第一个)、Last(一组变量中的最后一个)、Next(一组变量中的下一个变量)、Prev(一组变量中的上 一个)、Cur(一组变量中的当前变量)。

· 为每个变量选择最佳的数据类型,这样即能减少对内存的需求量,加快代码的执行速度,又会降低出错的可能性。用于变量的数据类型可能会影响该变量进行计算所产生的结果。在这种情况下,编译器不会产生运行期错误,它只是迫使该值符合数据类型的要求。这类问题极难查找。

· 尽量缩小变量的作用域。如果变量的作用域大于它应有的范围,变量可继续存在,并且在不再需要该变量后的很长时间内仍然占用资源。它们的主要问题是,任何类 中的任何方法都能对它们进行修改,并且很难跟踪究竟是何处进行修改的。占用资源是作用域涉及的一个重要问题。对变量来说,尽量缩小作用域将会对应用程序的 可靠性产生巨大的影响。
关于常量的命名方法,在JAVA代码中,无论什么时候,均提倡应用常量取代数字、固定字符串。也就是说,程序中除0,1以外,尽量不应该出现其他数 字。常量可以集中在程序开始部分定义或者更宽的作用域内,名字应该都使用大写字母,并且指出该常量完整含义。如果一个常量名称由多个单词组成,则应该用下 划线“_”来分割这些单词如:NUM_DAYS_IN_WEEK、MAX_VALUE。

posted @ 2012-04-15 18:53 paulwong 阅读(4548) | 评论 (0)编辑 收藏

工作流引擎Activiti使用总结

http://www.kafeitu.me/activiti/2012/03/22/workflow-activiti-action.html


源码来了:
https://github.com/henryyan/kft-activiti-demo

posted @ 2012-04-09 16:39 paulwong 阅读(2067) | 评论 (0)编辑 收藏

调优思路

1、使用剖析器,如jvisualvm等工具抓快照,分析调用次数最多,和耗时最多的Java代码;

2、如果数据库是Oracle,可以把数据库迁移到11G进行测试,11G里面也有一个剖析工具抓快照,可以分析出哪条SQL语句执行最频繁,哪条SQL执行最耗时;

3、如果是WebApp,可以自己去修改clickstream的filter抓取和记录所有的用户请求,统计记录结果中的访问次数和耗时;

posted @ 2012-04-09 00:37 paulwong 阅读(254) | 评论 (0)编辑 收藏

基于ZooKeeper的分布式Session实现

http://my.oschina.net/jsan/blog/52151

posted @ 2012-04-02 00:14 paulwong 阅读(359) | 评论 (0)编辑 收藏

ACTIVITI KICK START

https://github.com/jbarrez/Activiti-KickStart

一个基于ACTIVITI的内容管理系统:
http://code.google.com/p/echoice/source/browse

ACTIVITI中文使用手册:
http://www.georgeinfo.com/file/activiti-doc.pdf

posted @ 2012-04-01 00:43 paulwong 阅读(285) | 评论 (0)编辑 收藏

使用 WS-AtomicTransaction 和 JTA 的分布式事务

http://wenku.baidu.com/view/f3126425ccbff121dd3683b3.html

在现在的企业应用程序的开发中,Web 服务已经越来越普遍。然而,从传统意义上来说,它们还没有达到和所支持的服务相同的水平。当构建 J2EE 应用程序,特别是事务服务的时候,企业依赖于这些服务。本文概述了事务服务是如何在一个使用 Java Transaction API 的 J2EE 环境中的 Web 服务事务的帮助下,与 Web 服务实现无缝连接的。

本文简要地概述了这项新的 Web 服务技术和已被证实的传统的事务技术,解释了它们是如何能够跨分布式的 J2EE 环境甚至跨不同的事务体系结构来实现互操作的。

本文假设您已经对事务服务的概念(例如,ACID properties、提交/回滚、事务划分,等等)的理解达到中级水平。要想了解事务服务的进一步信息,特别是 JTS,请参考文章 Java theory and practice:Understanding JTS —— An introduction to transactions。这篇文章可以在 developerWorks 上找到(请参阅 参考资料)。同样,我也想要推荐一本关于事务的更全面信息的好书,它就是由 Philip Bernstein 和 Eric Newcomer 合著的 Principles of Transaction Processing(请参阅 参考资料


什么是 Java Transaction API(JTA)?

JTA 是事务服务的 J2EE 解决方案。本质上,它是描述事务接口(比如 UserTransaction 接口,开发人员直接使用该接口或者通过 J2EE 容器使用该接口来确保业务逻辑能够可靠地运行)的 J2EE 模型的一部分。

JTA 具有的三个主要的接口分别是 UserTransaction 接口、 TransactionManager 接口和 Transaction 接口。这些接口共享公共的事务操作,例如 commit()rollback() , 但是也包含特殊的事务操作,例如 suspend()resume()enlist() ,它们只出现在特定的接口上,以便在实现中允许一定程度的访问控制。例如, UserTransaction 能够执行事务划分和基本的事务操作,而 TransactionManager 能够执行上下文管理。本文仅仅需要您对 JTA 有一个基本的了解。


JTA 的好处?

JTA 是一个定义明确的事务服务,向 J2EE 应用程序开发人员提供一种可以直接使用的服务。作为选择,一个应用程序也可能这样部署,容器将代替开发人员来管理事务行为。在后一种情况下,开发人员能够全神贯注于他们的应用程序的业务逻辑,同时由 J2EE 容器来负责事务逻辑。

模型明确的事务服务的好处是对于每个单独的事务总是维持四个 ACID 特性。尽管这是一个实现相关的问题,WebSphere Application Server 提供为每个导入的或者导出的事务保护这些 ACID 特性的能力,而不管并发的事务数目是多少。


JTA 的限制?

经历过所有的事务体系结构,想要有效地将一组事务传送给其他并不共享同样模型的事务服务,同时保持原子的工作单元,是非常困难的。在我们的案例中,建模的 JTA 运行在 Java Transaction Service(JTS) 之上,JTS 处理输入和输出事务传送的请求。

因为 JTS 是一种由 CORBA 定义的对象事务服务(OTS)的 Java 实现,它只能够与另一个 OTS 模型连接。因此, 一个事务只能传送给另一个 OTS-兼容的目标,典型地即另一个 J2EE 实现。因为 JTA 和 JTS 规范没有对这些接口的底层实现加以限制 (只要它们符合模型),事务可以安全地在两个 J2EE-兼容的应用程序服务器之间传送,而没有丢失它们的 ACID 特性的风险。然而,J2EE 服务器并不必须处理非 J2EE 调用。

某些 J2EE 服务器可能是例外;例如,WebSphere Application Server 将正确地处理一个与 CORBA 兼容事务相关联的输入的 CORBA 请求,将这个事务传送给线程,然后在它的上下文里执行事务工作。然而,在大多数情况下,当您试图在事务模型之间移动的时候,您不得不超越 JTA 和 JTS,把目光投得更远,在这里 Web 服务出现了。


什么是 Web 服务?

Web 服务是一种能够作为应用程序一部分部署在可访问的服务器上供内部和外部客户使用的对象。Web 服务由它的 Web 服务描述语言(WSDL)来描述。它定义了一个使用基于 XML 调用(典型地使用 SOAP 协议)的 Web 服务的输入和输出参数的用法。例如,客户端可以查看已经由服务器发布的 WSDL,然后构造客户端代码来调用 Web 服务。一旦完成,它就能够通过将 SOAP 消息传递给 Web 服务的一个方法来调用它。在这条 SOAP 消息中包括诸如方法 名称的信息以及任何它所需要的参数。返回值将在另一条 SOAP 消息里被传送回来,再由客户提取出来。


使用 Web 服务的好处?

Web 服务由哪种语言编写而成并不重要,因为 WSDL 没有定义语言或者编程的模型相关的细节(例如,Java 和 J2EE 技术)。这就给了 Web 服务的作者和客户端的作者选择他首选的解决方案 的灵活性。

让我们来比较一下 Web 服务和 Enterprise JavaBean(EJB)组件。EJB 组件要求 RMI-编译的代码,以便使客户端能够访问,所以 它能够像它的代理一样创造本地的存根(stub)对象。因此,这将需要在每一次它们改变的时候,向所有的客户端重新分配存根(stub)。 无论如何,和 Web 服务一起您将使用 WSDL,所以客户端能够构造它们自己的客户端调用代码,在本地类路径上不需要服务器的类来执行调用。这个模型提供了一个非常巧妙的方法调用过程。 EJB,作为 J2EE 模型的一部分,必须使用 Java 客户来调用,最好是一个 J2EE 管理的客户端。另一方面,Web 服务可以被任何客户端代码所调用,这个代码能够构造一个结构良好的 SOAP 请求。因而,举例来说,一个部署在 J2EE 服务 器上的 Web 服务能够使用 C++ 客户来调用。


Web 服务的限制?

因为 Web 服务请求(通过 HTTP 的 SOAP)的性质与其他的方法调用(例如,一个使用通过 IIOP 的 RMI 的 EJB 调用)差别很大,支持执行分布式事务的代码直到最近才可获得。这已经成为在 使用 Web 服务作为分布式事务企业应用程序一部分时,主要的问题。本质上,Web 服务不能运行在 Web 服务调用之前开始的事务上下文 中,也不能将一个事务上下文传送给另一个组件。


那么,问题是什么呢?

如果 Web 服务被用于工业,必须确保它们在事务环境 中运行的时候,以可靠的和可预知的方式工作。直到现在,Web 服务只能够使用独立于其他组件的事务——在 Web 服务的方法范围里划分的 和服从它的底层的事务实现的规则——并且物理上不能离开 Web 服务或者进入另一个 Web 服务。企业应用程序具有始终在企业组件间流动 的事务。这需要成为 Web 服务的标准来确保它们能够被正确地使用,通过利用 Web 服务的功能仅仅忽略在我们的所有严格的企业应用 程序中依赖的和使用的事务支持来避免改变您的编程风格。


那么,解决方案是什么呢?

解决方案就是一种称为 Web 服务事务(WS-Transaction) 的新技术。它能够调整事务的上下文。这个上下文可以被 Web 服务、其他的诸如 EJB 的 J2EE 组件、甚至其他支持 WS-Transaction 的 非 J2EE 事务服务使用。

WS-Transaction 是一个规范。它扩展了 Web 服务协调(WS-Coordination)规范来定义一种支持原子事务的协调。


什么是 WS-Coordination

WS-Coordination 是一个协调框架来使分布的参与者 能够在他们个体行动之上就一个通用的结果达成协议。

本质上,这意味着分布式的参与者(例如,在不同机器上的两个应用程序服务器)将能够使用 WS-Coordination 把每个参与者 的行为集在合一起,进一步地,并且通过确保它们完全同意对于在这个协调上下文里它们各自执行的所有行为均产生单一的结果,来 进一步管理这些行为。否则,则不能以一个受控的方式来完成这些功能。

协调上下文可以被看作是一个标识符,行为执行在这个标识符之下。作一比较,这个概念非常类似于事务上下文。当事务工作完成, 在事务上下文里管理它,当调用这个上下文去确定或会滚时这个工作完成。协调上下文包含的附加信息是一个协调标识符、关于协调类型的 详细资料以及包括端口信息以便协调服务能够被访问的协调协议。在下面定义了这些术语。

 

协调服务,或者 协调器(Coordinator), 进一步由三个服务组成: 激活服务(activation service)注册服务(registration service)协调协议(coordination protocol) 服务。激活服务支持 CreateCoodinationContext 操作来允许新的协调上下文存在。注册服务支持 Register 操作来允许参与者 在协调上下文中执行工作。协调协议服务支持协调协议的使用,这个协议定义了协调器(Coordinator)和参与者之间的行为和通信。

协调类型是一个协调行为的固定的集合,这个集合详细说明了协调协议的集合以及协调器(Coordinator)应该如何驱动完成。 WS-Transaction 规范描述了两个协调类型—— 原子事务(Atomic Transaction)(AT)和 业务协定(Business Agreement)(BA)。 这些协调类型中的每一个都包括协调协议。例如,原子事务(Atomic Transaction)协调类型包括像 two-phase commit protocol(Durable2PC)和 phaseZero protocol(Volatile2PC)这样的协调协议。 您可以希望在支持原子事务的环境中使用这两个协议。

业务协定(Business Agreement)协调类型提供了一种不同的功能类型。它被设计成用于更长的时帧。而不像原子事务,正常地您 与它联系一个非常短的生命期。业务协定协议(Business Agreement protocol)的一个例子就是它自己,被称作业务协定(Business Agreement) 它是一个补偿协议。


WS-Coordination 和 WS-Transaction 之间是什么关系?

WS-Coordination 是基本的框架,使参与者之间活动的分布式结果成为可能。WS-Transaction 定义了协调类型,例如原子事务(Atomic Transaction),协调类型使用 WS-Coordination 框架来定义规则。在协调器(Coordinator)和参与者通信时,它们必须遵循这些规则。两者之间的这个区别很重要。

两个应用程序和一个协调器(Coordinator)之间主要的协调流程如下面的 图 1所示。


图 1. 基本协调流程
  1. App1 向在协调器上的激活服务提出一个请求。
  2. 协调器开始一个新的活动,使用它的 CoordinationContext (协调器的 XML 消息)来对 App1 做出响应。
  3. App1向注册服务提出请求来注册使用协调协议X。
  4. App1 以它期望的方式调用 App2,传递 CoordinationContext 给协调器。
  5. App2 向注册服务提出请求(使用诸如端口信息的参数,它们 可以在 App1 传递的 CoordinationContext 中找到)来注册使用协调协议Y。
  6. App2结束它的工作,将控制返还给 App1,活动结束。
  7. 协调器使用协议 X 类型消息响应 App1
  8. 协调器使用协议 Y 类型消息响应 App2


协调器调解

在一个现实世界的情况中,Web 服务可能是事务的和分布式的,协调器的发起者( App1)将 CoordinationContext 传递给任何它所期望的活动中的参与者( App2)。这个上下文的接收者有两种选择:它们可以使用已经创建好了的协调器( Ca),或者如果它们愿意,也可以在初始的 CoordinationContext 中传递创建的新的协调器。然后,第二种选择将新的协调器( Cb) 作为 App2的代理协调器。它将包括与协调器 Ca相同的活动标识符,但是当 App2向它的协调器 Cb注册 Durable2PC 协议的时候,它的请求直接传送给了协调器 Ca。 类似地,在结束时,准备和提交消息在最终到达 App2(它已经注册过 Durable2PC 协议)之前将从协调器 Ca传递给协调器 Cb

请参阅 WS-Transaction 规范的 4.1 节 AT3.1 Example Atomic Transaction Message Flow,在那里您将看到一个应用程序和调解的协调器之间的 WS-Coordination 流程的非常好的示例(请参阅 参考资料)。


Web 服务事务:原子事务(WS-AtomicTransaction)

WS-AtomicTransaction 是一种对于原子事务的特殊的协调类型,它提供了一组协调协议。这些协调协议是:

  • Completion
  • CompletionWithAck
  • Volatile2PC
  • Durable2PC
  • OutcomeNotification

当协调上下文创建以后,协调类型被指定,但是协调协议直到注册时才被指定。任何参与者可以注册任意数目的协调协议,应该发送和接收 由协议定义的恰当的消息。例如,如果一个参与者在协调器中注册了 Durable2PC 协议,当完成时一条准备消息将被发送给这个参与者,它们将被认为以与正常的事务资源相似的方式投票。想要了解这里每个协议的信息和它们的状态图,请查阅 WS-Transaction 规范, 第 4 节 AT3 Coordination protocols(请参阅 参考资料)。


如何能将 JTA 事务和 WS-AtomicTransaction 一起使用?

因为 JTA 和 JTS 是实现相关的,我将使用的这个示例是 WebSphere Application Server V5.0.2 和 WS-Transaction Tech Preview。这个场景将有两台机器,每个上都运行有应用程序服务器,如 图 2 所示。 应用程序服务器A部署并运行一个 Bean Managed Transaction(BMT)EJB 组件。应用程序服务器B部署并运行一个 Web 服务。 EJB 组件通过使用 JTA 提供的接口 UserTransaction 开始一个事务。它对 XA-compliant database 执行事务工作(步骤 1),然后使用 SOAP/HTTP 向在应用程序服务器B上的 Web 服务发送一个请求(步骤 2)。Web 服务对 XA-compliant database 执行工作(步骤 3),然后返回到 EJB 组件(步骤 4),由它再次使用 UserTransaction 接口来提交事务。所有由 EJB 和 Web 服务对数据库执行的事务都已经被包含在一个活动的范围里,这个活动是由协调器恰好在调用 Web 服务(步骤 2)之前创建的,它已经被提交,同时保存着所有的 ACID 特性,它就好像是单一的工作单元。

让我们来看看下面的两个领域——J2EE 领域和 Web 服务领域。在 J2EE 领域里,使用的事务模型是 JTA。在 Web 服务领域里, 使用的事务模型是 WS-AtomicTransaction。WebSphere Application Server 把一个 Java Web 服务看作是一个 J2EE 对象,因此也就 意味着,Web 服务的实现属于 J2EE 领域,而调用属于 Web 服务领域。在 WebSphere 领域,正确地驱动协议总是正在被使用的模型 (JTA 或者 WS-AtomicTransaction)的责任。

图 2 展示了 在一个事务企业应用程序中包含 Web 服务是多么的容易,同时也展示了对于没有费一行代码麻烦就在导入的事务上下文中运行这个 Web 服务的用户来说,它又是多么的无缝。


图 2. 使用 JTA 事务和 WS-AtomicTransaction 事务

请注意:The EJB 组件正运行在一个受管理的环境中(EJB 容器)并且 Web 服务是符合 JSR 109。

它只能和 JTA 一起工作吗?

WS-Coordination 依靠它的基于XML的调用来 利用它本身是 Web 服务的优势。因为用来调用 WS-Coordination 操作的协议是 SOAP,消息内容是 XML 格式的纯文本。这意味着,当使用 HTTP 传递给 Web 服务时,将不能仅仅通过 SOAP 包本身来确定客户的详细资料,例如编程语言。因此,WS-AtomicTransaction 将能够与任何其他的使用任何支持 WS-AtomicTransaction 的编程语言编码的事务服务相连接。

在近来的一个由 IBM 和 Microsoft 主办的 Web 服务演示上,展示了 WS-AtomicTransaction 的这个跨事务服务和编程语言的互操作性。 图 3 展示了一个示范这项技术的场景。

图 3 中有一个.NET 服务器开始一个非 JTA 事务,向两个 WebSphere 应用程序服务器和另外一个.NET 服务器提出了 Web 服务调用请求。每个应用程序服务器都使用它们的底层事务服务来执行事务工作。每次您能够使用 WS-Transaction 调用一个您将转到的 Web 服务。当发起者完成事务,您使用 WS-Transaction 技术来协调每个参与者,确保它们都已完成,就好像它们是单一的工作单元似的。


图 3. 在 Steve Mills 和 Bill Gates 的 Web 服务演示中的一个 WS-AtomicTransaction 场景的示例拓扑。

总结

在本文中,您已经了解到 WS-Coordination 和 WS-Transaction 的基本概念。到现在为止,Web 服务还不能在分布式环境里使用事务。WS-Transaction 允许 Web 服务执行事务工作,这个事务工作作为更广泛的活动生成组件、应用程序服务器、甚至实现的一部分,正如在 IBM 和 Microsoft Web 服务演示中所展示的。

在 WS-Transaction 的支持下,我们能够可靠地使用 Web 服务作为我们的企业应用程序的一部分,因为它已经为事务支持嵌入到其他的企业组件里。

posted @ 2012-03-30 00:21 paulwong 阅读(935) | 评论 (0)编辑 收藏

分布式事务

一、 事务概述

1、 事务协议

WCF相关的事务协议有三个。

1.1. Lightweight protocol

这是个轻量级的本地事务协议,限制管理在一个AppDomain中的事务。不能跨AppDomain边界传播事务,也不能跨服务边界传播事务。跟其他协议比,Lightweight protocol是最有效率的协议。

1.2. OleTx protocol

这个协议可用于跨AppDomain,进程和机器边界,管理两阶段提交的事务协议。这个跨边界的事务协议使用RPC,是二进制的,windows专有的协议,不能穿越防火墙,也不能用于与非windows系统的互操作。在windows环境下的局域网,OleTx协议是很好的分布式事务的管理协议。

1.3. WS-Atomic Transaction (WSAT) protocol

WSATOleTx协议类似,也可以跨AppDomain,进程和机器边界,管理两阶段提交的事务协议。但WSAT不是微软的专有协议,此协议是微软、IBMBEA等公司共同提出的工业标准。此协议也可用于Intranet中,但是更多的是用于在Internet环境下,或者跟非windows系统进行互操作的分布式事务。

说到WSAT协议这里有必要一下分布式事务的发展。

分布式事务:

分布式事务在企业应用中是很重要的一个方面,微软使用MSDTC作为分布式事务管理器,使用OleTx事务协议进行分布式事务管理,但是OleTx事务协议是微软的专有协议,不是公认标准。在分布式环境中异构系统的交互一定要有个公认同意的分布式事务标准才能在异构的系统中实现分布式事务的协调。

l OGMXA事务规范

1994年,开放组织(Open Group)的 X/Open 分布式事务流程(DTP)模型,它定义了 应用程序如何使用 事务管理程序跨多个 资源管理程序来协调分布式事务。如果事务是通过遵循 XA 的事务管理程序来进行协调的,则任何遵循 XA 规范的资源管理程序都可以参与该事务,因此就可以让不同厂商的事务产品可以共同工作。所有遵循 XA 的事务都是分布式事务。XA 既支持一步提交,也支持两阶段提交。

对象管理组(Object Management Group)的通用对象请求代理体系结构对象事务服务(Common Object Request Broker Architecture Object Transaction Service -- 定义遵循规范的流程如何跨多个流程线程将事务上下文从一个流程传播到另一个流程。这种传播使得即使分布式对象运行于来自不同厂商的容器中,也可以在单个事务中合作。CORBA OTS 规范建立在 XA 规范的基础之上。

l OASISBusiness Transaction ProtocolBTP)标准

2001 年, 一个由各大公司(包括惠普公司(Hewlett-PackardHP)、甲骨文公司(Oracle)及BEA公司)组成的联盟开始着手创建 Organization for Advance Structured Information Systems (OASIS) Business Transaction ProtocolBTP)标准。

BTP 不是专门用于 Web 服务的事务处理协议,它的目的是它也能用于其他的环境中。因而,BTP 定义了事务性的 XML 协议,并且必须在规范中指定所有的服务依赖性。

BTP协议相对比较复杂,并且它把原子性事务和长时间运行的商业事务放在一起进行管理,这样它必须解决各种各样不同的问题。它是通过放松限制来这样做的,这表面上给人感觉灵活度很高,但是很多东西就留给实现者去实现了,导致使用比较复杂。

l OASISWS-Transactions

BTP出现后,其他一些行业大公司,包括 IBMMicrosoft BEA,又发布了它们自己的规范: Web 服务事务处理(WS-Transactions),由三部分组成WS-Coordination(事务协调器)、WS-AtomicTransaction(实现原子事务)、WS-BusinessActivity(实现长时间运行的商业事务)。WS-TransactionsBTP好的方面是它将事务协调器独立出来,另外用两个标准在WS-Transactions基础上分别实现原子事务和长时间运行的事务,这样简化了复杂性。

WS-Transactions是专门用于web services的事务规范。

2005年,WS-Transactions发布了第一个版本,Version 1.0

2007年,WS-Transactions发布了Version 1.1,并被OASIS组织接受为标准,成为WS-*标准的一部分。

WCF支持的可互操作的分布式事务协议只有WS-Transactions,并且只实现了WS-CoordinationWS-AtomicTransactionWS-BusinessActivity没被实现。

1.3.1. WS-Coordination

WS-Coordination它描述了一个可扩展的交流协议框架,该框架对分布式的请求进行其请求协议的判断与处理,处理后再将请求向下方的业务处理模块进行分发。

该 框架最大特点是其能够以系统流程代理的身份来处理请求。当不同的请求,无论是内部的同系统的请求或外部的异构系统的请求,经过该框架处理后,再其原有的特 殊的传输代理层上添加了符合本系统信息流规则的本地协议。也就是本系统允许已经存在的事务、工作流或其他服务的请求隐藏其自身独特的传输协议,并可顺利运 行于异构式的系统环境中。

当前WS-Coordination稳定的版本是1.1,同样是OASIS2007年发布的国际标准。现在1.2版本也在起草过程之中。WS-Coordination规范的具体实现需要开发中在基于XML的配置文件中引入WS-Coordination规范的命名空间,如指定<xsschema>字段的值为ws-addr.xsd。由于是基于XML文件来实现,WS-Coordination规范易于实现,且对日后的扩展支持度高。

通过WS-Coordination的使用,一方面通过附加统一的本地协议,在使用不同通信协议的请求到来时,进行统一的协议处理,加快了请求的处理速度,提高了系统的松耦合性;另一方面WS-Coordination规范所定义的框架加强了系统的异构性,使系统不因请求所使用的协议不同而无法处理,这提升了系统整体的兼容性,加强了系统的综合服务能力。

1.3.2. WS-AtomicTransaction

WS-AtomicTransaction 定义了一组特定的协议,这组协议可以插入 WS-Coordination 模型,以实现传统的两阶段原子事务处理协议。注意到原子的两阶段模型只是就涉及的服务而言的非常重要。提供服务的站点或基础体系结构可能大肆宣传两阶段提交,但是却使用一些其他的企业内部模型,比如补偿模型或版本模型。这种自由使简单的两阶段提交模型对于长期运行的 Internet 计算更有用。

WCF实现了WS-AtomicTransaction协议,事务管理器是由MSDTC实现,也就是说在WCF中可以使用WS-AtomicTransaction协议进行分布式事务的管理,并跟其他实现了WS-AtomicTransaction的异构分布式事务互操作。

1.3.3. WS-BusinessActivity

WS-BusinessActivity 定义了一组特定的协议,这组协议可以插入 WS-Coordination 模型,以实现长期运行的、基于补偿的事务处理协议。

WS-BusinessActivity定义的是long-running事务,所谓long-running事务是指那些企业业务流程,需要跨应用、跨企业来完成某个事务,甚至在事务流程中还需要有手工操作的参与,这类事务的完成时间可能以分计,以小时计,甚至可能以天计,这类事务也被称为SAGA

这类事务如果按照事务的ACID的要求去设计,势必造成系统的可用性大大的降低。试想一个由两台服务器一起参与的事务,服务器A发起事务,服务器B参与事务,B的事务需要人工参与,所以处理时间可能很长。如果按照ACID的原则,要保持事务的隔离性、一致性,服务器A中发起的事务中使用到的事务资源将会被锁定,不允许其他应用访问到事务过程中的中间结果,直到整个事务被提交或者回滚。这就造成事务A中的资源被长时间锁定,系统的可用性将不可接受。

WS-BusinessActivity提供了一种基于补偿的long-running的事务处理模型。还是上面的例子,服务器A的事务如果执行顺利,那么事务A就先行提交,如果事务B也执行顺利,则事务B也提交,整个事务就算完成。但是如果事务B执行失败,事务B本身回滚,这时事务A已经被提交,所以需要执行一个补偿操作,将已经提交的事务A执行的操作作反操作,恢复到未执行前事务A的状态。这样的SAGA事务模型,是牺牲了一定的隔离性和一致性的,但是提高了long-running事务的可用性。

目前的WCF中未实现WS-BusinessActivity,在WCF 4.0 beta2中也为实现,估计在WCF 4.0正式版中也不会实现WS-BusinessActivity协议。

2、 事务管理器

管理事务必须有相应的协议外,还必须有个事务管理器,事务管理器通过相应的事务协议对本机的事务进行管理。如果同一个事务需要跨机器,则每台参与事务的机器的事务管理器之间进行相互协调共同完成一个分布式事务。

2.1. LTM

轻型事务管理器Lightweight Transaction Manager,只能管理本地事务,事务在一个AppDomain内。LTM使用Lightweight protocol管理两阶段提交的事务。LTM只能管理单一的可持久化的资源,如果有两个以上的可持久化资源登记到事务中,LTM将被升级到DTC管理器。

2.2. KTM

Vistawindows2008引入了内核事务管理器Kernel Transaction Manager (KTM)

Windows Vista中还引入了两个主要的事务资源,事务NTFS和事务注册表,称作核心资源kernel resource managers (KRM)KTM可以管理这两类资源。

事务性 NTFS,也称为 TxF,使用TxF可以将文件操作纳入到事务管理中,在事务中的的文件操作将同事务中的其他事务资源一样在事务前后保持一致性。

同样事务性注册表,也称作TxR,注册表的操作也可以纳入到事务管理中。

KTMLTM一样,使用Lightweight protocol管理两阶段提交的事务,只能管理本地事务,事务在一个AppDomain内,只能管理单一的可持久化的资源。

2.3. DTC

DTC可以管理任何跨越执行边界的事务,跨AppDomain,跨进程,跨机器,跨服务。DTC可以使用OleTx 或者WSAT事务协议。

DTC既可以管理本地事务,更重要的是它能够管理跨边界的服务。

在使用WCF的场景下,每台运行WCF服务的机器都默认使用DTCDTC建立一个新事务,并跨机器把事务传播到其他机器,发起事务的机器上的事务为根事务,这个机器上的DTC就要负责这个分布式事务的协调任务,负责启动事务,提交事务,和回滚事务。

事务管理器可使用的事务协议:

Protocal

LTM

KTM

DTC

Lightweight protocol

Yes

Yes

No

OleTx protocol

No

No

Yes

WS-Atomic Transaction

No

No

Yes

3、 事务资源

在事务范围内,可纳入事务管理的资源,即可以在事务正常执行后提交生效,在事务失败可以回滚恢复到事务启动前状态的资源成为事务资源。最常见的事务资源就是常用的数据库操作。但是在vista开始引入了两个核心事务资源:TxF事务文件和TxR事务注册表。

可用的事务资源:

3.1. sql server事务资源

Sql 2005sql 2008的事务资源是新sql serverLTM事务管理器可以管理这类事务。

3.2. 核心事务资源

Vista开始引入的TxF事务文件和TxR事务注册表。可以对文件和注册表进行事务性操作。KTM可以管理这类资源。

3.3. 传统事务资源

SQL Server 2000, Oracle, DB2, MSMQ这类资源是传统的事务资源,这些资源只能由DTC事务管理器进行管理。

事务管理器可管理的事务资源:

Resource

LTM

KTM

DTC

Sql Server事务资源

Yes

No

Yes

核心事务资源

No

Yes

Yes

传统事务资源

No

No

Yes

4、 事务管理器的升级

每个WCF中启动的事务都是先由LTM事务管理器管理,一旦事务中出现现有事务管理器无法管理的资源或情况,WCF会提升事务管理器的级别。分别从LTM升级到KTM,最高级别升级到DTC。事务管理器可以多次升级,事务管理器一旦升级后不能降级。

事务管理器的升级规则:

image

posted @ 2012-03-30 00:20 paulwong 阅读(1560) | 评论 (0)编辑 收藏

开始Spring MVC

建立一个web project,并导入spring 3.x的jar包配置web.xml根据上面的spring mvc流程图,我们知道DispatcherServlet是spring mvc 的一个前端控制器,所以我们当然要去配置它,以便于将请求转给DispatcherServlet处理

  <servlet>
    
<servlet-name>dispatcher</servlet-name>
    
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
<init-param>
        
<param-name>contextConfigLocation</param-name>
<!-- 如果配置文件位于classpath下,可以这么写: classpath:dispatcher.xml -->
        
<param-value>/WEB-INF/dispatcher.xml</param-value>
    
</init-param>
    
<load-on-startup>1</load-on-startup>
  
</servlet>

  
<servlet-mapping>
    
<servlet-name>dispatcher</servlet-name>
    
<url-pattern>/</url-pattern>
  
</servlet-mapping>

注:由于DispatcherServlet在初始化的过程中需要一个配置文件来生产文件中的各种bean,并生成WebApplicationContext对象,保存到ServletContext中(如果DispatcherServlet有多个,那么每一个DispatcherServlet都对应一个WebApplicationContext),我们可以在Servlet的init-param中配置配置文件的路径,当然如果我们没有配置Init-Param,它会默认到WEB-INF的文件夹中找[servletname]-servlet.xml文件,例如上面如果我们没有配置,则会去寻找dispatcher-servlet.xml这个配置文件。(在init-param中我们可以指定多个配置文件,用逗号分隔也可以使用通配符*)
配置上文中我们指定的所需的dispatcher.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context
="http://www.springframework.org/schema/context"
xmlns:mvc
="http://www.springframework.org/schema/mvc"
xsi:schemaLocation
="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"
>

    
<context:component-scan base-package="com.controls" />
    
<mvc:annotation-driven />
    
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
       
<property name="prefix" value="/WEB-INF/views/"></property>
       
<property name="suffix" value=".jsp"></property>
       
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
    
</bean>
</beans>



配置文件说明:
<context:component-scan base-package="com.controls" />
由于我们使用了基于注解的Controller,所以这里指定了需要被扫描的包路径,如果有多个可以使用逗号分隔
<mvc:annotation-driven />

上面的spring mvc流程图中我们知道DispatcherServlet接管请求后,会由HandlerMapping来执行映射,所以我们需要注册HanlderMapping,比如上面的标签会自动注册比如DefaultAnnotationHandlerMapping(执行请求到Controller的映射)和AnnotationMethodHandlerAdapter(调用controller中的方法)这样的bean,当然这个标签还提供了其他的一些支持(更多介绍请参照spring官方文档第455页)。

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

       
<property name="prefix" value="/WEB-INF/views/"></property>

       
<property name="suffix" value=".jsp"></property>

       
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>

</bean>

上面spring mvc流程图的最后controller处理完毕后会交给一个ViewResolver进行处理,大体上是解析视图逻辑名并经过一定的处理获取一个视图,这里的意思是设置视图用jsp来处理(比如我们设置viewClass为JstlView)来进行处理,就是以forward的形式转发给jsp,这个地址是:/WEB-INF/views/[controller返回值].jsp ,当然视图解析器可以定义多个,(视图解析器不会处理ModelAndView中已经设置了View的请求,因为已经有View来处理了,那当然就不需要它来解析到真正的视图View啦)
编写Controller控制器这里我们使用例子的形式来说明

实现一个控制器类@Controller
@Controller
@RequestMapping(
"/user")
public class UserControl {

}

只要给类加上Controller的注解,并放在被扫描的包中,那么这个类就是一个控制器了,RequestMapping表明匹配的路径,可以写在类或者类的方法上,如果类上面有RequestMapping注解,那么其下面的方法就是相对于类上面注解地址的一个相对路径


定义一个接管请求的方法方法名:无任何要求
--------------------------------------------------------------
参数:(顺序以及个数无任何要求)
HttpServletRequest
HttpServletResponse
PrintWriter 相当于HttpResponse.getWriter()获得的对象
Map 其实是获得了ModelAndView中的Model
BindingResult 绑定数据的处理结果
HttpSession 如果使用此参数,需要注意如果是第一次访问还没有session的话会报错
@PathVariable 用于接收路径变量
@RequestParam 相当于调用request.getParameter(“name”)方法
@CookieValue 获取cookie的值
@RequestHeader 获取header中的值
实体对象 会根据请求的参数名,注入到这个对象对于得属性中,必须提供set方法
等等等等等
--------------------------------------------------------------
返回值:
void
返回值是void,如果接收了PrintWriter 或者 HttpServletResponse 参数,那么返回的ModelAndView就是null,也就是直接将输出流输出到客户端,如果方法没有接收输出参数,后面会默认生成一个视图名

String 视图逻辑名

ModelAndView 是一个视图逻辑名+Map的封装类
其他任意类型 存入ModelAndView中的Model
--------------------------------------------------------------

不管我们的返回类型是什么,返回值会通过处理,最终返回一个ModelAndView或者null
例1:给方法定义一个请求映射并使用路径变量 @RequestMapping("/id/{userid}/name/{username}")

@RequestMapping("/id/{userid}/name/{username}")
public String queryUser(@PathVariable("userid"long userID, @PathVariable("username") String userName, Map<String, User> model) {
       User user 
= new User();
       user.setUserID(userID);
       user.setUserName(userName);
       model.put(
"userInfo", user);
       
return "Home";
    }


@RequestMapping定义路由映射,其中{userid} {username} 是PathVariable(路径变量)
这样我们只需访问 http://localhost:8080/SpringMVC/user/id/10001/name/liudehua 就能进入上面的方法
RequestMapping还可以使用通配符,如: /test/*/name/{name}

例2:接受请求参数@RequestMapping("/save")
@RequestMapping("/save")
public String save(@RequestParam("userName") String userName,@RequestParam("Age"int age) {
       System.out.println(userName);
       System.out.println(age);
       
return "Home";
}


例3:请求参数直接注入到Model@RequestMapping("/save")
@RequestMapping("/save")
public String save(User user) {
       System.out.println(user.getUserID());
       System.out.println(user.getUserName());
       
return "Home";
}

例4:转发与重定向转发: (相当于 request.getRequestDispatcher(“”).forward(request, response))
return “forward:/user/test”;

重定向: (相当于response.redirect(“”))
return “redirect:/user/test”
return “redirect:http://www.google.com.hk”;

例5:根据HttpMethod来过滤请求
@RequestMapping(params="hello=world", method={RequestMethod.GET, RequestMethod.POST}
public String helloworld() {
}
 


例6:根据参数来进行过滤
@RequestMapping(params="hello=world", method={RequestMethod.GET, RequestMethod.POST})  
public String helloworld() {
}
  

必须有个hello的参数并且名称为world,而且只能是get或post请求才会进入此方法

http://www.cnblogs.com/zhaoyang/archive/2012/01/07/2315425.html

posted @ 2012-03-29 19:06 paulwong 阅读(1159) | 评论 (0)编辑 收藏

Joda-Time 简介

假设我希望输出这样一个日期:距离 Y2K 45 天之后的某天在下一个月的当前周的最后一天的日期。坦白地说,我甚至不想使用 Calendar 处理这个问题。使用 JDK 实在太痛苦了,即使是简单的日期计算,比如上面这个计算。正是多年前的这样一个时刻,我第一次领略到 Joda-Time 的强大。使用 Joda,用于计算的代码如清单 3 所示:

DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(
45).plusMonths(1).dayOfWeek() .withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");

详见:
http://www.oschina.net/question/12_7643

posted @ 2012-03-27 17:09 paulwong 阅读(296) | 评论 (0)编辑 收藏

仅列出标题
共115页: First 上一页 84 85 86 87 88 89 90 91 92 下一页 Last