Terry.Li-彬

虚其心,可解天下之问;专其心,可治天下之学;静其心,可悟天下之理;恒其心,可成天下之业。

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  143 随笔 :: 344 文章 :: 130 评论 :: 0 Trackbacks
Spring3.0之前大家集成GWT基本上都是用 GWT- Widget/Server Library ,现在最新的Spring发布包提供了REST支持,使用这种方式更自然。其实这是一个老外的博客上提出来 的:http://bigohno.blogspot.com/2009/04/gwt-spring-mvc-and-rest-on-google- app.html

只是这位同志哥只提出了一个概念和其中使用到的一些技术,但是没有细节和代码,我几乎花了近两周的时间才把一个完整的业务流程顺利跑通。当然这也是由于我是第一次使用Gwt1.6,Spring MVC,REST,JSONlib,Restlet,Maven。

也 许你们注意到了,除了后台的openCRX/MDX是我研究多年的技术,其他技术除了spring,几乎都是完全没有接触过。然而,也因为这样,当我完成 了整个框架基本功能的调试运行之后,得出的结论是:其他技术可以不用,Maven2一定要用,Gwt可以不用,SpringMVC/REST一定要用。

当然了,很多只使用过Ant的同志,对于Maven可能觉得可有可无,够用就好。但是,Maven绝不仅仅是构建,而是帮你管理了整个项目的外部资源、内部构建和单元测试的自动化过程。可以这样说,一旦用过Maven,你就再也不愿意去做没有Maven的项目。

SpringMVC是一个较早出现的技术,以前大家习惯使用Struts1或者2作为MVC的实现框架,Spring技术与之相比并没有太大的优势,所以很多人没有用过SpringMVC包括很多老革命在内。在最新的Spring3.0M2中已经加入了REST,这使得我第一次体验到了AnnotationViewResolver的强大,当然最关键的起因在于REST本身就是一个非常吸引人的技术框架。而Rest的出现,个人认为是来自于对Ajax框架最好的支持。

前面是宣传一下新技术,下面先给大伙儿上张系统架构图:


对 于使用其他后台程序的项目,可以简单的用自己的Service替换openCRX API。说到这里,如果使用Maven进行以来管理和打包,那么会有一个很直接的好处,就是GWT的几个包,不需要手工编写脚本来区分开发时使用和部署时 使用,而是简单的配置一下scope属性就可以了。另外需要更新spring最新版本的jar包时,也不需要手工去下载更新,只是简单的修改一下版本号就 可以了。
特别要说的是:当使用SpringAOP特性的时候,需要一个cglib2的包,当我自己从网上搜索时下载的包死活无法正常运行,而我简 单的加入了SpringAOP自己的pom中所使用的依赖之后,就很顺利的启动了。这让我不禁畅想,如果所有的项目都使用maven发布自己的库,那是多 美妙的事情呀。
很遗憾,opencrx/mdx就没有一个自己的maven repository——还好我在网上偶然发现一篇博客介绍一款maven repository代理软件,这下可好了,对于一个项目组的成员或者整个公司来说,都可以搭建这样一个本地库,就可以“一次导入,到处更新”了,哈 哈。。。
到这里,相信大家对于这个“最新技术”所组成的“最新框架”应该有一个大致的印象了,下次介绍如何建立一个基于maven 的GWT1.6+Spring3.0的Eclipse工程。


这次就是关于如何 使用Maven2快速搭建一个支持GWT1.6和Spring3.0的Eclipse工程,这可以帮助我们用最快的速度开始真正的开发。

1.这里,Maven for Google Appengine Java,是一篇介绍如何基于Maven搭建基于google appengine的工程,我们可以利用它先建好一个gwt的工程,照着图文教程一步步做,之后就会得到一个标准的maven工程。

2. 执行mvn eclipse:eclipse,这样可以生成eclipse所需要的工程文件和classpath.这里要注意:如果你不需要支持google appengine那么就需要修改pom.xml文件中,把有关appengine的三个dependency注释掉,并且工程目录下的 build.xml也是可以删除的

3.仍然是修改pom.xml文件,加入对于spring3.0的依赖。这里要注意两点:一、前面提到 的那篇博客里面所说的加入一个dependency来加入spring3.0的支持,似乎已经过时了,结果是一个包都加不进来。我在网上搜索过很多有关 spring3.0 maven的文章,最后只有一个spring官方论坛中的帖子是有效的,但是连接已经找不到了,我就直接贴出这一段dependency了:
<dependency>
            <groupId>asm</groupId>
            <artifactId>asm-commons</artifactId>
            <version>2.2.3</version>
        </dependency>
        <dependency>
            <groupId>asm</groupId>
            <artifactId>asm</artifactId>
            <version>2.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.transaction</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.web.servlet</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.webflow</groupId>
            <artifactId>org.springframework.js</artifactId>
            <version>${webflow.version}</version>
        </dependency>
