27.3. Spring/EJB3

27.3.1. 前言

一个 Web应用并不仅仅只是UI展现层那么简单,我们还要考虑数据持久化、事物完整性保障等等。我们既希望 OperaMasks 能够提供完整解决方案,又不需要它重新发明轮子,因为后台的业务编程模型已经有了优秀的并且经过业界验证的解决方案,如 Spring、EJB 3等。其中,又以 Spring 在业界的使用更为广泛,因此,本文主要介绍 OperaMasks 是怎样和 Spring 紧密集成的,并顺带着介绍一下 EJB 3的支持。

27.3.2. Spring is here

我们说:OperaMasks与Spring是无缝集成的。各位同学一看到“无缝集成”这几个字,想必嘴角一撇,心想:典型的广告用语。那么,OperaMasks在与 Spring 集成这方面上,到底做了哪些工作?简单介绍,三个特性而已:

  • 所有在 Spring 中定义的 Bean,都能够被当成 OperaMasks 中的 LiteBean 直接使用。

    你可以理解成:假设你在 Spring 中定义了一个 MySpringBean,那么,在OperaMasks中,你可以 #{MySpringBean} 直接引用此Bean。

  • 所有在 OperaMasks 中定义的 LiteBean,都能够被当成 Spring 中的 Bean 直接使用。

    你可以理解成:假设你在 OperaMasks 中定义了一个 MyLiteBean,那么,在 Spring 中,你可以通过 ApplicationContext.getBean("MyLiteBean")获得它

  • 在 Spring 中定义的 Bean,同样能够获得 IoVC 的支持。

    你可以理解成:假设你在某个 Spring Bean的某个属性上加以 @Bind 的声明,那么该属性依然可以被绑定到页面控件

至于有了上述三个特性,算不算得“无缝集成”,则是仁者见仁,智者见智了。

27.3.3. 对Spring集成的示例性说明

做个简单的例子,来验证第一点:所有在 Spring 中定义的 Bean,都能够被当成 OperaMasks 中的 LiteBean 直接使用。

首先将 Spring 等相关 jar 包部署到 WEB-INF/lib 目录中,然后在web.xml中增加以下内容(这都是 Spring 的相关知识):

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>

再在 WEB-INF 中放一个 applicationContext.xml 文件,其内容及详细注释如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation=
          "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">

  <!-- Attention:this bean is essential!
       Making LiteBeans available in Spring container. -->
  <bean class="org.operamasks.faces.spring.ManagedBeanConfigurer"/>

  <!-- Attention: this bean is recommended!  
       Config the apusic application server transaction manager as the Spring transaction manager -->
  <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManagerName">
      <value>java:/TransactionManager</value>
    </property>
  </bean>

  <!-- This is a sample bean -->
  <bean id="MySpringBean" class="demo.MySpringBean" scope="session">
    <property name="name" value="Kevin"/>
  </bean>
</beans>

请注意:上面的 org.operamasks.faces.spring.ManagedBeanConfigurer是 OperaMasks 默认提供的一个 Spring Bean 定义,必须在Spring中配置此Bean,它的作用是:让 OperaMasks 中的 LiteBean 同样能够被 Spring 所感应到。同时,如果你使用 Apusic 应用服务器,我们还建议你正确配置 Apusic 应用服务器的 TransactionManager。

将我们的注意力回到 MySpringBean 的定义上,以下是示例性代码:

public class MySpringBean {

  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String sayHello() {
    System.out.println("hello " + name);
    return null;
  }

  @Override
  public String toString() {
    return "MySpringBean";
  }

}

我们再准备一个 mySpring.xhtml的faces page:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
  renderKitId="AJAX">
  <w:page title="spring">
    <w:form>
      <layout:panelGrid columns="2">
        <w:textField value="#{MySpringBean.name}" />
        <w:button value="sayHello" action="#{MySpringBean.sayHello}" />
      </layout:panelGrid>
    </w:form>
  </w:page>
</f:view>

此页面的运行效果如下:

运行效果

图 27.9. 运行效果


我们可以看到,<w:textField>能够正常显示name属性,并且,当我们点击 sayHello 按钮时,程序也可以正常运行。由此可见:在 OperaMasks 中,是可以把一个 Spring Bean 当成 LiteBean 来使用的。

再来验证第二点:所有在 OperaMasks 中定义的 LiteBean,都能够被当成 Spring 中的 Bean 直接使用。

首先声明一个LiteBean:

@ManagedBean(scope=ManagedBeanScope.SESSION)
public class MyLiteBean {

  @Override
  public String toString() {
    return "MyLiteBean";
  }
}

然后再写一个test.xhtml页面测试一下:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
 xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
 xmlns:h="http://java.sun.com/jsf/html" xmlns:ajax="http://www.apusic.com/jsf/ajax"
 renderKitId="AJAX">
 <w:head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 </w:head>
 <w:page title="Insert title here">
  <h:outputText id="spring"></h:outputText>
  <br/>
  <h:outputText id="elite"></h:outputText>
 </w:page>
</f:view>

声明testBean:

@ManagedBean(name = "testBean", scope = ManagedBeanScope.SESSION)
public class TestBean implements Serializable {
 
 @Bind(id = "spring", attribute = "value")
 private String spring;

 @Bind(id = "elite", attribute = "value")
 private String elite;

 @BeforeRender
 public void beforeRender(boolean isPostback) {
  WebApplicationContext appContext = WebApplicationContextUtils
    .getWebApplicationContext(((HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(true)).getServletContext());
  MySpringBean mySpringBean = (MySpringBean) appContext.getBean("MySpringBean");
  spring = "The bean defined in Spring: " + mySpringBean;
  MyLiteBean myLiteBean = (MyLiteBean) appContext.getBean("MyLiteBean");
  elite = "The bean defined in AOM: " + myLiteBean;
 }
}

此页面的运行效果如下:

运行效果

图 27.10. 运行效果


由此可见,在 OperaMasks 中定义的 LiteBean,在 Spring 容器中是同样可见的。

最后再来验证第三点:在 Spring 中定义的 Bean,同样能够获得 IoVC 的支持。

首先,根据“约定优于配置”原则,mySpring.xhtml页面是可以自动找到 MySpringBean 的,那么,我们将 MySpringBean 修订成这样:

public class MySpringBean {

  @Bind
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
 
  @Action
  public String sayHello() {
    System.out.println("hello " + name);
    return null;
  }

  @Override
  public String toString() {
    return "MySpringBean";
  }
}

然后再将 mySpring.xhtml 修订成如下示例:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
  xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"
  renderKitId="AJAX">
  <w:page title="spring">
    <w:form>
      <layout:panelGrid columns="2">
        <w:textField id="name"/>
        <w:button id="sayHello"/>
      </layout:panelGrid>
    </w:form>
  </w:page>
</f:view>

页面执行效果如下:

运行效果

图 27.11. 运行效果


页面可以正常显示及运行,由此可见:在Spring中定义的Bean,其IoVC能力依然得以保留。

最后,再看一下基于IoVC思想下的 mySpring.xhtml 的源码,忍不住再赞一下:)

27.3.4. EJB 3的支持

EJB 3也是一种非常优秀的构件模型,可能是由于 EJB 2.x 版本带给大家的不佳印象,以至于 EJB 3 并没有得到像 Spring 那样的流行程度。 OperaMasks 对 EJB 3同样提供了良好的支持,总结成两句话:

  • 一个 EJB 3 对象也可以同时当作一个 LiteBean 来使用。

  • 你还可以通过 @EJB 注解,将 EJB 对象注入到 LiteBean中使用,并且,这种做法更符合大家的习惯。

还是需要特意加以说明的是:将一个 EJB 3对象当成一个 LiteBean 使用,需要更改 EJB容器的实现代码,因此,这个特性目前只有 Apusic AS服务器予以支持。