4.4避免过度的双向映射
在Hello World一例中 ,通过将数据组件作为业务组件的属性,可以轻易的减少或者消除双向映射。在JSF页面中,通过这种方式,开发者只需要引用业务组件,而不需要在业务组件和数据组件之间的双向映射。例如,开发者可以修改 ManagerAction类为以下所述。
依赖双向映射是一个非常实用的设计模式。但是,正如其他设计模式,过度使用就会有害。过度的依赖双向映射让代码变得难以阅读,因为开发者必须理解每个注入的组件出自何处。过度的依赖双向映射也能增加性能消耗,因为双向映射是在运行时进行。
@Stateless
@Name("manager")
public class ManagerAction implements Manager {
private Person person;
public Person getPerson () {return person;}
public void setPerson (Person person) {
this.person = person;
}
private List <Person> fans;
public List<Person> getFans () {return fans;}
... ...
}
接下来,我们在页面上引用的属性如下:
<h:form>
Please enter your name:<br/>
<h:inputText value="#{manager.person.name}"/>
<br/>
<h:commandButton type="submit" value="Say Hello"
action="#{manager.sayHello}"/>
</h:form>
... ...
<h:dataTable value="#{manager.fans}" var="fan">
<h:column>
<h:outputText value="#{fan.name}"/>
</h:column>
</h:dataTable>
最后,具有了依赖管理的Seam是多用的。通常用数据访问业务组件封装数据是一项好的实践,特别是针对有状态业务组件。
4.5 JSF中的页面导航
本例中,只有一个页面。每次点击按钮后,JSF页面会重新显示更新过的数据模型。显然,大多数web应用多于一个页面。在JSF中,一个用户界面事件处理器能通过返回导航规则名称,决定下一步该显示哪个页面。例如,开发者可以在navigation.xml中定义以下导航规则。
<navigation-case>
<from-outcome>anotherPage</from-outcome>
<to-view-id>/anotherPage.jsp</to-view-id>
</navigation-case>
之后,如果sayHello()方法返回一个名为“another page”的字符串,JSF下一步就该展示anotherPage.jsp。UI事件处理器决定了接下来要显示哪个页面,从而为我们带来了有步骤的控制。
4.6 通过EntityManager访问数据库
JPA(Java Persistence API)EntityManager管理着关系数据库表与实体bean 之间的映射。EntityManager 在运行时由应用服务器创建。你能使用注解@PersistenceContext,注入一个EntityManager的实例。
EntityManager.persist()方法将实体bean存为与之对应数据表的一条记录。EntityManager.query()方法运行SQL化的查询,并以实体bean集合形式从数据库返回数据。更多细节请参考JPA文件中关于如何使用EntityManager和查询语言。在本书中,我们只用最简单的查询语句。
默认地,EntityManager将数据存于嵌入的HSQL数据库中。如果在本机上运行Jboss AS,可以通过以下步骤,为HSQL数据库开启一个GUI控制台:访问http://localhost:8080/jmx-console/,点击database=localDB,service=Hypersonic MBean服务,之后,点击在startDatabaseManager方法下方的“invoke”按钮。你就可以从控制台执行任意SQL指令。
5. 配置和打包
下面,我们将转移话题,讨论配置文件和应用程序打包。实际上,你可以通过Seam Gen命令行工具,生成几乎所有的配置文档和构造脚本文件。或者你也可以简单的重用在示例中的源文件。所以,如果你想首先学习Seam编程技术,但又担心接下来的配置和部署,这个是很正常的。你可以完全放心地跳过本节,需要的时候可以再次阅读。
本节中,我们集中探讨Seam EJB3组件配置,JBoss AS外的Seam POJO配置和部署当然是可行的。
大多数Seam配置文件都是XML文档。但是等等!我们刚才不是承诺Seam能让我们摆脱J2EE和Spring中的XML地狱吗?为什么它又有了XML文档呢? 是的,XML文档确实有很多用处。XML文档非常适合部署阶段的配置(例如web应用的根URL和后台数据库的定位)。因为它允许我们在部署阶段改变配置而不需要改变和重新编译源代码。它也适合粘合应用服务器中的不同子系统(例如,配置如何让JSF组件与Seam EJB3组件交互)。XML文档也非常适合表示层相关内容(例如网页和页面导航流程)。
我们反对在XML文档中重复已经存在于Java源代码中的信息。开发者很快就会发现,这个简单的SeamEJB3 应用有多个XML配置文档,每个文档那个都非常简短,并且没有一个包含存在于Java代码中的信息。换句话说,Seam中没有“XML代码”。
进一步讲,XML文档中的大多数内容都是静态的。所以开发者能在自己的Seam应用中轻松地重用这些文档。如何使用示例作为自己的应用模板的介绍,请参见附录B——使用应用示例作为模板。
我们将用下面几页来详细讲解示例应用的配置文档和打包后的目录结构。如果你没有耐心看下去,而且很满意这个应用模板,你可以跳过以下内容。不管怎样,不再罗嗦, 我们一起来了解hello world示例是如何进行配置和打包的。为了构建一个JBoss AS的部署Seam 应用,我们必须将以上所有java 类和配置文档打包为企业应用程序归档(EAR)。该例中,EAR文件是helloworld.ear。它包含了三个JAR文件那个和两个XML配置文档。
helloworld.ear
|+ app.war //包含Web页面等
|+ app.jar //包含Seam组件
|+ jboss-seam.jar // Seam库
|+ META-INF
|+ application.xml
|+ jboss-app.xml
源代码目录
在此项目的源代码中,resources/WEB-INF目录包含属于app.war/WEB-INF目录的配置文档。resources/META-INF目录包含属于app.jar/META-INF和helloworld.ear/META-INF的文档。Resources根目录包含属于根目录app.jar的文档。
application.xml文档列出了在EAR中的JAR文件,并为该应用指定了根URL。
<application>
<display-name>Seam Hello World</display-name>
<module>
<web>
<web-uri>app.war</web-uri>
<context-root>/helloworld</context-root>
</web>
</module>
<module>
<ejb>app.jar</ejb>
</module>
<module>
<java>jboss-seam.jar</java>
</module>
</application>
jboss-app.xml文档为该应用指定了类加载器,每个EAR应用的类加载器应该有一个唯一的名称。这里我们使用应用程序名作为类加载器的名称,以避免重复。
<jboss-app>
<loader-repository>
helloworld:archive=helloworld.ear
</loader-repository>
</jboss-app>
jboss-seam.jar是Seam发布Seam类库。app.war和app.jar文档由我们来建构。所以,下面我们要研究app.war和app.jar。
5.1. WAR文件
app.war是按照Web应用程序归档规范打包的JAR文件,它包含页面和标准的JSF/Seam配置文档。你还可以将JSF特有的类库文件放入WEB-INF/lib目录 (例如jboss-seam-ui.jar)。
app.war
|+ hello.jsp
|+ index.html
|+ WEB-INF
|+ web.xml
|+ faces-config.xml
|+ components.xml
|+ navigation.xml
web.xml文档是所有java EE web应用必需的。JSF用它来配置JSF servlet控制器,Seam用它来拦截所有的web请求。该配置文档的相当标准。
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="..."
xsi:schemaLocation="...">
<!-- Seam -->
<listener>
<listener-class>
org.jboss.seam.servlet.SeamListener
</listener-class>
</listener>
<!-- MyFaces -->
<listener>
<listener-class>
org.apache.myfaces.webapp.StartupServletContextListener
</listener-class>
</listener>
<context-param>
<param-name>
javax.faces.STATE_SAVING_METHOD
</param-name>
<param-value>client</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/navigation.xml</param-value>
</context-param>
</web-app>
faces-config.xml文档是JSF标准的配置文档,Seam用它来将其拦截器添加到JSF生命周期中。
<faces-config>
<lifecycle>
<phase-listener>
org.jboss.seam.jsf.SeamPhaseListener
</phase-listener>
</lifecycle>
</faces-config>
navigation.xml文档为多页面应用包含JSF页面导航规则。因为hello world示例只有一个简单的页面,因此该文档是空的。
components.xml文档包含Seam特有的配置选项,除jndi-pattern属性以外,其他都不依赖于应用。该属性必须包括EAR文档的名称,以便Seam通过其的JNDI全名访问EJB3 bean。
<components ...>
<core:init
jndi-pattern="helloworld/#{ejbName}/local"
debug="false"/>
<core:manager conversation-timeout="120000"/>
</components>
5.2. Seam组件JAR包
app.jar文档包含所有的EJB3bean 类(实体bean和会话bean)以及EJB3相关的配置文档。
app.jar
|+ Person.class // entity bean
|+ Manager.class // session bean interface
|+ ManagerAction.class // session bean
|+ seam.properties // empty file but needed
|+ META-INF
|+ ejb-jar.xml
|+ persistence.xml
seam.properties文档这儿是空但必需的,因为Jboss要通过它知道此JAR文件包含Seam EJB3 bean类,并且相应地处理注解。
ejb-jar.xml文档包含额外的配置信息,这些信息能重载或者增补EJB3 bean上的注解。在一个Seam应用中,它能将所有的EJB3 类加入Seam拦截器。我们能在所有的Seam应用中重用该文档。
<ejb-jar>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>
org.jboss.seam.ejb.SeamInterceptor
</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
persistence.xml文档为EJB3 实体bean配置了后台数据源。本例中,我们只是使用了被嵌入到JBoss AS中默认的HSQL数据库(也就是java:/DefaultDS数据源)。
<persistence>
<persistence-unit name="helloworld">
<provider>
org.hibernate.ejb.HibernatePersistence
</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto"
value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
这样,以上就是一个简单Seam应用所需的所有配置和打包。我们将在以后讨论到本书的更高级的主题时,涵盖更多的配置选项和类库。再次强调一下,Seam应用入门最简单的方法就是,不要担心这些配置文件,只需要从已有的应用模板做起。
6. 为何这么简单?
这就是hello world 应用,三个简单的Java类,一个JSF页面,一组静态配置文件。我们已经有了一个完整的数据库驱动的web应用。整个应用只需要的是少于30行的Java代码,没有一处“XML代码”。但是如果开发者有PHP背景,你可能仍然会问“何以这么简单?我能在php中使用更少的代码吗?”
好吧,答案就是Seam应用在理论上要比PHP(或者其他任何一种脚本语言)应用简单的多。Seam 组件模式允许我们,有所控制地,可维护地给应用增加更多的功能。我们很快就会发现,Seam组件使开发有状态的和有事务的web应用变得易如反掌。对象关系映射框架(例如:实体bean)允许我们将注意力放在抽象数据模型上,而不需要处理数据库特有的SQL语句。
本文是基于该书的第一章和第二章。后面的章节中,我们继续讨论如何使用Seam组件继续开发复杂的Seam 应用。参见本书目录来查看本书的所有主题。
请看Gavin King的两个访谈录来查看以往关于Seam的话题。
- JBoss Seam 1.1 Indepth: An Interview with Gavin King
- JBoss Seam 1.0: rethinking web application architecture
关于作者
Dr. Michael Yuan是JBoss Seam: Simplicity and Power Beyond Java EE 5.0一书的作者。该书探讨了下一代web应用框架。他同时也是Nokia Smartphone Hacks一书和其他三本技术读物的作者。Michael潜心研究轻量级企业web应用,终端对终端的移动应用开发。你可以通过他的博客联系他。
查看英文原文:Introduction to JBoss Seam
译者简介: 包亮,一名普通的程序员,喜欢敏捷实践,喜欢"懒惰",减少重复,尽可能让工作变得简单。几年来,一直通过网络汲取知识,也希望通过网络将知识与人分享 。志愿参与InfoQ中文站内容建设,请邮件至china-editorial@infoq.com。
<script type="text/javascript"></script>
<script src="/scripts/forum.js;jsessionid=EB50EBDE36DF1B83D0AFAA386E6A7CEF?rev=new" type="text/javascript"></script>
Seam 2.0正式发布 发表人 qi xiang 发表于 2007年11月6日 上午6时2分
<script type="text/javascript"></script>
分享到:
2 条回复
回复