<dependency>
            <groupId>net.sourceforge.cglib</groupId>
            <artifactId>com.springsource.net.sf.cglib</artifactId>
            <version>2.1.3</version>
        </dependency>
注 意:asm和cglib都是spring的依赖包,但有于是可选,所以不能在默认情况下加进工程库中。本来也可以修改相应的spring dependency条目,但是我在公司机器上没有maven pom编辑器,就偷一下懒,直接引用了。大家记得,有条件一定要装一个maven eclipse的插件,特别是pom编辑器的插件,能够极大提高生产力。

4.加入restlet和jsonlib的dependency条目,这两个都可以在网上找到,但是要注意一点:因为他们会依赖spring2.5.6的包,这会和我们现在的spring3.0冲突,所以需要加入一个exclusion条目

5.加入opencrx/mdx依赖,但是实际上opencrx/mdx开发小组沉迷于cvs/ant之中,给我的答复是:现状很好,无须改变。没办法只好自食其力了.
首先,自行定义opencrx/mdx的groupId,artifactId,下面可以参考:
<dependency>
            <groupId>opencrx</groupId>
            <artifactId>opencrx-application</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>opencrx</groupId>
            <artifactId>opencrx-base</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>opencrx</groupId>
            <artifactId>opencrx-extension</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>opencrx</groupId>
            <artifactId>opencrx-kernel</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>opencrx</groupId>
            <artifactId>opencrx-mail</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>opencrx</groupId>
            <artifactId>opencrx-security</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.core</groupId>
            <artifactId>openmdx-application</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.core</groupId>
            <artifactId>openmdx-base</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.core</groupId>
            <artifactId>openmdx-base-ext</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.core</groupId>
            <artifactId>openmdx-system</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.core</groupId>
            <artifactId>tomcat-juli</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.portal</groupId>
            <artifactId>openmdx-portal</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.security</groupId>
            <artifactId>openmdx-ldap</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.security</groupId>
            <artifactId>openmdx-radius</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>openmdx.security</groupId>
            <artifactId>openmdx-security</artifactId>
            <version>2.4.1</version>
            <scope>provided</scope>
        </dependency>
其次,手工安装相应的jar包到maven repository。这里有一个可选项,就是在本地网上安装maven repository代理服务器,那么可以统一安装在这台服务器上,所有开发人员可以从这里统一更新所有的开发用jar包。

6. 以上整个过程中,如果你是在eclipse环境中,并且安装了maven-elcipse插件,那么每次修改了pom.xml文件,都会自动进行 maven upate,这样会验证其中所有的依赖包的有效性。如果没有问题,那么可以执行:mvn install,把整个工程构建并打包到本地maven repository之中。因为本人对于Maven研究还不深入,所以把部署的任务也放到了install之中执行。理论上说,可以增加maven build的run命令在eclipse里面,这样可以更方便。大家如果有知道这个方法的,请告知。

最后总结一下:如果你对maven有 更深的了解,就会发现更多的惊喜。同样限于时间,我还没有加入编译gwt的任务到pom中,简单尝试下,可以进行编译但是不能生成静态页面到我指定的目 录,等有时间再加上这个吧,这样就可以整合gwt compile和deploy into tomcat的任务了。另外,顺便对比一下maven和ant的build,在maven下一旦包安装好整个过程不超过10s.但是如果没有注释掉 appengine就会耗费2分钟以上,我也没去研究这到底是什么原因,大家可以自己研究看看,相信其中可以发现更多的功能和收获更多的开发乐趣。


第一篇中,有一张整个架构的示意图,其中联系前台和后台的是http/Rest标准协议。现在我们就先从前台所使用的技术GWT和Restlet开始。

1.GWT 作为一种基于java开发Ajax页面的技术框架,真正的发展是开始于1.5版本(支持了大部分java5的特性和库),到了1.6又再一次进行了大的修 订。目前来说,它的最大优势就是支持大量开源的控件,包括gwt-ext,smartgwt等等,和另外一个要强调的,所见即所得的编辑器GWT Designer(目前似乎已经被更名为window builder),有了这两点,对于java/j2ee开发人员来说几乎是接近了C/S时代RAD的开发效率了。可惜的是,对于一个完整的web应用来说 web ui仅仅是前台的一部分,在webservice/rest协议出现前,前端展现和后台程序耦合性达到了令人发指的地步。GWT也因此没有更大范围的流行 起来,特别是国内,几乎看不到使用gwt成功的案例。难以设计/改变风格,不熟悉/适应这种开发方式,都是让人难以接近的原因。

2.Restlet 本身是一个完整的Rest实现框架,包括了前端、后台以及和其他技术的集成模块,我们在这里仅仅使用了和GWT集成的Restlet-GWT模块,大家有 兴趣也可以去restlet官方网站查看其它解决方案。不得不说,restlet在对于REST支持的领域上已经远远领先,特别是和GWT的集成上,几乎 为开发人员周到的考虑到了方方面面的需求,简化了GWT远程调用的繁琐(2个接口类、一个实现类再加上servlet的申明),对于get/post /put/delete来说,都仅仅几行代码就可以实现,下面是一个get方法的例子:
            public void onClick(Widget sender) {
                // Add an AJAX call to the server
                final Client client = new Client(Protocol.HTTP);
                client.get("http://localhost:8888/ping", new Callback() {
                    @Override
                    public void onEvent(Request request, Response response) {
                        button.setText(response.getEntity().getText());
                    }
                });
                dialogBox.hide();
            }

3.对于页面流程、验证/异常处理以及权限控制,在GWT框架下,都是一种新的挑战,但是也可以借鉴C/S的开发经验。
基 于角色和页面的编号,可以处理功能权限,基于角色的参数管理,可以控制相应的数据权限,基于opencrx可以提供SaaS的数据分区控制。验证和异常处 理依然可以分成前后台,分别处理。基于GWT,已经简化了web flow的处理,但是也同时绑定了业务处理的部分流程,只有通过尽可能多的重用页面组件来提供工作流程的灵活性。


服务器的现在最流行的框架就是Spring了,在Spring3.0 MVC中推出了REST的支持,正好可以和前端GWT+Restlet实现紧密而又松耦合的集成。
加 上提供JSONView的JSON-lib和后台持久层的opencrx,一个完整的后台框架由Spring+Jsonlib+opencrx来实现。其 中所有的service和controller以及opencrxContext对象都是由spring进行管理,但是对于数据库事务,由于 opencrx api的使用,需要显式的在代码中声明,这是一个小小的遗憾,好在所有的业务已经是对于Opencrx api的封装了,所以事务处理是独立且无需互相引用了。下面是spring配置文件的部分(包括了 jsonview,opencrxContext,controller等等的声明):
<!-- Dispatches requests mapped to POJO @Controllers implementations -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

<!--  gwt handler -->
<bean name="jsonView" class="com.omdasoft.core.ui.server.view.EISCoreJsonView" />
<!--
Maps request paths to @Controller classes; e.g. a path of /person
looks for a controller named PersonController
-->
<bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="0" />
</bean>
<context:component-scan base-package="com.omdasoft.core.ui.server.mvc"
use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
</context:component-scan>
<!--
Service beans list
-->
<context:property-placeholder location="classpath:common.properties" />
<bean id="opencrxContext" class="com.omdasoft.core.ui.server.common.OpenCrxContext"
scope="session">
<aop:scoped-proxy />
</bean>
<bean id="baseService" abstract="true">
<property name="context" ref="opencrxContext"></property>
</bean>
<bean id="userService" class="com.omdasoft.core.ui.server.service.UserService"
parent="baseService" />
是 不是觉得很简单?个人认为,一个好的框架应该是越简单越好,能够用最少的步骤完成大多数业务流程,并且在需要的时候可以扩展。一个试图包罗万象的框架除了 增加开发人员的学习和调试难度之外,没有任何好处。而一个无法替换的技术更是增加了框架扩展功能的难度。在我们开发的这个框架中,除了spring mvc是作为REST服务的提供者和服务端的管理容器无法替换之外,其他部分都可以按照需求更换为其他技术框架。例如,web层可以换成spring mvc管理的jsp等View,后台opencrx api可以更换成hibernate管理的持久层和自行设计的数据库,而中间的service(业务逻辑部分),几乎不需要大的修改。这也让我们更专注于 业务逻辑的定义和后台API接口的通用化、统一化。
由于本人对于Spring ViewResolver认识还不深入,所以在Controller中,方法直接返回了ModelAndView(JsonView),而不是简单的String,这也是进一步优化框架的方向之一。下面是一段用户注册的代码:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ModelAndView addUser(@ModelAttribute("account") Contact contact) {
Map model = new HashMap();
try {
userService.createContact(contact);

            model.put(ResultConstant.REDIRECT_SCREEN, contact);
} catch (ServiceException e) {
model.put(ResultConstant.ERROR_CODE, "/account/"
+ contact.getId());
}
return new ModelAndView("jsonView", model);
}
另 外要注意的是,和spring mvc处理异常的方式不同,基于Rest/Json对象来处理前台请求时,返回值和异常必须统一在一个类型格式下,这样才方便前台获取对应的数据和异常信 息,而Validation异常可以采用GWT-VL框架的方式来处理,直接抛出ValidationException异常,然后在GWT中,可以用 GWT-VL的方法来捕获这个异常。


posted on 2010-12-20 00:01 礼物 阅读(1588) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。

网站导航: