SeamFramework.orgCommunity Documentation

第 3 章 使用Web Beans的参考实现

3.1. 使用JBoss AS 5
3.2. 使用Apache Tomcat 6.0
3.3. 使用Glassfish
3.4. 猜数字例子
3.4.1. 猜数字例子
3.5. 翻译器例子

Web Bean参考实现由Seam项目开发。你可以从the downloads page下载最新的开发者版本。

Web Beans RI自带了两个例子:webbeans-numberguess (一个仅包含一个简单Bean的WAR应用例子)和webbeans-translator (一个包含企业Bean的EAR应用例子)。为了运行例子,你需要:

当前,Web Beans参考实现只能运行在JBoss AS 5之上。你需要从jboss.org下载JBoss AS 5.0.0.GA, 然后解压。例如:

$ cd /Applications
$ unzip ~/jboss-5.0.0.GA.zip

然后从seamframework.org下载Web Beans的参考实现,然后解压。例如:

$ cd ~/
$ unzip ~/webbeans-1.0.0.ALPHA1.zip

然后,我们需要告诉Web Beans JBoss的位置。编辑jboss-as/build.properties,设置jboss.home属性。例如:

jboss.home=/Applications/jboss-5.0.0.GA

为了安装更新,你需要安装Ant 1.7.0,设置ANT_HOME环境变量。例如:

$ unzip apache-ant-1.7.0.zip
$ export ANT_HOME=~/apache-ant-1.7.0

然后,你需要安装更新,更新脚本使用Maven来自动下载Web Beans和EJB3。

$ cd webbeans-1.0.0.ALPHA1/jboss-as
$ ant update

现在,你可以部署你的第一个例子了!

提示

例子的构建脚本包含多个目标:

  • ant restart - 以exploded形式部署例子

  • ant explode - 无需重新部署,更新一个exploded形式部署的例子

  • ant deploy - 以压缩jar包形式部署例子

  • ant undeploy - 将例子从服务器中移除

  • ant clean - 清除例子

部署猜数字(numberguess)例子:

$ cd examples/numberguess
ant deploy

启动 JBoss AS:

jboss.home=/Applications/jboss-5.0.0.GA

提示

如果你使用Windows操作系统,则使用run.bat 脚本。

等待应用部署完毕,好好体验一下http://localhost:8080/webbeans-numberguess

Web Bean参考实现的第二个简单例子能够将你的文本翻译为拉丁文。猜数字例子是一个WAR应用,仅仅使用了一个简单Beans;翻译器例子是一个EAR应用,包含了打包在EJB模块中的企业Beans。试一下:

$ cd examples/traslator
ant deploy

等待应用部署,试一下http://localhost:8080/webbeans-translator

然后从seamframework.org下载Web Beans的参考实现,然后解压。例如:

$ cd /Applications
$ unzip ~/jboss-5.0.0.GA.zip

然后从seamframework.org下载Web Beans的参考实现,然后解压。例如:

$ cd ~/
$ unzip ~/webbeans-1.0.0.ALPHA1.zip

然后,我们需要告诉Web Beans JBoss的位置。编辑jboss-as/build.properties,设置jboss.home属性。例如:

jboss.home=/Applications/jboss-5.0.0.GA

提示

例子的构建脚本包含多个目标:

  • ant restart - 以exploded形式部署例子

  • ant explode - 无需重新部署,更新一个exploded形式部署的例子

  • ant deploy - 以压缩jar包形式部署例子

  • ant undeploy - 将例子从服务器中移除

  • ant clean - 清除例子

部署猜数字(numberguess)例子:

$ cd examples/traslator
ant deploy

启动Tomcat:

jboss.home=/Applications/jboss-5.0.0.GA

提示

如果你使用Windows操作系统,则使用run.bat 脚本。

等待应用部署完毕,好好体验一下http://localhost:8080/webbeans-numberguess

待办

在猜数字应用中,你有十次机会来猜一个1到100之间的数字。每次猜测之后,应用都会告诉你你猜的数字是高了还是低了。

猜数字应用由Web Beans,配置文件,Facelete JSF页面组成,打包为一个WAR。我们先看一下配置文件。

猜数字应用的所有的配置文件位于WEB-INF/,这个目录位于源码树的WebContent中。首先,我们在faces-config.xml文件中告诉JSF使用Faceletes:


<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
    
    <application>
        <view-handler
>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>

</faces-config
>

这有一个空的web-beans.xml文件,标识这个应用是一个Web Beans应用。

最后,这有一个 web.xml

Let's take a look at the Facelet view:

<!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"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:s="http://jboss.com/products/seam/taglib">

  <ui:composit(1)ion template="template.xhtml">
    <ui:define name="content">
       <h1
>Guess a number...</h1>
       <h:form(2) id="NumberGuessMain">
          <div style="color: red">
             <h:messages id="messages" globalOnly="false"/>
             <h:outputText id="Higher" value="Higher!" rendered="#{game.number gt game.guess and game.guess ne 0}"/>
             <h:outputText id="Lower" value="Lower!" rendered="#{game.number lt game.guess and game.guess ne 0}"/>
          </div>
   
          <div(3)>
             I'm thinking of a number between #{game.smallest} and #{game.biggest}.
             You have #{game.remainingGuesses} guesses.
          </div>
     
          <div>
             Y(4)our guess: 
             <h:inputText id="inputGuess" 
                          value="#{game.guess}" 
                          required="true" 
                          size="3" 
              (5)            disabled="#{game.number eq game.guess}">
                <f:validateLongRange maximum="#{game.biggest}" 
                                     minimum="#{game.smallest}"/>
             <(6)/h:inputText>
            <h:commandButton id="GuessButton"  
                             value="Guess" 
                             action="#{game.check}" 
                             disabled="#{game.number eq game.guess}"/>
          </div>
          <div>
            <h:commandButton id="RestartButton" value="Reset" action="#{game.reset}" immediate="true" />
          </div>
       </h:form>
    </ui:define>
  </ui:composition>
</html
>
1

Facelets is a templating language for JSF, here we are wrapping our page in a template which defines the header.

2

There are a number of messages which can be sent to the user, "Higher!", "Lower!" and "Correct!"

3

As the user guesses, the range of numbers they can guess gets smaller - this sentance changes to make sure they know what range to guess in.

4

This input field is bound to a Web Bean, using the value expression.

5

A range validator is used to make sure the user doesn't accidentally input a number outside of the range in which they can guess - if the validator wasn't here, the user might use up a guess on an out of range number.

6

And, of course, there must be a way for the user to send their guess to the server. Here we bind to an action method on the Web Bean.

这个例子包括4个类,前面两个是绑定类型。首先,这有一个@Random绑定类型,用来注入一个随机数:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface Random {}

这还有一个@MaxNumber绑定类型,用来注入一个最大值:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface MaxNumber {}

Generator类通过一个生产者(producer)方法创建一个随机数。它也通过一个生产者方法暴露可能的最大值:

@ApplicationScoped

public class Generator {
   
   private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
   
   private int maxNumber = 100;
   
   java.util.Random getRandom()
   {
      return random;
   }
   
   @Produces @Random int next() { 
      return getRandom().nextInt(maxNumber); 
   }
   
   @Produces @MaxNumber int getMaxNumber()
   {
      return maxNumber;
   }
}

你会注意到Generator是应用范围的;因此我们不会每次都得到一个不同的随机对象。

最终的应用中的Web Bean是会话范围的 Game

你也许注意到我们使用了 @Named注释,以便我们能够通过EL(表达式语言)在JSF页面中使用Bean。最后,我们通过构造器注入来初始化猜数字游戏并给它设一个随机数。当然,在玩家猜对数字后,我们需要告诉玩家他赢了,所以我们通过FacesMessage反馈给玩家一条信息。

package org.jboss.webbeans.examples.numberguess;



import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.webbeans.AnnotationLiteral;
import javax.webbeans.Current;
import javax.webbeans.Initializer;
import javax.webbeans.Named;
import javax.webbeans.SessionScoped;
import javax.webbeans.manager.Manager;
@Named
@SessionScoped
public class Game
{
   private int number;
   
   private int guess;
   private int smallest;
   private int biggest;
   private int remainingGuesses;
   
   @Current Manager manager;
   
   public Game()
   {
   }
   
   @Initializer
   Game(@MaxNumber int maxNumber)
   {      
      this.biggest = maxNumber;
   }
   public int getNumber()
   {
      return number;
   }
   
   public int getGuess()
   {
      return guess;
   }
   
   public void setGuess(int guess)
   {
      this.guess = guess;
   }
   
   public int getSmallest()
   {
      return smallest;
   }
   
   public int getBiggest()
   {
      return biggest;
   }
   
   public int getRemainingGuesses()
   {
      return remainingGuesses;
   }
   
   public String check()
   {
      if (guess
>number)
      {
         biggest = guess - 1;
      }
      if (guess<number)
      {
         smallest = guess + 1;
      }
      if (guess == number)
      {
         FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
      }
      remainingGuesses--;
      return null;
   }
   
   @PostConstruct
   public void reset()
   {
      this.smallest = 0;
      this.guess = 0;
      this.remainingGuesses = 10;
      this.number = manager.getInstanceByType(Integer.class, new AnnotationLiteral<Random
>(){});
   }
   
}

翻译器例子能够将你输入的句子翻译为拉丁文。

翻译器例子是一个EAR应用,包含EJBs和企业Beans。因此,它的结构比猜数字例子复杂。

首先,让我们看一下EAR聚合器,它位于webbeans-translator-ear模块下。Maven将为我们自动生成application.xmljboss-app.xml文件:


<plugin>
   <groupId
>org.apache.maven.plugins</groupId>
   <artifactId
>maven-ear-plugin</artifactId>
   <configuration>
      <modules>
         <webModule>
            <groupId
>org.jboss.webbeans.examples.translator</groupId>
            <artifactId
>webbeans-translator-war</artifactId>
            <contextRoot
>/webbeans-translator</contextRoot>
         </webModule>
      </modules>
      <jboss>
         <loader-repository
>webbeans.jboss.org:loader=webbeans-translator</loader-repository>
      </jboss>
   </configuration>
</plugin
>

我们需要在这里做些事情-首先我们需要设置上下文路径为一个不错的URL(http://localhost:8080/webbeans-translator),我们还需要将JBoss AS的类加载器隔离配置激活。

提示

如果你不使用Maven来生成这些文件,你将需要META-INF/jboss-app.xml


<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"
             version="5">
  <display-name
>webbeans-translator-ear</display-name>
  <description
>Ear Example for the reference implementation of JSR 299: Web Beans</description>
  
  <module>
    <web>
      <web-uri
>webbeans-translator.war</web-uri>
      <context-root
>/webbeans-translator</context-root>
    </web>
  </module>
  <module>
    <ejb
>webbeans-translator.jar</ejb>
  </module>
</application
>

然后,我们看一下WAR包。在猜数字例子中,我们需要faces-config.xml(配置Facelets)和一个位于WebContent/WEB-INF下的web.xml(配置JSF并将Web Beans服务加入Servlet容器中)。

更有意思的是用来翻译文本的facelet。在猜数字应用中我们有一个模板,这个模板套着表单(省略了表单):


<h:form id="NumberGuessMain">
            
   <table>
      <tr align="center" style="font-weight: bold" >
         <td>
            Your text
         </td>
         <td>
            Translation
         </td>
      </tr>
      <tr>
         <td>
            <h:inputTextarea id="text" value="#{translator.text}" required="true" rows="5" cols="80" />
         </td>
         <td>
            <h:outputText value="#{translator.translatedText}" />
         </td>
      </tr>
   </table>
   <div>
      <h:commandButton id="button" value="Translate" action="#{translator.translate}"/>
   </div>
   
</h:form
>

用户可以在左手边的文本区输入一些文本,点击翻译按钮查看右边的翻译结果。

最后,我们看一下EJB模块,webbeans-translator-ejb。在src/main/resources/META-INF下有两个配置文件,一个是空的web-beans.xml,用来标识这个档案包包含Web Beans,一个是ejb-jar.xml。Web Beans为所有的EJB提供注入和初始化服务,使用ejb-jar.xml文件来配置。你将在使用Web Beans的EJB项目中需要这些配置:

我们将最有意思的部分放在最后,那就是代码!这个例子有两个简单Beans, SentanceParserTextTranslator,还有两个企业Beans,TanslatorControllerBeanSentenceTranslator。现在你应该对Web Beans有点熟悉了,我们在这里着重最有意思的部分。

SentanceParserTextTranslator是相互依赖的Beans,TextTranslator使用构造器初始化:

public class TextTranslator { 

   private SentenceParser sentenceParser; 
   private Translator sentenceTranslator; 
   
   @Initializer
   TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) 
   { 
      this.sentenceParser = sentenceParser; 
      this.sentenceTranslator = sentenceTranslator;

TextTranslator是一个无状态Bean(拥有一个本地业务接口),这里是魔术展现的地方-当然,我们不会开发一个完整的翻译器,但我们可以开发一个不错的小玩意!

最后,这里又要一个面向UI的控制器,从用户输入处搜集文本,转发给翻译器。这个控制器是请求范围的,具名的,有状态的会话Bean,它可以将翻译器注入进来。

@Stateful

@RequestScoped
@Named("translator")
public class TranslatorControllerBean implements TranslatorController
{
   
   @Current TextTranslator translator;

这个Bean也拥有页面上所有域的getter和setter方法。

这个Bean是有状态会话Bean,我们需要有一个remove方法:

   @Remove

   public void remove()
   {
      
   }

Web Beans管理器在这个bean销毁的时候调用remove方法;在这个例子中是请求结束的时候。

Web Beans参考实现的例子到此结束。想要获得关于参考实现更多的知识或者帮助,请访问http://www.seamframework.org/WebBeans/Development

我们在各个方面都需要帮助-bug修复,新特性开发,例子开发和参考指南的翻译等等。