??xml version="1.0" encoding="utf-8" standalone="yes"?>在线国产精品视频,成人午夜国产,四虎影院观看视频在线观看http://www.aygfsteel.com/table/category/43261.htmlzh-cnSat, 13 Mar 2010 16:49:41 GMTSat, 13 Mar 2010 16:49:41 GMT60使用 Struts 2 开?RESTful 服务http://www.aygfsteel.com/table/articles/315146.htmlThu, 11 Mar 2010 03:06:00 GMThttp://www.aygfsteel.com/table/articles/315146.htmlhttp://www.aygfsteel.com/table/comments/315146.htmlhttp://www.aygfsteel.com/table/articles/315146.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/315146.htmlhttp://www.aygfsteel.com/table/services/trackbacks/315146.htmlU别Q?初

??/a>, 自由撰稿?br />

2009 q?8 ?28 ?/p>

?V2.1 开始,Struts 2 开始提?Convention 插gQ它允许Ҏ“U定”来搜?ActionQ以及管?Action ?Result 的映。另外,Struts 2.1 q提供了 REST 插gQ Struts 2 可以支持 Rails 风格?URLQ以对外提供 REST 风格的资源服务。本文作者通过代码CZ演示了这些特性?/blockquote>

REST ?/span>

REST 是英?Representational State Transfer 的羃写,q个术语?Roy Thomas Fielding 博士在他的论文《Architectural Styles and the Design of Network-based Software Architectures》中提出。从q篇论文的标题可以看出:REST 是一U基于网l的软g架构风格?/p>

提示Q国内很多网l资料将 REST 译?#8220;表述性状态{U?#8221;Q不q笔者对q个译不太认同。因个专业术语无法传?REST 的含义,读者可以先不要理会 REST 到底该如何翻译,量先去理解 REST 是什么?有什么用Q然后再来看q个术语的翻译。关?Roy Thomas Fielding 博士的原文参见如下地址Qhttp://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm?/p>

REST 架构是针对传l?Web 应用提出的一U改q,是一U新型的分布式Y件设计架构。对于异构系l如何进行整合的问题Q目前主做法都集中在?SOAP、WSDL ?WS-* 规范?Web Services。?REST 架构实际上也是解军_构系l整合问题的一U新思\?/p>

如果开发者在开发过E中能坚?REST 原则Q将可以得到一个用了优质 Web 架构的系l,从而ؓpȝ提供更好的可伸羃性,q低开发难度。关?REST 架构的主要原则如下:

  • |络上的所有事物都可被抽象源(ResourceQ?
  • 每个资源都有一个唯一的资源标识符QResource IdentifierQ?
  • 同一资源h多种表现形式?
  • 使用标准Ҏ操作资源?
  • 通过~存来提高性能?
  • 对资源的各种操作不会改变资源标识W?
  • 所有的操作都是无状态的QStatelessQ?

仅从上面几条原则来看 REST 架构Q其实依然比较难以理解,下面W者将从如下二个方面来介绍 REST?/p>



回页?/strong>


资源和标识符

现在?Web 应用上包含了大量信息Q但q些信息都被隐藏?HTML、CSS ?JavaScript 代码中,对于普通浏览者而言Q他们进入这个系l时无法知道该系l包含哪些页面;对于一个需要访问该pȝ资源的第三方pȝ而言Q同h法明白这个系l包含多功能和信息?/p>
URI ?URL

?URI 相关的概念还?URLQURL ?Uniform Resource LocatorQ也是l一资源定位W的意思。其?http://www.crazyit.org 是一个统一资源定位W,URL ?URI 的子集。简而言之:每个 URL 都是 URIQ但不是每个 URI 都是 URL?/p>

?REST 架构的角度来看,该系l里包含的所有功能和信息Q都可被UCؓ资源QResourceQ,REST 架构中的资源包含静态页面、JSP ?Servlet {,该应用暴露在|络上的所有功能和信息都可被称源?/p>

除此之外QREST 架构规范了应用资源的命名方式QREST 规定对应用资源用统一的命名方式:REST pȝ中的资源必须l一命名和规划,REST pȝ׃?URIQUniform Resource IdentifierQ即l一资源标识W)命名的资源组成。由?REST 对资源用了Z URI 的统一命名Q因此这些信息就自然地暴露出来了Q从而避?“信息地窖”的不良后果?/p>

对于当今最常见的网l应用来_资源标识W就?URIQ资源的使用者则Ҏ URI 来操作应用资源。当 URI 发生改变Ӟ表明客户机所使用的资源发生了改变?/p>

从资源的角度来看Q当客户机操作不同的资源Ӟ资源所在的 Web (?Web 当成虚拟的状态机来看Q的状态就会发生改变、迁U(TransferQ,q就?REST 术语?STQState TranferQ的由来了?/p>

客户Zؓ了操作不同状态的资源Q则需要发送一?Representational 的数据,q些数据包含必要的交互数据,以及描述q些数据的元数据。这是 REST 术语?REQRepresentationalQ的由来了。理解了q个层次之后Q至?REST 如何译、或是否真正l它一个中文术语,读者可自行军_?/p>



回页?/strong>


操作资源的方?/span>

对于 REST 架构的服务器端而言Q它提供的是资源Q但同一资源h多种表现形式Q可通过?HTTP Content-type 头中包含关于数据cd的元数据Q。如果客L序完全支?HTTP 应用协议Qƈ能正处?REST 架构的标准数据格式,那么它就可以与世界上L一?REST 风格的用户交互。这U情况不仅适用于从服务器端到客L的数据,反之亦然——倘若从客L传来的数据符?REST 架构的标准数据格式,那么服务器端也可以正处理数据,而不d心客L的类型?/p>

典型情况下,REST 风格的资源能?XHTML、XML ?JSON 三种形式存在Q其?XML 格式的数据是 WebServices 技术的数据交换格式Q?JSON 则是另一U轻量的数据交换格式;至于 XHTML 格式则主要由览器负责呈现。当服务器ؓ所有资源提供多U表现Ş式之后,q些资源不仅可以被标?Web 览器所使用Q还可以?JavaScript 通过 Ajax 技术调用,或者以 RPCQRemote Procedure CallQ风D用,从而变?REST 风格?WebServices?/p>

REST 架构除了规定服务器提供资源的方式之外Q还推荐客户端?HTTP 作ؓ Generic Connector InterfaceQ也是通用q接器接口)Q?HTTP 则把对一?URI 的操作限制在?4 个之内:GET、POST、PUT ?DELETE。通过使用通用q接器接口对资源q行操作的好处是保证pȝ提供的服务都是高度解耦的Q从而简化了pȝ开发,改善了系l的交互性和可重用性?/p>

REST 架构要求客户端的所有的操作在本质上是无状态的Q即从客户到服务器的每个 Request 都必d含理解该 Request 的所有必需信息。这U无状态性的规范提供了如下几点好处:

  • 无状态性得客L和服务器端不必保存对方的详细信息Q服务器只需要处理当?RequestQ而不必了解前?Request 的历双Ӏ?
  • 无状态性减了服务器从局部错误中恢复的Q务量Q可以非常方便地实现 Fail Over 技术,从而很Ҏ地将服务器组仉|在集群内?
  • 无状态性得服务器端不必在多个 Request 中保存状态,从而可以更Ҏ地释放资源?
  • 无状态性无需服务lg保存 Request 状态,因此可让服务器充分利?Pool 技术来提高E_性和性能?

当然Q无状态性会使得服务器不再保?Request 的状态数据,因此需要在一pd Request 中发送重复数据,从而提高了pȝ的通信成本。ؓ了改善无状态性带来的性能下降QREST 架构填加了缓存约束。缓存约束允讔R式或昑ּ地标C?Response 中的数据Q这样就赋予了客L~存 Response 数据的功能,q样可以ؓ以后?Request q~存的数据,部分或全部的消除一些交互,增加了网l的效率。但是用于客L~存了信息,也就同时增加了客L与服务器数据不一致的可能Q从而降低了可靠性?/p>



回页?/strong>


Struts 2 ?REST 支持

U定优于配置

Convention q个单词的翻译过来就?#8220;U定”的意思。有 Ruby On Rails 开发经验的读者知?Rails 有一条重要原则:U定优于配置。Rails 开发者只需要按U定开?ActiveRecord、ActiveController 卛_Q无需q行配置。很明显QStruts 2 ?Convention 插g借鉴?Rails 的创意,甚至q插件的名称都借鉴?#8220;U定优于配置”原则?/p>

?Struts 2.1 开始,Struts 2 改ؓ使用 Convention 插g来支持零配置。Convention 插gd地抛弃了配置信息Q不仅不需要?struts.xml 文gq行配置Q甚至不需要?Annotation q行配置。而是?Struts 2 ҎU定来自动配|?/p>

Convention q个单词的翻译过来就?#8220;U定”的意思。有 Ruby On Rails 开发经验的读者知?Rails 有一条重要原则:U定优于配置。Rails 开发者只需要按U定开?ActiveRecord、ActiveController 卛_Q无需q行配置。很明显QStruts 2 ?Convention 插g借鉴?Rails 的创意,甚至q插件的名称都借鉴?#8220;U定优于配置”原则?/p>

׃ Struts 2 ?Convention 插g的主要特Ҏ“U定优于配置”Q当我们已经习惯?Struts 2 的基本开发方法之后,如果希望改ؓ使用 Convention 插g也非常容易,我们只要攑ּ Stuts 2.1 应用原有的配|文Ӟ改ؓ?Convention 插g的约定来定义 Action 卛_?/p>

?Convention 插g为基QStruts 2.1 又新增了 REST 插gQ允?Struts 2 应用对外提供 REST 服务。REST 插g也无需使用 XML q行配置理。Struts 2.1 通过 REST 插g完全可以提供让h和机器客L共同使用的资源,q支?Ruby On Rails 风格?URL?/p>



回页?/strong>


RestActionMapper ?/span>

从本质上来看QStruts 2 依然是一?MVC 框架Q最初设?Struts 2 时ƈ没有?REST 架构q行设计Q因?Struts 2 本质上ƈ不是一?REST 框架。由?Struts 2 提供了良好的可扩展性,因此允许通过 REST 插g其扩展成支?REST 的框架。REST 插g的核心是 RestActionMapperQ它负责?Rails 风格?URL 转换Zl请求的 URL?/p>

?WinRAR 打开 struts2-rest-plugin-2.1.6 文gQ看到该文g里包含一?struts-plugin.xml 文gQ该文g中包含如下一行:

<!-- 定义支持 REST ?ActionMapper -->
            <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper"
            name="rest" class="org.apache.struts2.rest.RestActionMapper" />
            

通过查看 RestActionMapper ?API 说明Q我们发现它可接受如下几个参敎ͼ

  • struts.mapper.idParameterNameQ用于设|?ID h参数的参数名Q该属性值默认是 id?
  • struts.mapper.indexMethodNameQ设|不?id h参数?GET h调用 Action 的哪个方法。该属性值默认是 index?
  • struts.mapper.getMethodNameQ设|带 id h参数?GET h调用 Action 的哪个方法。该属性值默认是 show?
  • struts.mapper.postMethodNameQ设|不?id h参数?POST h调用 Action 的哪个方法。该属性值默认是 create?
  • struts.mapper.putMethodNameQ设|带 id h参数?PUT h调用 Action 的哪个方法。该属性值默认是 update?
  • struts.mapper.deleteMethodNameQ设|带 id h参数?DELETE h调用 Action 的哪个方法。该属性值默认是 destroy?
  • struts.mapper.editMethodNameQ设|带 id h参数、且指定操作 edit 资源?GET h调用 Action 的哪个方法。该属性值默认是 edit?
  • struts.mapper.newMethodNameQ设|不?id h参数、且指定操作 edit 资源?GET h调用 Action 的哪个方法。该属性值默认是 editNew?

?RestActionMapper 的方法列表中Q我们看?setIdParameterName、setIndexMethodName、setGetMethodName、setPostMethodName、setPutMethodName、setDeleteMethodName、setEditMethodName、setNewMethodName {方法,q些Ҏ对应Z面列出的Ҏ提供 setter 支持?/p>

通常情况下,我们没有必要改变 RestActionMapper 的参敎ͼ直接使用q些参数的默认值就可支?Rails 风格?REST。根据前面介l可以看出:支持 REST 风格?Action 臛_包含如下 7 个方法:

  • indexQ处理不?id h参数?GET h?
  • showQ处理带 id h参数?GET h?
  • createQ处理不?id h参数?POST h?
  • updateQ处理带 id h参数?PUT h?
  • destroyQ处理带 id h参数?DELETE h?
  • editQ处理带 id h参数Q且指定操作 edit 资源?GET h?
  • editNewQ处理不?id h参数Q且指定操作 edit 资源?GET h?

如果h需要向服务器发?id h参数Q直接将h参数的值附加在 URL 中即可。表 1 昄?RestActionMapper 对不?HTTP h的处理结果?/p>
?1. RestActionMapper ?HTTP h的处?/strong>
HTTP Ҏ URI 调用 Action 的方?/strong> h参数
GET /book index  
POST /book create  
PUT /book/2 update id=2
DELETE /book/2 destroy id=2
GET /book/2 show id=2
GET /book/2/edit edit id=2
GET /book/new editNew  

不幸地是Q标?HTML 语言目前Ҏ不支?PUT ?DELETE 两个操作Qؓ了I补这U不IREST 插g允许开发者提交请求时额外增加一?_method h参数Q该参数值可以ؓ PUT ?DELETEQ用于模?HTTP 协议?PUT ?DELETE 操作?/p>



回页?/strong>


?Struts 2 应用安装 REST 插g

安装 REST 插g非常单,只需按如下步骤进行即可:

  1. ?Struts 2 目?struts2-convention-plugin-2.1.6.jar、struts2-rest-plugin-2.1.6.jar 两个 JAR 包复制到 Web 应用?WEB-INF\lib 路径下?
  2. ׃ Struts 2 ?REST 插gq需要将提供 XML、JSON 格式的数据,因此q需要将 xstream-1.2.2.jar、json-lib-2.1.jar、ezmorph-1.0.3.jar 以及 Jakarta-Common 相关 JAR 包复制到 Web 应用?WEB-INF/lib 路径下?
  3. 通过 struts.xml、struts.properties ?web.xml 改变 struts.convention.default.parent.package 帔R的|让支?REST 风格?Action 所在的包默认?rest-defaultQ而不是扉K认的 convention-default 父包?

对于W三个步骤而言Q开发者完全可以不讄该常量,如果开发者不讄该常量,则意味着开发者必通过 Annotation 为每?Action c设|父包?/p>



回页?/strong>


实现支持 REST ?Action c?/span>

在实现支?REST ?Action 之前Q我们先为系l提供一?Model c:BookQ该 Book c非常简单,代码如下Q?/p>
public class Book
            {
            private Integer id;
            private String name;
            private double price;
            // 无参数的构造器
            public Book(){}
            //id 属性的 setter ?getter Ҏ
            public void setId(Integer id)
            {
            this.id = id;
            }
            public Integer getId()
            {
            return this.id;
            }
            // 省略 name ?price ?setter ?getter Ҏ
            ...
            }
            

除了提供上面?Book cM外,我们qؓ?Book cL供一个业务逻辑lgQBookService。ؓ了简单v见,BookService cM再依?DAO lg讉K数据库,而是直接操作内存中的 Book 数组——简单地_本系l中状态是瞬态的Q没有持久化保存Q应用运行过E中q些状态一直存在,但一旦重启该应用Q则pȝ状态丢失。下面是 BookService cȝ代码Q?/p>
public class BookService
            {
            private static Map<Integer , Book> books
            = new HashMap<Integer , Book>();
            // 保留下本图书?ID
            private static int nextId = 5;
            // 以内存中的数据模拟数据库的持久存?
            static {
            books.put(1 , new Book(1
            , "疯狂 Java 讲义" , 99));
            books.put(2 , new Book(2
            , "轻量U?Java EE 企业应用实战" , 89));
            books.put(3 , new Book(3
            , "疯狂 Ajax 讲义", 78));
            books.put(4 , new Book(4
            , "Struts 2 权威指南" , 79));
            }
            // Ҏ ID 获取
            public Book get(int id)
            {
            return books.get(id);
            }
            // 获取pȝ中全部图?
            public List<Book> getAll()
            {
            return new ArrayList<Book>(books.values());
            }
            // 更新已有的图书或保存新图?
            public void saveOrUpdate(Book book)
            {
            // 如果试图保存的图书的 ID ?nullQ表明是保存新的图书
            if (book.getId() == null)
            {
            // 为新的图书分?ID?
            book.setId(nextId++);
            }
            // 保?book
            books.put(book.getId() , book);
            }
            // 删除图书
            public void remove(int id)
            {
            books.remove(id);
            }
            }
            

从上面粗体字代码可以看出QBookService 提供?4 个方法,用于实现?Book 对象?CRUD 操作?/p>

下面开始定义支?REST ?Action cMQ这?Action cM前面介绍 Struts 2 的普?Action 存在一些差异——因 Action 不再?execute() Ҏ来处理用戯求,而是使用前面介绍?7 个标准方法来处理用户h。除此之外,?Action L需要处?id h参数Q因此必L?id h参数QƈZ提供对应?setter ?getter Ҏ?/p>

因ؓ本系l已l提供了 Book Model c,q且Z更好的模?Rails ?ActiveControllerQControllerQ直接访?ActiveRecordQModelQ的方式Q本pȝ采用?ModelDriven 的开发方式,下面是本pȝ中支?REST ?Action cȝ代码?/p>
 // 定义q回 success 旉定向?book Action
            @Results(@Result(name="success"
            , type="redirectAction"
            , params = {"actionName" , "book"}))
            public class BookController extends ActionSupport
            implements ModelDriven<Object>
            {
            // 装 id h参数的属?
            private int id;
            private Book model = new Book();
            private List<Book> list;
            // 定义业务逻辑lg
            private BookService bookService = new BookService();
            // 获取 id h参数的方?
            public void setId(int id)
            {
            this.id = id;
            // 取得Ҏ旉带初始化 model 对象
            if (id > 0)
            {
            this.model = bookService.get(id);
            }
            }
            public int getId()
            {
            return this.id;
            }
            // 处理不带 id 参数?GET h
            // q入首页
            public HttpHeaders index()
            {
            list = bookService.getAll();
            return new DefaultHttpHeaders("index")
            .disableCaching();
            }
            // 处理不带 id 参数?GET h
            // q入d新图书?
            public String editNew()
            {
            // 创徏一个新图书
            model = new Book();
            return "editNew";
            }
            // 处理不带 id 参数?POST h
            // 保存新图?
            public HttpHeaders create()
            {
            // 保存图书
            bookService.saveOrUpdate(model);
            addActionMessage("d图书成功");
            return new DefaultHttpHeaders("success")
            .setLocationId(model.getId());
            }
            // 处理?id 参数?GET h
            // 昄指定图书
            public HttpHeaders show()
            {
            return new DefaultHttpHeaders("show");
            }
            // 处理?id 参数、且指定操作 edit 资源?GET h
            // q入~辑面 (book-edit.jsp)
            public String edit()
            {
            return "edit";
            }
            // 处理?id 参数?PUT h
            // 修改图书
            public String update()
            {
            bookService.saveOrUpdate(model);
            addActionMessage("图书~辑成功Q?);
            return "success";
            }
            // 处理?id 参数Q且指定操作 deleteConfirm 资源的方?
            // q入删除面 (book-deleteConfirm.jsp)
            public String deleteConfirm()
            {
            return "deleteConfirm";
            }
            // 处理?id 参数?DELETE h
            // 删除图书
            public String destroy()
            {
            bookService.remove(id);
            addActionMessage("成功删除 ID ? + id + "的图书!");
            return "success";
            }
            // 实现 ModelDriven 接口必须实现?getModel Ҏ
            public Object getModel()
            {
            return (list != null ? list : model);
            }
            }
            

上面 Action 代码中粗体字代码定义?7 个方法,q?7 个方法正是前面提到的标准Ҏ。除此之外,?Action 里还包含一个额外的 deleteConfirm() ҎQ这个方法用于处理带 id 参数、且指定操作 deleteConfirm 资源?GET h。也是_当用戯?/book/1/deleteConfirm Ӟ该请求将pҎ负责处理。实际上QRestActionMapper 不仅可以对 /book/1/edit 的请求映到 Book 控制器的 edit() ҎQ?1 作?id h参数。实际上Q它可以Q?/book/1/xxx 的请求映到 Book 控制器的 xxx() ҎQ?1 是请求参数。上?Action cM用了 @Results q行修饰Q这表明?Action 的Q何方法返?#8220;success”逻辑视图Ӟpȝ重定向?book.action?/p>

可能有读者会?index()、create()、show() 三个Ҏ的返回值感到疑惑:它们不再直接q回普通字W串作ؓ逻辑视图名字Q而是q回一个以字符串ؓ参数?DefaultHttpHeaders 对象Q其实读者不必对 DefaultHttpHeaders 感到疑惑Q其?DefaultHttpHeaders 只是普通字W串的加强Ş式,用于 REST 对处理结果进行更多额外的控制。当 Action cȝ处理Ҏq回字符串作为逻辑视图ӞStruts 2 只能其当成一个简单的视图名,仅能Ҏ该视囑֐映射到实际视图资源,仅此而已。如果?DefaultHttpHeaders 作ؓ逻辑视图QDefaultHttpHeaders 除了可以包含普通字W串作ؓ逻辑视图名之外,q可以额外增加更多的控制数据Q从而可以增强对 Response 的控制。关?HttpHeaders ?DefaultHttpHeaders 的介l请参?REST 插g?API?/p>

q有一炚w要指出,上面?BookController 控制器实现类的类名ƈ不以 Action l尾Q而是?Controller l尾Q因此我们可以在 struts.xml 文g中配|如下常量:

 <!--  指定控制器类的后~?Controller -->
            <constant name="struts.convention.action.suffix"
            value="Controller"/>
            本应用里?struts.xml 文g如下Q?
            E序清单Qcodes\12\12.6\BookShow\WEB-INF\src\struts.xml
            <?xml version="1.0" encoding="GBK" ?>
            <!-- 指定 Struts 2 配置文g?DTD 信息 -->
            <!DOCTYPE struts PUBLIC
            "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
            "http://struts.apache.org/dtds/struts-2.1.dtd">
            <!-- 指定 Struts 2 配置文g的根元素 -->
            <struts>
            <constant name="struts.i18n.encoding" value="GBK"/>
            <!--  指定控制器类的后~?Controller -->
            <constant name="struts.convention.action.suffix"
            value="Controller"/>
            <constant name="struts.convention.action.mapAllMatches"
            value="true"/>
            <!-- 指定 Action 所在包l承的父?-->
            <constant name="struts.convention.default.parent.package"
            value="rest-default"/>
            </struts>
            





回页?/strong>


实现视图?/span>

定义了上?Action 之后Q接下来应该?Action 提供视图面了,Ҏ Convention 插g的约定,所有视N面都应该攑֜ WEB-INF\content 目录下,例如当用户向 /book.action 发送请求时Q该h由 BookController ?index() Ҏq行处理Q该Ҏ处理l束后返?#8220;index”字符Ԍ也就是将会?WEIN-INF\content\book-index.jsp 面作ؓ视图资源。下面是 book-index.jsp 面的代码:

 <%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
            <%@taglib prefix="s" uri="/struts-tags" %>
            <!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>
            <title> 图书展示pȝ </title>
            <link href="<%=request.getContextPath() %>/css/demo.css"
            rel="stylesheet" type="text/css" />
            </head>
            <body>
            <s:actionmessage />
            <table>
            <tr>
            <th> 图书 ID</th>
            <th> 书名 </th>
            <th> h </th>
            <th> 操作 </th>
            </tr>
            <s:iterator value="model">
            <tr>
            <td><s:property value="id"/></td>
            <td>${name}</td>
            <td>${price}</td>
            <td><a href="book/${id}"> 查看 </a> |
            <a href="book/${id}/edit"> ~辑 </a> |
            <a href="book/${id}/deleteConfirm"> 删除 </a></td>
            </tr>
            </s:iterator>
            </table>
            <a href="<%=request.getContextPath() %>/book/new"> 创徏新图?</a>
            </body>
            </html>
            

上面 JSP 面非常单,它负责P代输?Action 里包含的集合数据Q向该应?book.action 发送请求将看到如图 1 所C页面?/p>
?1 使用 Struts 2 开发的 REST 服务
?1 使用 Struts 2 开发的 REST 服务

Struts 2 ?REST 插g支持一U资源具有多表CŞ式,当浏览者向 book.xml 发送请求将可以看到如图 2 所C页面?/p>
?2 REST 服务?XML 形式
?2 REST 服务?XML 形式

从图 2 可以看出Q该面正是 Action 所包含的全部数据,当?XML 昄?REST 插g会负责把这些数据{换成 XML 文档?/p>

除此之外QREST 插gq提供了 JSON 格式的显C方式,当开发者向 book.json 发送请求将看到如图 3 所C页面?/p>
?3 REST 服务?JSON 形式
?3 REST 服务?JSON 形式

Struts 2 ?REST 插g默认支持 XHTML、XML ?JSON 三种形式的数据?/p>

当浏览者单击页面右边的“~辑”链接Q将会向 book/idVal/edit 发送请求,q是一个包?ID h参数、且指定操作 edit 资源的请求,因此由 BookController ?edit() Ҏ负责处理Q处理结束后q入 book-edit.jsp 面。浏览器里将看到如图 4 所C页面?/p>
?4 ~辑指定图书
?4 ~辑指定图书

该页面单?#8220;修改”按钮旉要修改图书信息,也就是需要?PUT 操作Q但׃ HTML 不支?PUT 操作Q因此需要ؓ该表单页增加一个额外的h参数Q_methodQ该h参数的gؓ put。该表单늚代码如下Q?/p>
 <%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
            <%@taglib prefix="s" uri="/struts-tags" %>
            <!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>
            <title> ~辑 ID ?<s:property value="id"/> 的图?</title>
            <link href="<%=request.getContextPath() %>/css/demo.css"
            rel="stylesheet" type="text/css" />
            </head>
            <body>
            <s:form method="post"
            action="%{#request.contextPath}/book/%{id}">
            <!-- 增加 _method h参数Q参数gؓ put 用于模拟 PUT 操作 -->
            <s:hidden name="_method" value="put" />
            <table>
            <s:textfield name="id" label="图书 ID" disabled="true"/>
            <s:textfield name="name" label="书名"/>
            <s:textfield name="price" label="h" />
            <tr>
            <td colspan="2">
            <s:submit value="修改"/>
            </td>
            </table>
            </s:form>
            <a href="<%=request.getContextPath() %>/book"> q回首页 </a>
            </body>
            </html>
            

该表单将提交l?BookController ?update() Ҏ处理Qupdate() Ҏ负责修改系l里指定 ID 对应的图书信息?/p>

与之cM的是Q当h需要执?DELETE 操作Ӟ一样需要增加名?_method 的请求参敎ͼq将该请求参数D|ؓ delete?/p>

参考资?



关于作?/span>

 

李刚Q从?Java EE 应用开发近 10 q。曾?LITEON 公司?J2EE 技术主,负责该公司的企业信息化^台的架构设计。曾d州电信、广东龙泉科技{公司的技术培训教师。疯?Java 联盟Qhttp://www.crazyit.orgQ站ѝ疯?Java 实训营创始hQ疯?Java 体系图书作者,曾Q东方标准q州中心软g教学ȝQ曾gQq东技术师范学院计机U学pȝD副教授。国内知名IT技术作Ӟ已出版《疯?Java 讲义》、《轻量 Java EE 企业应用实战》、《疯?Ajax 讲义》、《Struts 2.1 权威指南》、《Ruby On Rails 敏捷开发最佛_c等著作?/p>



2010-03-11 11:06 发表评论
]]>Struts2讉K隐藏的request和session http://www.aygfsteel.com/table/articles/307194.htmlThu, 24 Dec 2009 10:09:00 GMThttp://www.aygfsteel.com/table/articles/307194.htmlhttp://www.aygfsteel.com/table/comments/307194.htmlhttp://www.aygfsteel.com/table/articles/307194.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/307194.htmlhttp://www.aygfsteel.com/table/services/trackbacks/307194.html
在Struts1.*中,要想讉Krequest、response以及session{Servlet对象是很方便的,因ؓ它们一直是作ؓ形参在各个方法之间进行传递的Q而在Struts2中我们就很难看到它们的芳t了Q因为我们获得表单中的值都是通过预先讄好了的getҎ来得到的Q那么如果有些参数我们必通过request.getParametre或者session.getAttribute来得刎ͼ那么应该怎么做呢Q按照Max的教E上的说法,可以分ؓ两种QIoC方式和非IoC方式Q如何理解这两种方式的区别呢QIoC是Spring里面的特征之一Q字面意思是反{控制Q说白了是依赖注入Q比方说cA依赖cBQ那么就d的给A注入一个类B的对象,下面看一下这两种Ҏ的具体实现?/div>
1Q非Ioc方式
q种方式主要是利用了com.opensymphony.xwork2.ActionContextcM?span style="background: red">org.apache.struts2.ServletActionContextc,具体的方法如下所C?/div>
获得request对象Q?/div>
AQ?/span>HttpServletRequest request = ServletActionContext.getRequest ();
BQActionContext ct= ActionContext.getContext()
   HttpServletRequest request=
(HttpServletRequest)ct.get(ServletActionContext.HTTP_REQUEST);
获得session对象Q?/div>
在Struts2中底层的session都被装成了MapcdQ我们称之ؓSessionMapQ而^常我们所说的session则是指HttpSession对象Q具体的获得Ҏ如下所C?/div>
AQMap session=ActionContext.getSession();
BQMap session=(Map)ActionContext.getContext().get(ActionContext.SESSION);
得到q个SessionMap之后我们可以对sessionq行d了,如果我们惛_到原始的HttpSession可以首先得到HttpServletRequest对象Q然后通过request.getSession()来取得原始的HttpSession对象。一般情况下SessionMap已经可以完成所有的工作Q我们不必再ȝ底层的session了?/div>
2QIoC方式
q种方式相对来说变化比较少了,具体程如下所C?/div>
获得request对象Q?/div>
W一步:让action实现ServletRequestAware接口
W二步:?/span>action中声明一?/span>HttpServletRequestcd的实例变?/span>
W三步:?/span>action中实?/span>ServletRequestAware接口?/span>setServletRequestҎQ实现方式很单,如下所C?/span>
         private HttpServletRequest request;
publicvoid setServletRequest(HttpServletRequest request) {
            this.request = request;
    }
获得Session对象(注意Q此时的session?/span>SessionMapcd)Q?/span>
W一步:让action实现SessionAware接口
W二步:?/span>action中声明一?/span>HttpServletRequestcd的实例变?/span>
W三步:?/span>action中实?/span>SessionAware接口?/span>setSessionҎQ实现方式很单,如下所C?/span>
         private Map session;
publicvoid setSession(Map session) {
            this. session = session;
    }
 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1721226


2009-12-24 18:09 发表评论
]]>对struts2的底层源码的解读http://www.aygfsteel.com/table/articles/307164.htmlThu, 24 Dec 2009 07:42:00 GMThttp://www.aygfsteel.com/table/articles/307164.htmlhttp://www.aygfsteel.com/table/comments/307164.htmlhttp://www.aygfsteel.com/table/articles/307164.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/307164.htmlhttp://www.aygfsteel.com/table/services/trackbacks/307164.html

此问题网上很多h在讨论,至今q是没有很好的解x案,在我Ҏ码进行解dQ现在已l有个很好的解决ҎQ业务是用户注册Q含复选框Q,用户注册验证出错后,错误信息的要昄在相应控件的后面Q同时要让请求选中的复选框处于选中状态。希望您往下看Q说不定会小有收获哦Q!

步骤Q?/p>

1)开发工兯|?/span>

   a)工程~码utf-8

   b)文g代码样式Q?/span>java?/span>xmlQ?/span>

   c)文g~码格式utf-8Q?/span>jsp?/span>html?/span>jsQ?/span>

2)创徏目录l构

3) 搭徏struts2的环境,

a)    导入jar?/span>

参?font face="Times New Roman">struts2.1.8下的app下的struts2-blank-2.1.8目Q拷入基本的jar?/font>

==========================================================

struts2-core-2.x.x.jar :Struts 2框架的核心类?/span>

       xwork-2.x.x.jar :XWorkcdQ?/span>Struts 2在其上构?/span>

ognl-2.6.x.jar :对象囑֯航语aQ?/span>Object Graph Navigation LanguageQ,struts2框架通过其读写对象的属?/span>

       freemarker-2.3.x.jar :Struts 2?/span>UI标签的模板?/span>FreeMarker~写

       commons-fileupload-1.2.1.jar?/span>commons-io-1.3.2.jarq两?/span>jar是用于文件上?/span>

===========================================================

       b)修改web.xml配置文g、添?/span>struts.xml文g

       c)启动目试index.jsp界面输出

3Q注册页?/font>

面的源码如下:_心了点没有实现U国际化


怿您已l推断出我的目大致部vQ我q里q要描qCQ?/p>

配置文gQ?span style="color: blue">struts.xml?/span>strus-user.xmlZ模块化配|,UserAction-registerUser-validation.xml在执?/span>UserAction?/span>registerUserҎ时进行校?/span>

属性文Ӟ有全局的国际化文g?/span>user模块的国际化文g

c:UserAction?/span>UserBean?/span>Hobby

工程目录


Struts.xml文g


UserActionc?/p>




工程目录
Struts.xml文g
UserActionc?/div>
W一个问题出CQ如下图Q?/span>
说明Q?/div>
很多人都是对此很烦恼Qstruts2使用的默认主题是xhtmlQ查看html面的源码发玎ͼ它给我们生成了表格布局Q所以界面比较整齐,但在提供便利的同Ӟ也带来些烦恼Q就是错误提C出现的位置?/div>
解决ҎQ?/div>
1Q把主题设ؓtheme=simpleQ自己去控制布局Q?/div>
2Qstruts使用freemarker模版技术,为我们标{成了html代码Q所以我们通过修改模版讄错误信息提示的位|?/div>
解读源码了:
 
a: <s:textfield/>q是strus2l我们提供标{,所以我们查看官Ҏ档得知struts-tags.tld在struts-core.jar文g里,每个标签都是一个javac,只是该类extends TagSupportQ我们查看TextFieldTagc,其实有经验的人都能猜的八九不dQ您肯定是的?/span>
查看TextFieldTagc?/div>
发现它没有doStartTag()ҎQ所以猜肯定在父c里定义了,查看AbstractUITagQ这个类里也没有doStartTag()ҎQ这个类是所有UI标签的父c,里面定义了标{属?/div>
l箋查看父类Q?/div>
该类有doStartTag()ҎQ?/div>
我们查看得知Qcontainer容器注入了componentlgQ组件会话出html文本Q所有我们需要查看着个组件的具体实现c,在TextFieldTag里面发现
查看Component具体cTextFieldQ?/div>
查看注释得知该类构徏html文本Q但Z么TextField没有l承ComponentcdQ一猜就是UIBeanl承了,查看
果然是承了ComponentQ学框架思想最重要的是看别人的注释Q因为注释是别h思想的表达,q个c负责通过模版构徏html文本Q所以我们最重要的是扑ֈ模版的位|,q个我也是大概看懂,毕竟q没辑ֈ水^Q相信您已经辑ֈQ大致意思是找这些属性|从而定位到ftl模版文g
struts.ui.theme=xhtml
 
struts.ui.templateDir=template
struts.ui.templateSuffix=ftl

?<s:checkbox/> ?template/xhtml/checkbox.ftl模版文gQ类?lt;s:textfield/>,在strus2-core.jar下找textfield.ftlQ发现是text.ftl,打开我们查看Q?/p>

 
包含三个模版Qcontrolheader.ftl又包?span style="font-size: 10pt">controlheader-core.ftl文gQ该文g才是核心Q?/span>
现在我们要修改controlheader-core.ftl、controlfooter.ftl文gQ把错误信息攑ֈcontrolfooter.ftl里面Q我们可以有两种方式修改Q?/div>
1Q?nbsp;拯Z个文Ӟ修改后再压羃q去Q严重不Q)
2Q?nbsp;既然有这L需求,strus2团队肯定考虑CQ这让我实在太佩服他们了Q每个细节考虑的都是那么周刎ͼ看官Ҏ?/div>
Strus2团队提出了模版的加蝲、选择模版目录、选择主题、扩展主题。我们想让框架加载我们的模版文gQ所以我们点击Template Loading链接查看Q?/div>
 
意思大概是Q首先加载应用程序\径下模版文gQ然后去加蝲classpath下的模版文gQ如果需要覆盖某模版Q拷贝修改后攄应用E序下,那样首先加载。本语不是太好,如有出处Q请见谅Q?/div>
现在我们需要把两个ftl文g攄application下,有什么格式要求吗Q?/div>
本章里strus2团队q给我们提供很多Q徏议我们不要随便更换模版引擎,如要修改ftl文g最好把源文件拷贝出来加以修改,不要自己手工重写Q以防止出错?/div>
怿也不用解释了Q格式ؓQ?tt>/template/$theme/$template.ftl.
所以我们只要在Web-Root下创?template/xhtml文g夹,拯controlheader-core.ftl、controlfooter.ftl文g再加以修攏V?/div>
本h对于freemarker语言不是很了解,但掌握了思想Q这U增增删删的操作q是可以应付的,l过几轮修改QM搞定了?/div>
----------------------------------------------------------------------------------------------------
W二个问题:
先对strus2的默认拦截器原理说下
官方文档q两行最能表达我的意思,excludeMethods参数是设|该拦截器忽略哪些方法,下面反之?span style="color: blue">q简单原理相信您非常了解了,来看下我们这里出现的情况吧!
q是最普遍的业务逻辑Q?/div>
q是strus-user.xml配置文g里的registerUser*.action配置Q?/div>
情况一Q?/span>
我们按此配置q行Q我们不填用户名注册Q结果出现如下情况:
说明Q?/div>
Z么出现无法显C网呢Q我们注册首先被validate拦截器拦截后QUserAction-registerUser-validation.xml验证用户名不能ؓI,l过workflow拦截器,发现验证出错了,所以蟩转input试图Q我们input没有配置typeQ默认是dispatcherQ我们这栯{发的话,则不再被strus2的拦截器拦截Q相当于览器访问registerUserUI静态资源,因ؓ压根没有Q?span style="color: blue">所?/span>tomcat容器无法讉KQ则出现无法昄|页?/div>
情况二:
好的我们现在修改inputl果的type属性ؓredirectQ?/div>
我们选中两个爱好Q提交返回界面如下:
开始选中的被取消了,q且没有“用户名不能ؓI?#8221;的错误信息?/span>
说明Q?/div>
Z么会错误信息没了呢?同样l过validate、workflow拦截器后Q蟩转input试图Q一但我们重定向Q则strus2框架会ؓ我们创徏一个新?/span>UserAction对象Q则fieldErrors?span style="font-size: 10pt; color: black">UserBean?/span>hobby数组都ؓnull了,所以错误信息、开始选中的都昄不出来了?/span>
情况三:
怿您已l有解决Ҏ?/span>Q把input试图?/span>type设ؓchaincdQ?/span>chaincd的作用是Q让该请求重新被拦截器拦截,好我们来修改Q?/span>
我们同样不填用户名,选中两个爱好Q点L册后Q发现如下结果:
意思大概是Q?span style="color: blue">发现一个无限递归调用?/span>
说明Q?/div>
怎么会出现这h限递归呢?哪里在递归调用Q我们来分析一下流E,当经qvalidate、workflow拦截器后Q蟩转input试图Q此时type=chainQ所以我们看下设|ؓchaincd后,struts2到底都干了些什么?在官Ҏ?strong>Action Chaining一章里讲解刎ͼ
大概意思是Q?/span>如果你要拯当前的action属性到当前chain上的actionQ你需要应用Chaining拦截器,该拦截器会拷贝请求上?/span>parameters?/span>value stack传递到目标action?/span>Q原始的action会保存valueStackQ允许目标action讉K前面所有action的valueStack作用域上的属性,同样对于chain的resultl果试图如jsp、velocity界面同样可以讉Kq些属性?/div>
=====================================================
所以当我们以chain方式讉KregisterUserUI.actionQ原先action的valueStack上的fieldErrors同样被拷贝到当前UserAction对象fieldErrors字段里,在经qworkflow拦截器后Q又发现有错误,同样调用input试图Q进而又去调用registerUserUI.actionQ经qworkflow拦截器后Q又发现有错误,又去调用input试图Q进而递归调用?/div>
现在该怎样解决呢?我们要做的就是,让他调用registerUserUI.action时被workflow拦截器拦截后Q不再验证是否有错误Q前面说到过excludeMethods参数Q我们查看workflow拦截?/div>
发现他忽略inputҎQ所以我们加上这L配置Q?/div>
修改UserAction的registerUserUIҎ为inputQok了!我们q行同样不填用户名、选中两个爱好提交Q运行结果ؓQ?/div>
注意Q?/div>
虽然chain方式对于完成q种业务很方便,官方提示我们谨慎使用Q过度用会造成E序的代码؜乱,到时q是Ҏ业务来决定?/div>


2009-12-24 15:42 发表评论
]]> վ֩ģ壺 ƽ| | | | ɯ| | | տ| Ӷ| ɽ| Ҵ| | ˫| | | | ʷ| ̽| ֥| ²| ع| | г| | ²| ԭƽ| ϴ| ˳| | | Դ| | ֣| Ͷ| | | ʹ| | Ӵ| | |