第1 页 共3页123»

December 18th, 2008王者归来之Seam ?!

王者归来之Seam??!!!

前言:
seam :  以前在项目开发的过程中,你需要引入很多框架(比如:hibernate, jsf , 工作流引擎….) ,  JBOSS说,你们自己做整合,不专业,我来帮你们做了这个工作吧,我来整合,肯定比你们自己整合要好。于此,JBOSS招兵买马,把hibernate的核心团队,JSF规范制定核心团队,EJB3规范制定核心团队等等,都找来当咨询顾问,共讨整合之事,如此诞生了一个新宝宝,取名为SEAM

使用Seam已经有1个多月的时间了,对Seam有一些简单的了解,谈谈我对Seam的一点感想吧。Seam的目标就是把各种优秀的开源系统,J2EE规范,都通过seam这个框架整合起来,从而简化开发工作,提高开发效率。我想这是seam的目标。

seam亮点:

1  JSF和EJB3.0规范实现与整合是亮点之一。

seam实现了SUN公司JSF规范做了不错的工作,在开发JSF应用时,简化了一些JSF。 Seam框架试图简化JSF的开发比如JSF的异常处理,XML配置,JSF组件的调用等等,在开发的时候让JSF和后端的业务逻辑组件(business service bean)达到紧凑,易用的效果。

2 Seam 很有意思的组件管理

我没有去看seam的源代码,但是从seam的介绍文档里有一些简要的介绍,如果有说得不对的地方,仅为交流。
Seam有2种方式来管理组件,一种为了跟JSF,JSP, servlet 等接口,seam 在web.xml 里添加了一个SeamListener,把JSF所需要的组件实例缓存到ServletContext或者HttpSession中,当请求完成或者Session失效时销毁组件实例.

另一种方式是Seam业务组件,这个组件的生命周期管理,类似EJB3规范里规定的方式,Seam业务组件的生命周期管理是否就是EJB3组件规范的实现,还是有自己的管理方式,这些方面的问题,将在以后再去深入了解。
总之,有了对组件的生命周期管理,您不需要去关心事务,并发,同步等问题。我一直都觉得EJB3的组件生命周期管理,比Spring的实现要舒服很多(Spring的组件生命周期管理,还要实现Spring 的InitializingBean,DisposableBean 这2个类,EJB3的组件就是一个很干净的POJO)。

3 Hibernate,Seam中集成的经典JPA

EJB3的JPA规范,作为EJB3的重要组成部分,Seam中对此规范的实现,整合了Hibernate3. 在使用的过程中,好像针对Hibernate3做了一些优化,让您在开发过程中,使用hibernate3更加简单一些了。

EJB2的持久化方案,在过去的几年,被hibernate3之前的版本彻底打败,EJB3开始, 这2家冤家对头,居然走到了一起。在ejb3的JPA规范发布不久,Hibernate就开始支持这一规范, 以前的相互指责,现在变成相互吹捧。

(题外话:其实我在做项目的过程中是很不喜欢hibernate的,不是因为他能提高效率的话,早就抛弃它了。我更喜欢ibatis一些,都是手工写的SQL代码做成映射关系,效率提高很多,可惜他现在对ejb3规范还不支持。虽然开发的时候会多写一点代码,那可是值得的)

Seam真的是王者归来,我见未必。让我们跳出SEAM看seam。

不要看seam的介绍文档说得多好听,好比一个人自己说自己多厉害一样,没用,还是要人家去评价才行。 在你使用seam之前,请仔细评估自己的需求,是否需要使用他。

1,你的项目是否需要使用EJB?

SEAM 在文档里有说明,SEAM也可以开发类似struts+spring+hibernate一样的轻量级架构的系统,但是那是胡说八道。我可以负责任的告诉 你,seam是一个很重的架构,他大多数的技术体系都是偏重于EJB,用他开发SSH类似的系统,只会误事误工。

2, JSF是否适合你的项目?

JSF 是SUN公司主推的一种表示层解决方案。可惜生不逢时,用JSF做企业应用,有Flex,Silverlight等技术比他强。 用JSF做WEB网页方向的应用,JSF更是滑稽可笑。也许有人说,JSF跟Seam有什么关系?但是我告诉你,seam核心目标之一就是整合JSF做表 示层开发,是他整合目标的重要组成部分。如果你打算应用seam,那么表示层的开发就一定绕不过JSF这一关。尽管seam说,他不用JSF也可以,但是 不用JSF,用什么!! 没有其它好的选择了。

3,开发效率低

呵呵,seam说我的开发效率很高嘛,但那是他自己说的嘛,seam开发环境只能在Jboss里运行才有良好的表现,Jboss在开发,调试,部署还是非常麻烦,和tomcat比效率低很多。

4,学习成本

seam引入了很多相关的其它框架包,假设你有良好的JAVA基础,一个未使用过seam的开发团队,熟悉seam起码也要3周以上的时间,这是我见过的学习成本最高的框架,他整合的框架很多,技术面很广.

5,大型系统的解决方案,seam还是无法提供

一个大型的系统,核心的关键点是:负载均衡,集中缓存处理,并行计算, 分布式存取等, seam面对这些问题,一样显得力不从心,这些问题还是需要架构师自己动手解决。而且seam中有一个web beans的技术(我在想,他使用这个是否考虑到整合JSF才这么做呢!),给负载均衡, session的处理还不及其它框架灵活。

Seam是否是归来的王者? 让我们拭目以待!

文章内容只是个人之见,肤浅之处,意在交流。

Share/Save/Bookmark

当你用jsf的<h:selectOneMenu />之类的控件选择实体时,小心你的实体的equals方法,否则你可能就会遇到”value is not valid”的验证错误。比如,如果你的页面里有这样一段:

<h:selectOneMenu value="#{fooAction.foo}">
    <s:selectItems value="#{fooList.resultList}" var="foo" label="#{foo.name}"/>
    <s:convertEntity />
</h:selectOneMenu>

你的Foo实体类的equals方法又是这样写的:

public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    final Foo other = (Area) obj;
    if (id == null) {
        if (other.id != null)
            return false;
    } else if (!id.equals(other.id))
        return false;
    return true;
}

很好,你很可能就有机会遭遇seam下这个神奇的value is not valid验证问题了。到底原因何在?原因就在于seam不仅使用了jsf还使用了hibernate,而你的equals方法没有考虑到被对比的双方可 能一个是实体类,另一个可能是被hibernate动态增强过的类。JSF在客户端提交表单后,会验证客户端选中的元素是否是当初服务端给他的那些元素。 所以,JSF会拿经converter得到的实体与当初的集合里的实体一一调用equals方法对比,如果找不到一个在集合中的实体与提交来的一样,就报 错。如果集合里的对象是hibernate 延迟加载时的一个stub,那正常的对象和这个stub对比时就可能出错。对于上面的例子,getClass() != obj.getClass() 会为真,而 other.id != null 也可能为真。怎么办?改写equals方法,如下:

public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!getClass().isAssignableFrom(obj.getClass()))
        return false;
    final Area other = (Area) obj;
    if (id == null) {
        if (other.getId() != null)
            return false;
    } else if (!id.equals(other.getId()))
        return false;
    return true;
}

isAssignableFrom方法对子类调用时一样为真。other.getId()时,则会初始化stub,取出id的值。

还有一种更简单的方法,那就是不要让用户选择实体,而是选择实体的id,那就没这个问题了。

Share/Save/Bookmark

July 17th, 2008在seam中使用spring

选择seam并不代表要放弃spring, 两者并不互斥,相反,可以很好的协作。seam对spring提供了很好的兼容性。那么在seam里如何使用spring里的代码呢?

POJO代码的重用

正如spring管理的bean是POJO一样,seam管理的组件,也是POJO的,而且连ejb3也是POJO的,所以直接的重用以前用在 spring里的代码的方法,就是直接重用POJO了。以前我们就applicationContext.xml来配置bean,现在只需要换一个语法就 能把spring的POJO bean变成seam的component。例如,对于spring的bean:

<bean id=”testBean” class=”org.test.TestBean”>
<property name=”stringProperty” value=”this is a string” />
<property name=”intProperty” value=”3″ />
<property name=”anotherBean” ref=”anotherBean” />
</bean>

对应到seam里就是:

<component name=”testBean” class=”org.test.TestBean”>
<property name=”stringProperty”>this is a string</property>
<property name=”intProperty”>3</property>
<property name=”anotherBean”>#{anotherBean}</property>
</component>

大部分的POJO可能这就可以搞定了。但是要注意:
假如这个POJO依赖于spring容器,比如它实现了spring的bean生命周期回调方法,类似于  afterPropertiesSet() , 那么你也得让seam去模仿spring容器,在适应的时候,去调用这些回调方法。比如:

public class TestBeanAdapter extends TestBean {
@Create
public void afterPropertiesSet() {
super.afterPropertiesSet();
}
}


通JSF EL Resolver来桥接

只要实现了EL Resolver, 并在faces-config.xml里注册它,那么在JSF里使用EL表达式时,就会自动地调用EL Resolver来寻找这个EL表达式对应的对象等。seam当然实现了它自己的Resolver,而spring也实了这个Resolver,如果把它 们都注册上,那么JSF EL 将变成seam与spring的一座桥梁。用下面代码注册spring EL Resolver:

<faces-config>
<application>         
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver 
</el-resolver>
</application>
</faces-config>

如果你定义了spring bean testBean, 那么在seam组件里,你就可以通过 @In(”#{testBean}”) 来注入spring的bean。但是要记住,这种方法获得的对象,完全是由spring负责组装的地地道道的spring的bean, 所以它不具有seam component具有功能,比如双向注入,和seam的上下文完美一体,延长的持久化上下文(extended persistence context)。

seam里的spring

最完美的方案,我想你也想到了,就是让seam来管理spring容器,并加入seam的增强功能。这是可以做得到的,并且已经做到了,感谢spring的灵活性,和seam开发者们的努力。像下面那样在conponents.xml里加入spring名称空间:

<components xmlns=”http://jboss.com/products/seam/components”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:spring=”http://jboss.com/products/seam/spring”
xsi:schemaLocation=”
http://jboss.com/products/seam/components
http://jboss.com/products/seam/components-2.0.xsd
http://jboss.com/products/seam/spring
http://jboss.com/products/seam/spring-2.0.xsd”>
<!– component declarations –>
</components>

现在你可以在components.xml里使用<spring:context-loader />了:

<spring:context-loader>
<spring:config-locations>
<value>classpath:spring-beans-persistence.xml</value>
<value>classpath:spring-beans-service.xml</value>
</spring:config-locations>
</spring:context-loader>

好了,现在当你启动seam时,seam便会到你指定的位置去寻找spring的配置,并起动一个spring容器。为了能让seam知道要如何增强 spring里的bean,我们需要在spring那边做一些手脚。在spring的配置文件里加入seam的名称空间如下:

<beans xmlns=”http://www.springframework.org/schema/beans”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:seam=”http://jboss.com/products/seam/spring-seam”
xsi:schemaLocation=”
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://jboss.com/products/seam/spring-seam
http://jboss.com/products/seam/spring-seam-2.0.xsd”>
<!– bean definitions –>
</bean>

这样,你就可以在spring的配置文件里使用以下三种元素来定义如何增强spring bean了:

  1. <seam:conponent />  把一个spring bean变成seam component
  2. <seam:instance> 向一个spring bean里注入seam component
  3. <seam:configure-scopes> 定义一些默认的配置

看看下面的使用举例,我想你根本不需要解释就明白了:

<bean id=”aBean” class=”org.test.TestBean” scope=”prototype”>
<property name=”aString” value=”a string” />
<seam:component name=”testBean” scope=”CONVERSATION” auto-create=”true” />
</bean>

<bean id=”anotherBean” class=”org.test.AnotherBean” scope=”prototype”>
<property name=”aBean”>
<seam:instance name=”testBean” />
</property>
</bean>

<seam:configure-scopes default-auto-create=”true”/>

spring的 aBean在seam里被叫做 testBean, 并且它在 conversation scope里,spring的anotherBean在创建它时,会把testBean注入进去。非常简单吧?

但是,在使用过程中,要注意,seam不能对spring通过jdk 动态代理增强过的类进行再增强,所以如果要让一个spring的bean参予到seam里来,就不要对它使用动态代理的方式来实现AOP,你需要在spring的配置文件里做如下配置:

<aop:config proxy-target-class=”true”>
……
</aop:config>

好了,至此,在seam中使用spring的方法就介绍完了。

Share/Save/Bookmark

July 12th, 2008Seam增强了JSF

Seam采用的JSF作为表现层技术,但是标准的jsf有很多的不足之处。

1. JSF对POST方法的http request的依赖太强了,只有POST请求的数据才能直接绑定到后台的组件上,GET方法的则不可以。
2. 根据JSF的规范,当收到一个GET请求后,JSF走完RESTORE VIEW PHARSE 就会直接到RENDER RESPONSE PHARSE,而不会做INVOKE APPLICATION,所以GET方法的请求不能触发业务逻辑。

这些限定让JSF在某些方面不太方便,比如书签功能。因为浏览器的书签都是记下当前页的URL,而不会记下POST请求里的参数。SEAM虽然采用了JSF,但是加入了Page parameter 和 page action,弥补了JSF在这方面的缺陷。

Page parameter是将GET请求时的参数直接绑定到组件上的技术。Page action则是在生成页面之前调用业务逻辑的技术。这两者的配置都可以在WEB-INF/pages.xml或者 *.page.xml里完成。比如:

    <page view-id="/calculator.jsp" action="#{calculator.calculate}">
       <param name="x" value="#{calculator.lhs}"/>
       <param name="y" value="#{calculator.rhs}"/>
       <param name="op" converter="#{operatorConverter}" value="#{calculator.op}"/>
    </page>

上 面的配置,会让用GET方法访问/calculator.jsp时的参数x, y, op分别绑定到calculator的lhs, rhs和op属性上,然后执行calculator的calculate()方法。如果你只需要在页面上传递参数,而并不需要绑定到某个组件上,只需要写成:

    <page view-id="/calculator.jsp" >
        <param name="result" />
    </page>

这样以GET方法传来的名为result的参数将继续以result为名字在Page Scope内传递下去。

Page action 还可以按条件执行:

    <page view-id="/calculator.jsp" >
       <param name="x" value="#{calculator.lhs}"/>
       <param name="y" value="#{calculator.rhs}"/>
       <param name="op" converter="#{operatorConverter}" value="#{calculator.op}"/>
       <action execute="#{calculator.calculate}" if="#{calculator.op != null}" />
    </page>

除了在pages.xml, *.page.xml里配置,还可以用seam的组件s:link s:button来实现指定page action,例如:

     <s:link view="/calculator.jsp" action="#{calculator.calculate}" ></s:link>

这 样,当这个链接被点击后,会引发一个对/calculator.jsp的GET请求,并且在生成/calculator.jsp页面之前执行 #{calculator.calculate} 方法。如果用普通的h:commandLink的话,则会提交表单,引发POST请求。

使用page action时要注意,在处理POST请求的时候,page action会在INVOKE APPLICATION PHARSE之后执行。

Share/Save/Bookmark

尽管seam文档和其它相关的资料中,都声称entityManager的抽象程度已经很高了,没有使用DAO模式的需要,但是从这次的项目实践来看,将 部分的数据访问放到DAO里是很有必要的。首先,使用DAO有易于做单测。如果不使用DAO,需要访问数据库的Action就注入一个 entityManager直接访问数据库,这样写起来方便,但是在单测这个action的时候,要么得做一个假的entityManager,要么得真 的创建一个entityManager并准备好数据。这些都比做一个假的DAO来得要复杂。其次,如果每个ACTION里都自己拼接sql来查询数据,这 些查询SQL将会分散到各个ACTION中,难以管理和维护。再次,DAO更易于重用。很多的时候,一个查询语句在很多地方都初用到,如果每个地方都写一 遍自己的SQL,显然是不好的,放到一个单独的组件里做成DAO,将会更加易于重用。任何需要的ACTION都只要注入这个DAO就可以用了。


那么如何实现DAO才好呢?为了尽量让这个被经常调用到的组件轻量化,我觉得使用的statelss scope的普通java bean 就是行了,为了便于测试使用dao的其它组件,为DAO设计成接口与实现分离会更加地好。

Share/Save/Bookmark


第1 页 共3页123»
© 2007 涂0实验室 | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress