soap rest和soap区别可以集成吗

基于SOAP/REST混合式SOA的输变电设备信息集成平台--《机电工程》2014年04期
基于SOAP/REST混合式SOA的输变电设备信息集成平台
【摘要】:针对电力系统中存在的多源、异构信息汇聚、融合及全景应用困难的问题,提出了一种基于混合式SOA的解决方案。设计了混合使用SOAP/WSDL和REST两种不同实现技术、综合SOAP/WSDL机制完善和REST简洁高效两种优势的面向服务构架,并分析阐述了电力系统中的多种源数据的提取方法、CIM等关键数据格式的解析方法、SOAP/WSDL服务和REST服务的实现与封装方法等。搭建了一个输变电设备全景信息集成平台,对需要使用的一些关键技术进行了技术研究与验证。该平台中,将数据集成业务封装为REST服务,再与其他上层SOAP/WSDL服务相配合,共同发布到企业服务总线上完成了流程编排,从而完成了相应功能。研究结果表明,由于混合使用了两种不同的SOA实现技术并能按需对服务进行替换或编排,业务功能实现上具有高效、灵活的优势。
【作者单位】:
【关键词】:
【基金】:
【分类号】:TP393.09【正文快照】:
0引言输变电设备作为电力系统的重要组成部分,在运行中涉及到诸多信息的采集、传输和处理。这些信息包括与输电线路安全运行密切相关的气象预警和故障分析数据、变电站内各种监测数据等。由于来自不同厂商的软件往往使用不同的编程语言、数据格式及信息模型,使得电力系统中包
欢迎:、、)
支持CAJ、PDF文件格式,仅支持PDF格式
【参考文献】
中国期刊全文数据库
文耀平;[J];包装与食品机械;1994年02期
唐跃中;曹晋彰;郭创新;曹一家;韩祯祥;;[J];电力系统自动化;2008年14期
王继业;张崇见;;[J];电网技术;2006年09期
王珊;刘毅;郭创新;张扬;金文德;;[J];机电工程;2008年07期
王建斌;胡小生;李康君;赵靓;;[J];计算机应用与软件;2010年09期
郭创新;高振兴;张健;刘桂伶;毕建权;;[J];控制工程;2011年06期
何洁芳;;[J];机电工程技术;2013年12期
【共引文献】
中国期刊全文数据库
王青青;高伟庆;;[J];电力建设;2010年10期
王继业;;[J];电力系统自动化;2006年20期
黄小庆;周宇;吴含前;夏安邦;;[J];电力系统自动化;2009年04期
任开银;孔震;叶敏;;[J];电力系统自动化;2009年20期
陈建英;刘心松;唐宏斌;谭颖;;[J];电力系统自动化;2010年07期
周恒俊;曹晋彰;郭创新;曹一家;;[J];电力系统自动化;2010年13期
郭磊;郭创新;曹一家;朱传柏;潘坚跃;;[J];电力系统自动化;2010年20期
周恒俊;曹培;张金江;郭创新;曹晋彰;曹一家;;[J];电力系统自动化;2011年11期
曹晋彰;王扬;朱承治;张扬;郭创新;曹一家;;[J];电力系统自动化;2012年02期
翟长国;黄小鉥;叶剑斌;谢敏;许晓慧;丁孝华;;[J];电力系统自动化;2012年03期
中国重要会议论文全文数据库
申侃;梁昌勇;赵树平;;[A];第八届(2013)中国管理学年会论文集(选编)[C];2013年
中国博士学位论文全文数据库
张珊;[D];华东师范大学;2011年
周云成;[D];沈阳农业大学;2011年
张金江;[D];浙江大学;2009年
唐跃中;[D];浙江大学;2010年
屈志坚;[D];北京交通大学;2012年
刘廉如;[D];北京邮电大学;2012年
郑含博;[D];重庆大学;2012年
曹晋彰;[D];浙江大学;2013年
郭磊;[D];浙江大学;2012年
戴建国;[D];石河子大学;2013年
中国硕士学位论文全文数据库
王磊;[D];合肥工业大学;2010年
黄缙华;[D];华北电力大学(北京);2011年
张俊;[D];华北电力大学;2011年
陈建伟;[D];华北电力大学;2011年
郭健;[D];湖南科技大学;2011年
孙长青;[D];中山大学;2011年
张亨瑞;[D];上海交通大学;2012年
何伟;[D];电子科技大学;2011年
胡俊杰;[D];浙江大学;2012年
袁鹏;[D];浙江大学;2012年
【二级参考文献】
中国期刊全文数据库
郎风华;谷利泽;杨义先;钮心忻;;[J];北京邮电大学学报;2008年03期
董越,孙宏斌,吴文传,张伯明,刘崇茹;[J];电力系统自动化;2002年03期
张慎明,刘国定;[J];电力系统自动化;2002年14期
方烁;梁成辉;徐庆平;云昌钦;;[J];电力系统自动化;2006年15期
李向荣;郝悍勇;樊涛;唐跃中;;[J];电力系统自动化;2007年17期
廖瑞金;王谦;骆思佳;廖玉祥;孙才新;;[J];电力系统自动化;2008年03期
朱永利;申涛;李强;;[J];电力系统及其自动化学报;2008年06期
袁赟;;[J];电脑知识与技术(学术交流);2007年21期
王明俊;[J];电网技术;1999年10期
王风萍,刘晋萍,白毅;[J];电网技术;2000年11期
【相似文献】
中国期刊全文数据库
周洪建;[J];微机发展;2003年10期
吴文明,瞿裕忠,董逸生;[J];计算机应用与软件;2004年03期
余伟红,钟艳花;[J];现代电子技术;2005年02期
,郑时雄;[J];微计算机信息;2003年08期
梁为;[J];现代计算机;2003年04期
魏涛,蒋本珊;[J];计算机应用;2004年01期
刘爱琴,葛君伟,刘松柏;[J];计算机应用研究;2004年03期
白显羽,李村合,张培颖;[J];福建电脑;2004年08期
张文斌,陈恩红;[J];计算机应用;2002年05期
龚新浩,熊齐邦;[J];计算机工程;2003年22期
中国重要会议论文全文数据库
王聪;钟尚勤;徐国胜;;[A];第十三届中国科协年会第11分会场-中国智慧城市论坛论文集[C];2011年
程尊平;张谧;郑骥枥;王晨;汪卫;施伯乐;;[A];第二十届全国数据库学术会议论文集(研究报告篇)[C];2003年
曾春;邢春晓;李蕾;周立柱;;[A];第十八届全国数据库学术会议论文集(研究报告篇)[C];2001年
陈伟杰;林宏基;;[A];全国第16届计算机科学与技术应用(CACIS)学术会议论文集[C];2004年
;[A];Proceedings 2010 IEEE 2nd Symposium on Web Society[C];2010年
任毅;张勇;李晓峰;;[A];第九届全国青年通信学术会议论文集[C];2004年
王俊;于爱荣;曹雷;刘晓明;;[A];中国航空学会信号与信息处理专业全国第八届学术会议论文集[C];2004年
董丽丽;祁飞;罗婵;;[A];2006年全国开放式分布与并行计算学术会议论文集(二)[C];2006年
周琳;陈庆奎;;[A];2007年全国开放式分布与并行计算机学术会议论文集(上册)[C];2007年
董蒙;李庆忠;钱斌;;[A];全国第16届计算机科学与技术应用(CACIS)学术会议论文集[C];2004年
中国重要报纸全文数据库
金华;[N];中国计算机报;2003年
庞引明;[N];计算机世界;2005年
;[N];网络世界;2005年
北京.NET用户协会主席 董洵;[N];计算机世界;2004年
叶子平;[N];计算机世界;2004年
柴晓路;[N];计算机世界;2002年
WS-I工作组成员 柴晓路;[N];计算机世界;2003年
中国科学院软件研究所 刘绍华;[N];计算机世界;2005年
张福贞;[N];网络世界;2003年
程士寅;[N];中国电脑教育报;2004年
中国博士学位论文全文数据库
冯新扬;[D];解放军信息工程大学;2009年
熊光彩;[D];西北工业大学;2002年
赵逢禹;[D];复旦大学;2010年
张功萱;[D];南京理工大学;2005年
姚绍文;[D];电子科技大学;2002年
颜学雄;[D];解放军信息工程大学;2008年
中国硕士学位论文全文数据库
余晓峰;[D];浙江大学;2003年
杨俊超;[D];广东工业大学;2003年
娄小广;[D];合肥工业大学;2010年
刘立功;[D];西北工业大学;2003年
曹勇;[D];武汉理工大学;2004年
苏会杰;[D];北京邮电大学;2012年
杨艺清;[D];中南大学;2005年
许勇;[D];四川大学;2004年
费春;[D];电子科技大学;2005年
盛雨;[D];大连理工大学;2005年
&快捷付款方式
&订购知网充值卡
400-819-9993
《中国学术期刊(光盘版)》电子杂志社有限公司
同方知网数字出版技术股份有限公司
地址:北京清华大学 84-48信箱 知识超市公司
出版物经营许可证 新出发京批字第直0595号
订购热线:400-819-82499
服务热线:010--
在线咨询:
传真:010-
京公网安备74号REST 基础(2):Web 服务编程,REST 与 SOAP_Spring事务管理及与mybatis3调整的事务管理_Struts.xml要害的配置__脚本百事通
稍等,加载中……
^_^请注意,有可能下面的2篇文章才是您想要的内容:
REST 基础(2):Web 服务编程,REST 与 SOAP
Spring事务管理及与mybatis3调整的事务管理
Struts.xml要害的配置
REST 基础(2):Web 服务编程,REST 与 SOAP
REST 基础(二):Web 服务编程,REST 与 SOAP
应用场景介绍(在线用户管理)本文将借助于一个应用场景,通过基于 REST 和 SOAP Web 服务的不同实现,来对两者进行对比。该应用场景的业务逻辑会尽量保持简单且易于理解,以有助于把我们的重心放在 REST 和 SOAP Web 服务技术特质对比上。 需求描述这是一个在线的用户管理模块,负责用户信息的创建,修改,删除,查询。用户的信息主要包括: 用户名(唯一标志在系统中的用户) 头衔 公司 EMAIL 描述 需求用例图如下:如图 1 所示,客户端 1(Client1)与客户端 2(Client2)对于信息的存取具有不同的权限,客户端 1 可以执行所有的操作,而客户端 2 只被允许执行用户查询(Query User)与用户列表查询(Query User List)。关于这一点,我们在对 REST Web 服务与 SOAP Web 服务安全控制对比时会具体谈到。下面我们将分别向您介绍如何使用 REST 和 SOAP 架构实现 Web 服务。使用 REST 实现 Web 服务本部分将基于 Restlet 框架来实现该应用。Restlet 为那些要采用 REST 结构体系来构建应用程序的 Java 开发者提供了一个具体的解决方案。关于更多的 Restlet 相关内容,本文不做深入讨论,请见参考资源列表。设计我们将采用遵循 REST 设计原则的 ROA(Resource-Oriented Architecture,面向资源的体系架构)进行设计。ROA 是什么?简单点说,ROA 是一种把实际问题转换成 REST 式 Web 服务的方法,它使得 URI、HTTP 和 XML 具有跟其他 Web 应用一样的工作方式。在使用 ROA 进行设计时,我们需要把真实的应用需求转化成 ROA 中的资源,基本上遵循以下的步骤:分析应用需求中的数据集。 映射数据集到 ROA 中的资源。 对于每一资源,命名它的 URI。 为每一资源设计其 Representations。 用 hypermedia links 表述资源间的联系。 接下来我们按照以上的步骤来设计本文的应用案例。在线用户管理所涉及的数据集就是用户信息,如果映射到 ROA 资源,主要包括两类资源:用户及用户列表。用户资源的 URI 用 http://localhost:8182/v1/users/{username} 表示,用户列表资源的 URI 用 http://localhost:8182/v1/users 表示。它们的 Representation 如下,它们都采用了如清单 1 和清单 2 所示的 XML 表述方式。清单 1. 用户列表资源 Representation
&?xml version="1.0" encoding="UTF-8" standalone="no"?&
&name&tester&/name&
&link&http://localhost:8182/v1/users/tester&/link&
&name&tester1&/name&
&link&http://localhost:8182/v1/users/tester1&/link&
清单 2. 用户资源 Representation&?xml version="1.0" encoding="UTF-8" standalone="no"?&
&name&tester&/name&
&title&software engineer&/title&
&company&IBM&/company&
&email&tester@&/email&
&description&testing!&/description&
客户端通过 User List Resource 提供的 LINK 信息 ( 如 : &link&http://localhost:8182/v1/users/tester&/link& ) 获得具体的某个 USER ResourceRestful Web 服务架构首先给出 Web 服务使用 REST 风格实现的整体架构图,如下图所示:图 2. REST 实现架构接下来,我们将基于该架构,使用 Restlet 给出应用的 RESTful Web 服务实现。下面的章节中,我们将给出 REST Web 服务实现的核心代码片段。关于完整的代码清单,读者可以通过资源列表下载。客户端实现清单 3 给出的是客户端的核心实现部分,其主要由四部分组成:使用 HTTP PUT 增加、修改用户资源,使用 HTTP GET 得到某一具体用户资源,使用 HTTP DELETE 删除用户资源,使用 HTTP GET 得到用户列表资源。而这四部分也正对应了图 2 关于架构描述的四对 HTTP 消息来回。关于 UserRestHelper 类的完整实现,请读者参见本文所附的代码示例清单 3. 客户端实现public class UserRestHelper {
//The root URI of our ROA implementation.
public static final tring APPLICATION_URI = "http://localhost:8182/v1";
//Get the URI of user resource by user name.
private static String getUserUri(String name) {
return APPLICATION_URI + "/users/" +
//Get the URI of user list resource.
private static String getUsersUri() {
return APPLICATION_URI + "/users";
//Delete user resource from server by user name.
//使用 HTTP DELETE 方法经由 URI 删除用户资源
public static void deleteFromServer(String name) {
Response response = new Client(Protocol.HTTP).delete(getUserUri(name));
//Put user resource to server.
//使用 HTTP PUT 方法经由 URI 增加或者修改用户资源
public static void putToServer(User user) {
//Fill FORM using user data.
Form form = new Form();
form.add("user[title]", user.getTitle());
form.add("user[company]", user.getCompany());
form.add("user[email]", user.getEmail());
form.add("user[description]", user.getDescription());
Response putResponse = new Client(Protocol.HTTP).put(
getUserUri(user.getName()), form.getWebRepresentation());
//Output user resource to console.
public static void printUser(String name) {
printUserByURI(getUserUri(name));
//Output user list resource to console.
//使用 HTTP GET 方法经由 URI 显示用户列表资源
public static void printUserList() {
Response getResponse = new Client(Protocol.HTTP).get(getUsersUri());
if (getResponse.getStatus().isSuccess()) {
DomRepresentation result = getResponse.getEntityAsDom();
//The following code line will explore this XML document and output
//each user resource to console.
System.out.println("Unexpected status:"+ getResponse.getStatus());
//Output user resource to console.
//使用 HTTP GET 方法经由 URI 显示用户资源
private static void printUserByURI(String uri) {
Response getResponse = new Client(Protocol.HTTP).get(uri);
if (getResponse.getStatus().isSuccess()) {
DomRepresentation result = getResponse.getEntityAsDom();
//The following code line will explore this XML document and output
//current user resource to console.
System.out.println("unexpected status:"+ getResponse.getStatus());
服务器端实现清单 4 给出的是服务器端对于用户资源类(UserResourc)的实现,其核心的功能是响应有关用户资源的 HTTP GET/PUT/DELETE 请求,而这些请求响应逻辑正对应了 UserRestHelper 类中关于用户资源类的 HTTP 请求。清单 4. 服务器端实现public class UserResource extends Resource {
private User _
private String _userN
public UserResource(Context context, Request request, Response response) {
//Constructor is here.
//响应 HTTP DELETE 请求逻辑
public void delete() {
// Remove the user from container.
getContainer().remove(_userName);
getResponse().setStatus(Status.SUCCESS_OK);
//This method will be called by handleGet.
public Representation getRepresentation(Variant variant) {
Representation result =
if (variant.getMediaType().equals(MediaType.TEXT_XML)) {
Document doc = createDocument(this._user);
result = new DomRepresentation(MediaType.TEXT_XML, doc);
//响应 HTTP PUT 请求逻辑。
public void put(Representation entity) {
if (getUser() == null) {
//The user doesn't exist, create it
setUser(new User());
getUser().setName(this._userName);
getResponse().setStatus(Status.SUCCESS_CREATED);
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
//Parse the entity as a Web form.
Form form = new Form(entity);
getUser().setTitle(form.getFirstValue("user[title]"));
getUser().setCompany(form.getFirstValue("user[company]"));
getUser().setEmail(form.getFirstValue("user[email]"));
getUser().setDescription(form.getFirstValue("user[description]"));
//Put the user to the container.
getApplication().getContainer().put(_userName, getUser());
//响应 HTTP GET 请求逻辑。
public void handleGet() {
super.handleGet();
if(this._user != null ) {
getResponse().setEntity(getRepresentation(
new Variant(MediaType.TEXT_XML)));
getResponse().setStatus(Status.SUCCESS_OK);
getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
//build XML document for user resource.
private Document createDocument(User user) {
//The following code line will create XML document according to user info.
//The remaining methods here
UserResource 类是对用户资源类的抽象,包括了对该资源的创建修改(put 方法),读取(handleGet 方法 )和删除(delete 方法),被创建出来的 UserResource 类实例被 Restlet 框架所托管,所有操纵资源的方法会在相应的 HTTP 请求到达后被自动回调。另外,在服务端,还需要实现代表用户列表资源的资源类 UserListResource,它的实现与 UserResource 类似,响应 HTTP GET 请求,读取当前系统内的所有用户信息,形成如清单 1 所示的用户列表资源 Representation,然后返回该结果给客户端。具体的实现请读者参见本文所附的代码示例。使用 SOAP 实现 Web 服务本文对于 SOAP 实现,就不再像 REST 那样,具体到代码级别的实现。本节将主要通过 URI,HTTP 和 XML 来宏观上表述 SOAP Web 服务实现的技术本质,为下一节 REST Web 服务与 SOAP Web 服务的对比做铺垫。SOAP Web 服务架构同样,首先给出 SOAP 实现的整体架构图,如下图所示:图 3. SOAP 实现架构可以看到,与 REST 架构相比,SOAP 架构图明显不同的是:所有的 SOAP 消息发送都使用 HTTP POST 方法,并且所有 SOAP 消息的 URI 都是一样的,这是基于 SOAP 的 Web 服务的基本实践特征。获得用户信息列表基于 SOAP 的客户端创建如清单 5 所示的 SOAP XML 文档,它通过类 RPC 方式来获得用户列表信息。清单 5. getUserList SOAP 消息&?xml version="1.0" encoding="UTF-8" standalone="no"?&
&soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&
&soap:Body&
&p:getUserList xmlns:p=""/&
&/soap:Body&
&/soap:Envelope& 客户端将使用 HTTP 的 POST 方法,将上述的 SOAP 消息发送至 http://localhost:8182/v1/soap/servlet/messagerouter URI,SOAP SERVER 收到该 HTTP POST 请求,通过解码 SOAP 消息确定需要调用 getUserList 方法完成该 WEB 服务调用,返回如下的响应:清单 6. getUserListResponse 消息&?xml version="1.0" encoding="UTF-8" standalone="no"?&
&soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&
&soap:Body&
UserListResponse xmlns:p=""&
&username&tester&username&
&username&tester1&username&
&p: getUserListResponse &
&/soap:Body&
&/soap:Envelope&
获得某一具体用户信息清单 7. getUserByName SOAP 消息&?xml version="1.0" encoding="UTF-8" standalone="no"?&
&soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&
&soap:Body&
&p:getUserByName xmlns:p=""&
&username&tester&/username&
&/p:getUserByName &
&/soap:Body&
&/soap:Envelope& 同样地,客户端将使用 HTTP 的 POST 方法,将上述的 SOAP 消息发送至 http://localhost:8182/v1/soap/servlet/messagerouter URI,SOAP SERVER 处理后返回的 Response 如下清单 8. getUserByNameResponse SOAP 消息&?xml version="1.0" encoding="UTF-8" standalone="no"?&
&soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&
&soap:Body&
&p:getUserByNameResponse xmlns:p=""&
&name&tester&/name&
&title&software engineer&/title&
&company&IBM&/company&
&email&tester@&/email&
&description&testing!&/description&
&/p:getUserByNameResponse&
&/soap:Body&
&/soap:Envelope& 实际上,创建新的用户,过程也比较类似,在这里,就不一一列出,因为这两个例子对于本文在选定的点上对比 REST 与 SOAP 已经足够了。REST 与 SOAP 比较:略实例参加下面附件code_rest.zipfrom:/developerworks/cn/webservices/0907_rest_soap/
Spring事务管理及与mybatis3调整的事务管理
Spring事务管理及与mybatis3整合的事务管理
数据访问事务处理 in Spring+Mybatis3.0
事务---保证了用户的每一次操作都是可靠的,即使出现了异常的访问,也不至于破坏后台数据的完整性;
Java事务简介
事务必须符合ISO/IEC所定制的ACID原则
A(atomicity):原子性
在事务执行的过程中,任何的失败就将导致事务的任何修改失效,
C(consistency):一致性
事务回滚时,事务所执行的内容必须恢复到初始状态,即事务执行前的状态
I(isolation):隔离性
事务执行过程中对数据的修改,在提交之前的数据对其他事务不可见
D(durability):持久性
已经提交的数据在事务执行失败时,数据的状态都是正确的.
全局事务(分布式事务):
由应用服务器来管理(如JTA),同时可以用于多个事务性的资源;
本地事务和资源相关,主要通过JDBC来实现
在实际应用中,存在一种容器管理事务,容器事务主要是由javaEE应用服务器提供,容器事务大多给予JTA完成,事实上这是在容器中覆盖了JDBC和JTA事务.
事务特性分析(use spring)
TransactionDefinition 接口来定义事务属性。
interface TransactionDefinition{
int getIsolationLevel();
int getPropagationBehavior();
int getTimeout();
boolean isReadOnly();
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT(默认值):表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
隔离级别定义了事务与事务之间的隔离程度。
隔离级别与并发性是互为矛盾的:隔离程度越高,数据库的并发性越差;隔离程度越低,数据库的并发性越好。
ANSI/ISO SQL92标准定义了一些数据库操作的隔离级别:
未提交读(read uncommitted)
提交读(read committed)
重复读(repeatable read)
序列化(serializable)
通过一些现象,可以反映出隔离级别的效果。这些现象有:
更新丢失(lost update):当系统允许两个事务同时更新同一数据是,发生更新丢失。
脏读(dirty read):当一个事务读取另一个事务尚未提交的修改时,产生脏读。
非重复读(nonrepeatable read):同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生非重复读。(A transaction rereads data it has previously read and finds that another committed transaction has modified or deleted the data.)
幻读(phantom read):同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻像读。(A transaction reexecutes a query returning a set of rows that satisfies a search condition and finds that another committed transaction has inserted additional rows that satisfy the condition.)
隔离级别影响部分:
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:
创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:
以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
此处涉及到【嵌套事务】
内部事务依赖于外部事务
外部事务的提交和回滚直接影响到内部事务(内部事务不是独立的)
指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
事务的只读属性(readOnly)
对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。
事务的回滚规则
通常情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。如果没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这通常也是大多数开发者希望的处理方式,也是 EJB 中的默认处理方式。但是,我们可以根据需要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。
TransactionDefinition、PlatformTransactionManager、TransactionStatus
事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。“给定的事务规则”就是用TransactionDefinition 表示的,“按照……来执行提交或者回滚操作”便是用 PlatformTransactionManager 来表示,而 TransactionStatus 用于表示一个运行着的事务的状态。
PlatformTransactionManager 用于执行具体的事务操作。
Public interface PlatformTransactionManager{
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionE
void commit(TransactionStatus status)throws TransactionE
void rollback(TransactionStatus status)throws TransactionE
根据底层所使用的不同的持久化 API 或框架,PlatformTransactionManager 的主要实现类大致如下:
DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。
HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。
JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。
另外还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等等。
如果我们使用JTA进行事务管理,我们可以通过 JNDI 和 Spring 的 JtaTransactionManager 来获取一个容器管理的 DataSource。JtaTransactionManager 不需要知道 DataSource 和其他特定的资源,因为它将使用容器提供的全局事务管理。而对于其他事务管理器,比如DataSourceTransactionManager,在定义时需要提供底层的数据源作为其属性,也就是 DataSource。与 HibernateTransactionManager 对应的是 SessionFactory,与 JpaTransactionManager 对应的是 EntityManagerFactory 等等。
TransactionStatus
PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。TransactionStatus 接口提供了一个简单的控制事务执行和查询事务状态的方法。
interface TransactionStatus{
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
Spring 的编程式事务管理
在 Spring 出现以前,编程式事务管理对基于 POJO 的应用来说是唯一选择。用过 Hibernate 的人都知道,需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。通过 Spring 提供的事务管理 API,我们可以在代码中灵活控制事务的执行。在底层,Spring 仍然将事务操作委托给底层的持久化框架来执行。
声明式事务管理(方便代码维护,无污染,无重复代码,但是粒度控制不够)
Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。因为事务管理本身就是一个典型的横切逻辑,正是 AOP 的用武之地。
通常情况下,笔者强烈建议在开发中使用声明式事务,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大方便后期的代码维护。
基于transactionIntercepter
&bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"&
&property name="transactionManager" ref="transactionManager"/&
&property name="transactionAttributes"&
&prop key="transfer"&PROPAGATION_REQUIRED,-XXException,+xxxException&/prop&
&/property&
&bean id="serviceTarget" class="com.ncs.test.TestService"&
&property name="testDao" ref="testDao"/&
&bean id="service"
class="org.springframework.aop.framework.ProxyFactoryBean"&
&property name="target" ref=" serviceTarget "/&
&property name="interceptorNames"&
&idref bean="transactionInterceptor"/&
&/property&
我们配置了一个 TransactionInterceptor 来定义相关的事务规则,他有两个主要的属性:一个是 transactionManager,用来指定一个事务管理器,并将具体事务相关的操作委托给它;另一个是 Properties 类型的 transactionAttributes 属性,它主要用来定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性。
指定事务属性的取值有较复杂的规则:
传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]
传播行为是唯一必须设置的属性,其他都可以忽略,Spring为我们提供了合理的默认值。
传播行为的取值必须以“PROPAGATION_”开头,具体包括:PROPAGATION_MANDATORY、PROPAGATION_NESTED、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED、PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS,共七种取值。
隔离级别的取值必须以“ISOLATION_”开头,具体包括:ISOLATION_DEFAULT、ISOLATION_READ_COMMITTED、ISOLATION_READ_UNCOMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE,共五种取值。
如果事务是只读的,那么我们可以指定只读属性,使用“readOnly”指定。否则我们不需要设置该属性。
超时属性的取值必须以“TIMEOUT_”开头,后面跟一个int类型的值,表示超时时间,单位是秒。
不影响提交的异常是指,即使事务中抛出了这些类型的异常,事务任然正常提交。必须在每一个异常的名字前面加上“+”。异常的名字可以是类名的一部分。比如“+RuntimeException”、“+tion”等等。
导致回滚的异常是指,当事务中抛出这些类型的异常时,事务将回滚。必须在每一个异常的名字前面加上“-”。异常的名字可以是类名的全部或者部分,比如“-RuntimeException”、“-tion”等等。
&property name="*Service"&
PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,TIMEOUT_20,+AbcException,+DefException,-HijException
&/property&
基于TransactionProxyFactoryBean 的声明式事务管理
前面的声明式事务虽然好,但是配置文件太多。我们必须针对每一个目标对象配置一个ProxyFactoryBean;另外,虽然可以通过父子 Bean 的方式来复用 TransactionInterceptor 的配置,但是实际的复用几率也不高;这样,加上目标对象本身,每一个业务类可能需要对应三个 &bean/& 配置,随着业务类的增多,配置文件将会变得越来越庞大,管理配置文件又成了问题。
为了缓解这个问题,Spring 为我们提供了 TransactionProxyFactoryBean,用于将TransactionInterceptor 和 ProxyFactoryBean 的配置合二为一。
&bean id="testServiceTarget"
class="org.test.core.Test"&
&property name="testDao" ref="testDao"/&
&bean id="testService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&
&property name="target" ref=" testServiceTarget "/&
&property name="transactionManager" ref="transactionManager"/&
&property name="transactionAttributes"&
&prop key="transfer"&PROPAGATION_REQUIRED&/prop&
&/property&
基于 &tx& 命名空间的声明式事务管理
前面两种声明式事务配置方式奠定了 Spring 声明式事务管理的基石。在此基础上,Spring 2.x 引入了 &tx& 命名空间,结合使用 &aop& 命名空间,带给开发人员配置声明式事务的全新体验,配置变得更加简单和灵活。另外,得益于 &aop& 命名空间的切点表达式支持,声明式事务也变得更加强大。
&bean id=" testService " class="com.ncs.test testService"&
&property name="testDao" ref="testDao"/&
&tx:advice id="testAdvice" transaction-manager="transactionManager"&
&tx:attributes&
&tx:method name="transfer" propagation="REQUIRED"/&
&/tx:attributes&
&/tx:advice&
&aop:config&
&aop:pointcut id="testPointcut" expression="execution(* *.transfer(..))"/&
&aop:advisor advice-ref="testAdvice" pointcut-ref="testPointcut"/&
&/aop:config&
&bean id="testService" class=" com.ncs.test testService "&
&property name="testDao" ref="testDao"/&
&tx:advice id="testAdvice" transaction-manager="transactionManager"&
&aop:config&
&aop:pointcut id="testPointcut" expression="execution(**.transfer(..))"/&
&aop:advisor advice-ref="testAdvice" pointcut-ref="testPointcut"/&
&/aop:config&
基于 @Transactional 的声明式事务管理
事务配置:
&bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&
&property name="dataSource" ref="dataSource" /&
启用注释:
&tx:annotation-driv
MyBatis事务管理
在 MyBatis 中有两种事务管理器类型(也就是 type=”[JDBC|MANAGED]”):
JDBC – 这个配置直接简单使用了JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如 Spring 或 JEE应用服务器的上下文)。默认情况下它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,将 closeConnection 属性设置为 false。例如:
&transactionManager type="MANAGED"&
&property name="closeConnection" value="false"/&
&/transactionManager&
这两种事务管理器都不需要任何属性。然而它们都是类型别名,要替换使用它们,你需要放置将你自己的类的完全限定名或类型别名,它们引用了你对 TransacFactory 接口的实现类。
public interface TransactionFactory {
void setProperties(Properties props);
Transaction newTransaction(Connection conn, boolean autoCommit);
任何在 XML 中配置的属性在实例化之后将会被传递给 setProperties()方法。你的实现类需要创建一个事务接口的实现,这个接口也很简单:
public interface Transaction {
Connection getConnection();
void commit() throws SQLE
void rollback() throws SQLE
void close() throws SQLE
使用这两个接口,你可以完全自定义 MyBatis 对事务的处理
Struts.xml要害的配置
Struts.xml关键的配置
包含配置默认的情况下,Struts2将自动加载放在WEB-INF/classes路径下的struts.xml文件。大部分应用里,随着应用的规模的增加,系统的Action数量大量增加,导致了struts.xml配置文件变得非常臃肿。
为了避免这种情况,可以将一个struts.xml文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。比如说:在struts.xml文件中使用以下配置方式&include file=”struts-mod1.xml” /&通过这种方式提供了一种模块化的方式来管理struts.xml文件。另外,在core的根目录下有一个struts-default.xml文件,注意,这个文件是自动加载的,不需要在我们的struts.xml文件中用include导入。Bean的配置一般这个元素不需要我们进行配置,只是需要重新定义struts2的核心组件的时候才需要使用到这个元素。Struts2框架是一个可以高度扩展的框架,框架的大部分核心组件,都是通过IOC容器来进行管理的。因此,允许开发者可以很方便的提供自己的组件实现类,并将组件类部署到struts2就OK了。不过这里举个例子:&bean type=”com.opensymphony.xwork2.ObjectFactory” name=”caFactory” class=”cc.dynasoft.MyObject” /&以上代码的作用是使用一个自己定义的ObjectFactory来替换了struts2内置的object。常量配置Struts有一个属性文件,struts.properties,实际上这个配置文件是指定struts2的属性。而常量配置就是指定struts2属性的一种方式。例子:&constant name=”struts.custom.i18n.resources” value=”message” /&以上用于指定国际化资源文件的前缀名是message,当然也可以在struts.properties中配置,如下:struts.custom.i18n.resources=message也可以在web.xml中进行配置:作为FilterDispatcher的init-param&param-name& struts.custom.i18n.resources&/param-name&&param-value&message&/param-value&以下是加载struts2常量的搜索顺序:1、
struts-default.xml中2、
struts-plugin.xml中3、
struts.xml中4、
struts.properties5、
web.xml中包配置Struts2使用package来管理Action和拦截器等。配置该包时,必须指定一个name属性,用于指定包名,可以指定一个可选的extends属性,是另一个包的名字,子包可以继承父包的拦截器,拦截器栈、action等配置。除此之外,struts还提供了一个抽象包,抽象包的含义是该包不能有action的定义。设置abstract=”true”说明这是一个抽象包。以下是一个例子:&package name=”mod1” extends=”struts-default” /& 其中struts-default是struts-default.xml提供的包。另一个比较重要的概念就是命名空间:考虑在一个web应用中需要同名的action,struits2以命名空间的方式来管理Action,同一个命名空间不能有同名的Action,不同的命名空间可以有同名的Action。如果不指定命名空间,则默认的命名空间是””,指定了命名空间之后,Action的URL应该是命名空间+Action名。比如说:&action name=”register” extends=”struts-default” namespace=”/user” &……&/action&则对应的Action应该是/user/register.action。如果namespace=”/”,说明这是一个根命名空间。如果指定了命名空间,但是在该命名空间中找不到该Action,struts2将会在默认的命名空间中继续查找,如果还找不到,则出现系统错误。比如说:请求/order/save.action,系统首先在命名空间/order中查找,如果找到,使用该action进行处理,否则,系统将到默认的命名空间中查找,如果两个命名空间都找不到,出现系统错误。拦截器配置
拦截器实际上思想就是AOP,我们可以使用拦截器跟踪日志、跟踪系统性能瓶颈等。
拦截器的配置无非就是声明拦截器、引用拦截器以及声明拦截器栈。可以认为拦截器栈是由多个拦截器组成的一个大的拦截器。
定义拦截器和拦截器栈都在&interceptors /&这个标记内
以下是一个例子:
&interceptors&
&interceptor name=”log” class=”cc.dynasoft.LogInterceptor” /&&interceptor name=”authority” class=”cc.dynasoft. Authority Interceptor” /&&interceptor name=”timer” class=”cc.dynasoft.TimerInterceptor” /&&interceptor-stack name=”default”&
&interceptor-ref name=” authority” /&&interceptor-ref name=” timer” /&&/interceptor&引用拦截器是在action中引用的:例子:&action name=”login” class=”cc.dynasoft.LoginAction”&
&interceptor-ref name=”log” /&&/action&配置Action实际上配置Action很简单,就使用action标签就OK了,action标签有两个重要属性:name和class,name是必须指定的,他既是action的名字,也是该action需要处理的URL的前半部分。如果class没有指定,默认是ActionSupport。而ActionSupport默认处理就是返回一个SUCCESS字符串。struts的全局配置文件中有一个属性:struts.enable.SlashesInActionNames如果设置为true,允许struts2框架中使用斜线。Action中直接访问servlet API有以下两种方法:1、
如果需要访问ServletContext,Action类需要实现接口ServletContextAware如果需要访问 HttpServletRequest,Action类需要实现接口ServletRequestAware如果需要访问 HttpServletResponse,Action类需要实现接口ServletResponseAware以下是例子代码,主要用于访问Reponse对象:public class LoginAction implements Action,ServletResponseAware{
private HttpServletR
//实现ServletResponseAware接口必须实现的方法
public String execute() throw Exception
Cookie c = new Cookie(“user”,getUsername());
c.setMaxAge(60*60);//最大生存时间
response.addCookie(c);
return SUCCESS;
struts2提供了一个ServletActionContext,这个类包含了访问request,response等静态方法。注意:即使我们在struts2的Action中获得了HttpServletResponse对象,也不要尝试直接在Action中生成对客户端的输出。比如:下面代码是没有实际意义的,即response.getWriter().println(“hello world!”);动态方法调用(类似struts1的DispatchAction)其实没有什么特别的地方,只是提交表单的时候需要使用如下的格式:action=”ActionName!MethodName.action”举个例子:action=”Login!regist.action”,这句话的实质就是交给Login Action的regist方法进行处理使用动态方法调用前必须设置struts2允许动态方法调用。开启系统的动态方法调用是通过设置struts.enable.DynamicMethodInvocation常量完成的,设置该常量的值为true,将开启动态方法调用;否则将关闭动态方法调用。为action元素指定method属性对action进行如下的配置:&action name=”Login” class=”cc.dynasoft.LoginAction” method=”login” /&
……&/action&
&action name=”Regist” class=”cc.dynasoft.LoginAction” /&
……&/action&查看以上两个action,默认的情况是Regist,而当Login的时候,将提交到LoginAction的login中。查看上面的两个action,class都是一样的,于是就出现了下面的使用通配符的概念。使用通配符
在配置action的时候,action的三个属性name、class和method都可以使用通配符。
以下举例说明:
例1:&action name=”*Action” class=”cc.dynasoft.LoginAction” method=”{1}”&
&/action&解释一下上面代码的含义:上面定义的不是一个普通的action,而是定义了一系列的action,只要URL是*Action.action的模式,都可以通过该Action进行处理。但该Action定义了一个表达式{1},该表达式的值就是name属性值中的第一个*的值。例如,如果用户请求的URL是loginAction.action,则调用该action的login方法;如果用户请求的URL是registAction.action,则调用该action的regist方法。例2:&action name=”*Action” class=”cc.dynasoft.{1}Action”&
&/action&比如说:如果URL为RegistAction.action,可以匹配上面的action,该action的处理类是cc.dynasoft.RegistAction例3:&action name=”*_*” class=”cc.dynasoft.{1}Action” method=”{2}”&当一个action为Book_save.action的时候将调用BookAction的save方法来处理用户请求。注意这个时候指定校验文件需要注意,比如说Book_save.action的校验文件应该是Book_save-validation.xml。例4:&action name=”*”&
&result&/{1}.jsp&/result&
&/action&这个应该不难理解,呵呵。以下需要注意的问题:如果有URL为abcAction.action的请求,如果struts.xml文件有名为abcAction的Action,则一定由该Action来处理用户请求;如果struts.xml文件没有名为abcAction的Action,则搜索name属性值匹配abcAction的Action,例如name为*Action或*,*Action并不会比*更优先匹配abcAction的请求,而是先找到哪个Action,就先由哪个Action来处理用户的请求。因此,我们应该将名为*的Action配置在最后,否则struts2将使用该Action来处理所有希望使用模式匹配的请求。处理结果struts2通过在struts.xml文件中使用&result&元素来配置结果,根据&result&元素所在位置的不同,struts2提供了两种结果。局部结果:将&result&作为&action&元素的子元素配置全局结果:将&result&作为&global-result&元素的子元素配置。以下列出比较标准的配置:&result name=”success” type=”dispatcher”&
&param name=”location” &/thank_you.jsp&/param&&param name=”parse” &true&/param&&/result&location:用于指定实际视图资源parse:该参数指定是否允许在实际视图名称中使用ONGL表达式,默认为true。如果设置为false则不允许使用,通常不需要修改。一般我们比较常用的写法:&result name=”success” type=”dispatcher”&/thank_you.jsp &/result&注意如果没有指定name属性:则默认是success;如果没有指定type,则默认就是dispatcher,即JSP。下面列出struts2内建支持的结果类型如下:chain:Action链式处理的结果类型,也就是将结果转发到这个action中。chart:整合JFreeChart的结果类型dispatcher:用于整合JSP的结果类型freemarker:用于整合freemarker结果类型。httpheader:用于控制特殊的HTTP行为的结果类型。jasper:用于JasperReports整合的结果类型jsf:用于整合JSF后的结果类型redirect:实际上dispatcher和redirect的区别就是在于转发和重定向的区别。redirect-action:用于直接redirect action。stream:用于向浏览器返回一个Inputstream(用于文件下载)tiles:用于整合Tiles后的结果类型。velocity:用于整合Velocity的结果类型。xslt:用于整合XML/XSLT的结果类型。plaintext:用于显示某个页面的源代码。plaintext、redirect以及redirect-action的配置分别如下:&result type=”plaintext”&
&param name=”location”&/welcome.jsp&/param&
&!—设置字符集编码--&
&param name=”charset”&gb2312&/param&&/result&&result type=”redirect”&
/welcome.jsp&/result&&result type=” redirect-action”&
&!—指定action的命名空间--&
&param name=”namespace”&/ss&/param&&!—指定action的名字--&
&param name=”actionName”&login &/param&&/result&在请求结果中使用ONGL表达式例子:&result type=”redirect”&edit.action?skillName=${currentSkill.name}&/result&对于上面的表达式语法,要求action中必须包含currentSkill属性,并且currentSkill属性必须包含name属性,否则${currentSkill.name}表达式值为null。属性驱动和模型驱动我不推荐使用模型驱动,属性驱动完全可以实现模型驱动的效果。异常处理
异常处理在struts2中采用可配置的方式来处理,主要是为了防止异常代码和action代码耦合。我们比较希望的异常处理模式是这样的,如果出现了异常一,则系统跳转到视图1;如果出现了异常二,则系统转到视图2。
我们查看execute的声明:public String execute() throws Exception可以发现将异常抛出由struts2框架进行处理,为了处理异常,我们必须使用exception拦截器,由于在struts-default.xml的defaultStack中已经存在这个拦截器的定义,于是我们可以不用做任何事情。
struts2的异常处理机制是通过在struts.xml文件中配置&exception-mapping /&元素完成的,配置该元素的时候,需要指定两个属性:exception:此属性指定该异常映射所设置的异常类型。result:出现这个异常的时候,转入result属性所指向的结果。根据&exception-mapping /&元素出现的位置的不同,异常映射又可分为两种:局部异常映射:将&exception-mapping /&作为action的子元素配置全局异常映射:将&exception-mapping /&元素作为&global-exception-mappings&元素的子元素配置。实际上配置类似result,可以有局部,也可以是全局,局部优先。举个例子:&global-exception-mappings&
&exception-mapping exception=”java.sql.SQLException” result=”sql”/&&exception-mapping exception=”java.lang.Exception” result=”root”/&&/ global-exception-mappings&注意以上的sql和root是全局result。输出异常信息:&s:property value=”exception” /&:输出异常对象本身,注意这个异常代表Exception的实例,因此可以调用message属性。&s:property value=”exceptionStack” /&:输出异常堆栈信息。
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:&&,&&2号QQ群:,在群里结识技术精英和交流技术^_^
本站联系邮箱:

我要回帖

更多关于 rest和soap区别 的文章

 

随机推荐