`
alienj
  • 浏览: 78813 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

第4章 上下文组件模型

阅读更多

4 上下文组件模型

 

Seam中的两个核心概念是上下文注释和组件注释。组件是有状态对象,普通的EJB,以及一个上下文相关联的一个组件实例,在上下文中被赋予了一个名字。双向注入为上下文的名字取别名到内部组件名(实例变量)提供了一种机制,允许组件树被Seam动态装配,和反装配,

 

让我们开始描述Seam中内建的上下文。

 

4.1. Seam 上下文

 

Seam上下文由框架创建和销毁。应用程序通过明确的Java API调用并不控制上下文划分。上下文通常是隐藏的。然而,在一些情况中,上下文通过注释划分。

 

基本的Seam上下文有:

* Stateless context——无状态上下文

* Event (or request) context——事件(或请求)上下文

* Page context——页面上下文

* Conversation context——“对话”上下文

* Session context——会话上下文

* Business process context——业务处理上下文

* Application context——应用上下文

 

你大概从servlet和相关的规范中已认识了这些上下文中的大部分。然而,对你来说他们中的两个可能是陌生的:“对话”上下文和业务处理上下文。在网页应用程序中的状态管理这样脆弱和容易出错的一个原因是:从业务逻辑视角来看,是由于三个内建上下文(请求、会话、应用)不是特别有目的造成的。例如,一个用户的注册会话,在实际的应用程序工作流期间是一个相当专制的模式。因而,多数Seam组件是涉及到“对话”或业务逻辑,原因在于他们在应用程序的期间是最有目的的上下文。

 

让我们依次看一看每一个上下文。

 

4.1.1. 无状态上下文

 

真正的无状态(无状态会话bean,原始的)组件总是存活在无状态上下文(这真是一个非上下文)中。无状态组件是不非常引人注意的,并且可论证其不是完全面向对象的。然而,它们是重要的,且通常很有用。

 

4.1.2. 事件上下文

 

事件上下文是“最窄的”有状态上下文,是网页请求上下文概念的一个普通化,涵盖了其他类型的事件。不过,关联到JSF请求生命周期的事件上下文是最重要的一个事件上下文例子,并且是你常见的一个。关联到事件上下文的组件在请求结束时被销毁,但是对请求的最小生命周期,它们的状态是可用的和明确的。

 

当你通过RMIRemote Method Invocaton远程方法调用)调用一个Seam组件,或者通过Seam远程,事件上下文只是调用时被创建和销毁。

 

4.1.3. 页面上下文

 

页面上下文允许你关联状态到一个渲染页面的一个特殊实例。

 

用你的事件侦听器,你能够初始化状态,或者在实际渲染页面时,然后你能从来自页面引发的任何事件访问它。对可点击列表,这是特别有用的,用在服务器边的改变的数据返回到列表。状态实际上被序列化到客户端,所以,这种结构用在多窗品操作和返回按钮是极其有力的。

 

4.1.4. “对话”上下文

 

Seam中,“对话”上下文是一个真正的重要概念。从用户的角度看,一个“对话”上下文是一个工作单元。它可以跨越这个用户的几个交互过程,几个请求,和几个数据库处理。但是对用户而言,一个“对话”解决了一个单一的问题。例如,“预订旅馆”,“签订合同”,“创建订单”是全部对话。你可能喜欢认为一个“对话”实现了一个单一的“使用情形”或者“用户经历”,然而关联不必是十分精确的。

 

一个“对话”维持了“在这个窗口,用户现在正在做什么” 相关联的状态。单个用户可以在进行中的任何时间点有多个“对话”,通常使用多窗口。

 

 “对话”上下文允许我们确保来自不同对话的状态不会冲突和引起程序缺陷。

 

让你习惯于用““对话”术语思考应用程序可能要花些时间。但是一旦你习惯了它,我们认为你会爱上这个概念,并且不得不用“对话”术语思考了!

 

后来的一些对话仅仅是一个单一请求。对话跨越多个请求,必须用Seam提供的注释先声明。

 

一些对话也是作业。一个作业是一个对话,在一个长期运行业务处理的术语方面,它是有意义的,并且,当它是成功完成,对触发一个业务处理状态转换有潜在的能力。Seam对任务声明提供了一个特别的注释集合。对话可以被嵌套,有一个对话发生在一个更广的对话“内部”。这是一个高级特色。

 

通常,对话状态实际上是被Seam维持在servlet会话和请求之间。Seam在空闲期间实现可配置的对话,自动销毁停止的对话。因此,如果用户放弃了对话,也能确保单个用户注册的会话状态不出现在范围之外。

 

在同一个处理中,Seam并发请求的序列化处理发在同一个长期运行的对话上下文中。

 

作为选择,Seam可以被配置保持对话状态在客户端游览器中。

 

4.1.5.会话上下文

 

一个会话上下文维持与用户注册会话相关联的状态。虽然有一些情形,在几个对话之间共享状态是有用的,除了有关用户操作的全局信息外,我们通常不赞成为维持组件而使用会话上下文。在JSR-168入口环境中,会话上下文代表会话端口。

 

4.1.6. 业务处理上下文

 

业务处理上下文维持与长期运行的业务处理相关联的状态。这个状态被BPM引擎 (JBoss jBPM)管理和产生持久化。业务处理跨越多个用户多个交互,所以,这个状态在多个用户之间被共享,除了定义明确的类别之外。当前的作业决定当前的业务处理实例,并且业务处理的生命周期用一个处理定义语言在外部定义,所以,对业务处理声明没有专门的注释。

 

4.1.7. 应用上下文

 

应用上下文是来自servlet规范的熟悉的servlet上下文。应用上下文主要用来维持像配置数据、参考数据或元模型这类的状态信息。例如,Seam存贮它自己的配置和元模型在应用上下文中。

 

4.1.8.上下文变量

 

一个上下文定义了一个命名空间,一个上下文变量集合。它们工作正如在servlet规范中的会话或请求属性。你可能捆绑任何你希望的值到上下文变量,然而,通常我们捆绑Seam组件实例到上下文变量。

所以,在一个上下文内,一个组件实例用上下文变量名(这是普遍的,但也不总是,如组件名一样)来识别。通过Contexts类你可以编程访问一个在特殊范围内的命名组件实例,其提供访问个别的线程绑定的Contexts接口的实例:

 

User user = (User) Contexts.getSessionContext().get("user");

 

你也可以设置或改变与名字关联的值:

 

Contexts.getSessionContext().set("user", user);

 

然而,通常,我们通过“注入”从一个上下文获取组件,并通过“注出”安置组件实例到一个上下文中。

 

4.1.9. 上下文搜索优先级

 

有时,如上面,从一个特殊已知范围获取组件实例。其它时间,以优先顺序,所有的有状态范围被搜索。这个顺序如下:

* Event context——事件上下文

* Page context——页面上下文

* Conversation context——对话上下文

* Session context——会话上下文

* Business process context——业务处理上下文

* Application context——应用上下文

 

你能通过调用Contexts.lookupInStatefulContexts()处理一个优先搜索。

无论何时,你用名字从一个JSF页面访问一个组件,一个优先搜索发生。

 

4.1.10.并发模型

 

无论是servlet 还是 EJB都没有定义任何工具管理来自同一个客户并发请求发生。servlet 容器只是让所有线程并发运行,并委托应用代码执行线程安全。EJB容器允许无状态组件进行并发访问,并且,如果多线程访问一个有状态会话bean会抛出一个异常。

 

这种行为可能被老式的基于细粒度的同步请求的网页应用程序认同。然而,对现代的应用程序,产生大量多细粒度、异步(AJAX)请求的应用,并发是一个生命的行为,并且,通过编程模型必须被支持。Seam编织了一个并发管理层进入到它的上下文模型。

 

Seam的会话和应用上下文是多线程的。Seam会为了并发处理允许在一个上下文中进行并发请求。事件和页面上下文是使用原始的单一线程。业务处理上下文严格地说是多线程,但是,在实践中,并发是十分少见的,在多数情况下,这种行为可以被忽视。最后,对话上下文,Seam对每一过程每一对话执行单一线程,使用序列化在同一个长期运行的对话上下文中的并发请求。

 

因为会话上下文是多线程,并且,常常包含不稳定的状态,会话范围组件由于并发访问总是被Seam保护,只要Seam拦截器对组件不失效。如果拦截器是失效,任何被请求的线程安全必须由组件自身来实现。在缺省情况下,Seam为会话范围会话beanJavaBeans序列化请求(并且侦听和中止任何发生的死锁情况)。然而,应用范围组件没有缺省行为,因为应用范围组件不会常常维持不稳定状态,并且全局级别的同步是极其昂贵的。不过,你可以在任何会话beanJavaBean组件上通过增加@Synchronized注释,强迫序列化线程模型。

 

并发模型意味着AJAX客户端能安全使用不稳定的会话和对话状态,就开发者的这部分而言,不需要任何特殊的工作了。

 

4.2. Seam 组件

 

Seam组件是POJOsPlain Old Java Objects 简单老式Java对象)。特别,它们是JavaBeans EJB 3.0 实体 beans。虽然Seam组件不要求组件是EJBs,并且,甚至没有EJB 3.0兼容容器也能被使用,在思想上,Seam是为EJB 3.0设计的,并包含深度集成的EJB 3.0Seam支持下列组件类型。

* EJB 3.0 stateless session beans——EJB 3.0无状态会话beans

* EJB 3.0 stateful session beans——EJB 3.0有状态会话beans

* EJB 3.0 entity beans——EJB 3.0实体beans

* JavaBeans

* EJB 3.0 message-driven beans——EJB 3.0信息驱动beans

 

4.2.1. EJB 3.0无状态会话beans

 

无状态会话bean组件是横过多重调用却不能维持状态的。因而,它们常常运行在各种Seam上下文中的其它组件的状态之上而工作。它们可以被用来作为JSF的侦听器,但是,不能提供属性给JSF组件用于显示。

 

无状态会话beans总是生活在无状态上下文中。

 

无状态会话beans能被并发访问,因为一个新实例被用于每个请求。指派实例给请求是EJB3容器的责任(一般情况,实例会从一个可重用池分派,意味着你可以查找到任何包含前面bean使用过数据的实例变量)。

 

无状态会话beans是最不引人注意的Seam组件类型。

 

Seam无状态会话bean组件可以用Component.getInstance() @In(create=true)来实例化。它们不应通过JNDI查找或new操作符来直接实例化。

 

4.2.2. EJB 3.0有状态会话beans

 

有状态会话bean组件不仅横过bean的多重调用,而且横过多重请求能维持状态。应用程序状态不属于数据库,通常被有状态会话bean维持。这是Seam和大多数其它框架的主要区别。代替直接在HttpSession中的关于当前对话的 “有粘性的”信息,你应该维持它在一个被捆绑到对话上下文的有状态bean的实例变量中。这允许Seam为你管理状态的生命周期,并且,确保在关联到不同并发对话中的状态之间没有冲突。

 

有状态会话beans常被用来作为JSF动作侦听器,并且作为返回bean提供属性给JSF组件显示或表单提交。

 

缺省下,有状态会话beans被捆绑到对话上下文。它们不可以捆绑到页面或无状态上下文。

 

只要Seam拦截器对bean而言是效的,并发请求会话范围的有状态会话bean总是被Seam序列化。

 

Seam有状态会话bean组件可以用Component.getInstance()@In(create=true)实例化。它们不应通过JNDI查找或new操作符来直接实例化。

 

 

4.2.3. EJB 3.0实体beans

 

作为一个seam组件,实体beans可以被捆绑到一个上下文变量和函数。因为,实体除了它们的上下文标识外还有一个持久化标识,实体的实例常常用Java代码明确地捆绑,胜于Seam暗中地实现。

 

实体bean组件不支持双向注入或上下文划分。也不支持实体bean的调用触发校验。

 

实体beans通常不被用来作为JSF动作侦听器,但是它常常实现返回bean的功能,提供属性给JSF组件实现显示或提交。尤其是,用一个实体作为一个返回bean和一个无状态bean动作侦听器实现创建/更新/删除类的功能是普遍的。

 

缺省时,实例beans被捆绑到对话上下文。它们绝不能被捆绑到无状态上下文。

 

注意,在一个“群集”环境下,直接捆绑一个实体bean到一个对话或会话范围的Seam上下文变量,与在一个有状态会话bean中维护一个实体bean的引用相比,几乎是低效率的。为了这个原因,并不是所有Seam应用程序会定义实体beanSeam组件。

 

Seam实体bean组件可能用Component.getInstance()@In(create=true)或者直接用new操作符来实例化。

 

4.2.4. JavaBeans

 

Javabeans可以只是像一个无状态或有状态bean一样被使用。然而,它们不提供会话bean的功能(声明事务划分,声明安全性,高效的“群集”状态复制,EJB 3.0持久化,空闲方法等等)

 

在后面的章节,我们会展示如何在不用EJB容器的情况下使用Seam Hibernate。在这个应用情形下,组件是JavaBeans代替了会话bean。然而,注意,在多应用程序服务器下,“群集”对话或会话范围的Seam JavaBean组件,与“群集”有状态会话bean组件相比,是低效的。

 

缺省时,JavaBeans被捆绑到事件上下文。

 

对会话范围JavaBeans的并发请求总是由Seam序列化。

 

Seam JavaBean组件可以用Component.getInstance()@In(create=true)实例化。它们不应通过new操作符来直接实例化。

 

4.2.5. EJB 3.0信息驱动beans

 

信息驱动beans可以象一个seam组件一样起作用。然而,信息驱动beans被其它Seam组件调用十分困难——代替调用它们是通过上下文组件变量,它们侦听发送到JMS的队列或主题的消息。

 

信息驱动beans不可以被捆绑到Seam上下文。它们也不能访问它们的“呼叫者”的会话或对话状态。然而,它们支持双向注入和一些其它Seam的功能。

 

信息驱动beans不能被应用程序实例化。当接收一个消息时,它们被EJB容器实例化。

 

4.2.6. 拦截器

 

为了实现它的魔法(双向注入,上下文声明,校验等等),Seam必须拦截组件的调用。对JavaBeansSeam是在处在对组件的实例的全控制状态下,并且,不需要特别的配置。对实体,因为双向注入和上下文声明没有定义,拦截没有被要求。对会话bean,为会话bean组件我们必须注册一个EJB拦截器。我们能用一个注释,象下面:

 

@Stateless

@Interceptors(SeamInterceptor.class)

public class LoginAction implements Login {

    ...

}

 

但有一个更好的办法是在ejb-jar.xml中定义拦截器。

<interceptors>

   <interceptor>

      <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

   </interceptor>

</interceptors>

  

<assembly-descriptor>

   <interceptor-binding>

      <ejb-name>*</ejb-name>

      <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

   </interceptor-binding>

</assembly-descriptor>

 

4.2.7. 组件命名

 

所有seam的组件需要一个名字。我们能用@Name注释指派一个名字给一个组件:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    ...

}

这个名字是seam组件名,并与EJB规范定义的任何其它名字没有关系。然而,seam组件名工作就如JSF管理bean名,并且你能把这个两概念看作一样的。

 

@Name并不是定义组件名字的唯一方法,但我们总是需要在某处指定名字。如果我们没有,那么没有一个其它Seam组件会起作用。

 

正如JSF,一个seam组件实例常被捆绑到一个与组件名的同名的上下文变量。所以,对例子,我们会使用Contexts.getStatelessContext().get("loginAction")来访问LoginAction。特别是,只要Seam自身实例化一个组件,它就会捆绑这个新实例到一个与和组件名一致的变量。然而,又象JSF,应用程序通过可编程API调用捆绑一个组件到一些其它上下文变量是可能的。如果一个特殊组件在系统中服务的角色超过了一个,这是唯一有用的。例如,当前操作的User可以被捆绑到currentUser 会话上下文变量,同时,一个具有管理功能的用户可以被捆绑到user 对话上下文变量。

 

对十分大型的应用程序,和对内建seam组件,常常使用有条件组件名字。

@Name("com.jboss.myapp.loginAction")

@Stateless

public class LoginAction implements Login {

    ...

}

 

Java代码和JSF表达式语言(EL)中,我们可以使用有条件名字:

<h:commandButton type="submit" value="Login"

                 action="#{com.jboss.myapp.loginAction.login}"/>

 

因为这是杂乱的,Seam也提供了一个方法,别名一个条件名字成一个简单名字。象这样增加一行到components.xml文件:

<factory name="loginAction" scope="STATELESS" value="#{com.jboss.myapp.loginAction}"/>

 

所有内建的Seam组件是条件名字,但它们大部分通过在Seam jar中的components.xml

被别名成一个简单名字。

 

 4.2.8. 定义组件范围

 

我们能用@Scope注释覆盖组件的缺省范围(上下文)。当Seam实例化组件时,这能让我们定义组件实例被捆绑到的上下文。

@Name("user")

@Entity

@Scope(SESSION)

public class User {

    ...

}

org.jboss.seam.ScopeType 定义了一个可能范围的枚举。

 

4.2.9. 多角色组件

 

在系统中的一些Seam组件类能实现多个角色。例如,我们通常有一个User类,它常常被用来作为表示当前用户的一个会话范围组件,然而在用户管理屏幕它被作为一个对话范围组件使用。@Role注释让我们为一个组件定义一个另外的命名角色,用一个不同的范围——它让我们捆绑同一个组件类到不同的上下文变量。(一些Seam组件实例可以被捆绑到多个上下文变量,这仅仅让我们实现在类的级别,并且利用了自动化实例化。)

@Name("user")

@Entity

@Scope(CONVERSATION)

@Role(name="currentUser", scope=SESSION)

public class User {

    ...

}

@Roles注释让我们指定我们喜欢的很多另外的角色。

 

@Name("user")

@Entity

@Scope(CONVERSATION)

@Roles({@Role(name="currentUser", scope=SESSION),

        @Role(name="tempUser", scope=EVENT)})

public class User {

    ...

}

 

4.2.10. 内建组件

 

象许多好框架,Seam“吃它的自己的狗食”,并且大部分作为一个内建的Seam拦截器(看后面)和Seam组件集合被实现。这使得应用程序在运行期间与内建组件的交互变得容易,或者甚至通过用定制实现替换内建组件来定制Seam的基本功能。内建组件被定义在Seam命名空间org.jboss.seam.core和一样名字的Java包中。

 

内建组件可以被注入,正如任何Seam组件,然而它们也提供对话状态instance()方法:

FacesMessages.instance().add("Welcome back, #{user.name}!");

 

4.3. 双向注入

 

依赖注入或者倒置控制,到目前而言,对大部分Java开发者是一个熟悉的概念。

 

依赖注入允许一个组件获得另一个组件的一个引用,通过使用容器“注入”其它组件给一个setter方法或者实例变量。在所有我们看见的依赖注入实现情况里,当组件被构建时注入发生,并且引用在随后的组件实例存在期内不能改变。对无状态组件,这是合理的。从一个客户端视角来看,一个特殊的无状态组件的所有实例是可换的。另一方面,Seam强调有状态组件的使用。所以,传统的依赖注入不再是一个十分有用的结构。Seam引入了双向注入概念当做一个注入的普通化。与注入对比,双向注入是:

 

* 上下文的 —— 双向注入是被用来从各种不同的上下文装配有状态组件 (来自一个“宽的”上下文组件甚至可以有一个来自一个“窄的”上下文组件的引用)

 

* 双向的 —— 值是从上下文变量被注入进被调用组件的属性,并且也从组件属性返回注出到上下文,允许调用组件操纵上下文变量的值,通过简单地设置它自己的实例变量来实现。

 

* 动态的 —— 因为上下文变量的值随着时间的过去而改变,并且因为Seam组件是有状态的,双向注入在每一次一个组件被调用时发生。 

 

在本质上,双向注入让你别名一个上下文变量到一个组件实例变量,通过指定实例变量的值被注入,注出,或者两者。当然,我们使用注释能够用双向注入。

 

@In注释指定一个值将要被注入,注入到任一的一个实例变量:

 

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    @In User user;

    ...

}

 

或者注入到一个setter方法:

 

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    User user;

   

    @In

    public void setUser(User user) {

        this.user=user;

    }

   

    ...

}

缺省时,Seam会进行一个对所有上下文的优先搜索,用属性名字,或者被注入的实例变量。你可能希望明确指定上下文变量名字,用,例如,@In("currentUser")

 

如果你希望Seam创建一个组件实例,当没有组件实例捆绑到命名的上下文变量时,你应该指定@In(create=true)。如果值是可选的(它能是空值null),指定@In(required=false)

 

对一些组件,不得不在它们使用的每个地方指定@In(create=true),它是能重复的。在这种情况下,你能@AutoCreate注释组件,那么它总是会被创建,随时需要,甚至不必明确的使用create=true

 

你甚至能注入一个表达式的值:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    @In("#{user.username}") String username;

    ...

}

 

在方法完成和注出后,立刻,注入的值被“无注入”(也就是设置为null)。

(在下一章有更多的信息,关于组件生命周期和注入。)

 

@Out注释指定一个属性将要被注出,来自任一的一个实例变量:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    @Out User user;

    ...

}

 

或者来自一个getter方法:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    User user;

   

    @Out

    public User getUser() {

        return user;

    }

      ...

}

 

一个属性可以被注入并被注出:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    @In @Out User user;

    ...

}

 

或者:

@Name("loginAction")

@Stateless

public class LoginAction implements Login {

    User user;

   

    @In

    public void setUser(User user) {

        this.user=user;

    }

   

    @Out

    public User getUser() {

        return user;

    }

        ...

}

 

4.4. 生命周期方法

 

会话bean和实体bean Seam组件支持所有通常的EJB3生命周期回调(@PostConstruct, @PreDestroy,等等)。但是,关于JavaBean组件,Seam也支持这些回调的任何的使用。然而,因为这些注释不能用在J2EE环境,Seam定义了两个附加的组件生命周期回调,等价于@PostConstruct @PreDestroy

 

Seam实例化一个组件后,@Create方法被调用。组件可能中定义一个@Create方法。

 

Seam组件一定要结束上下文时,@Destroy方法被调用。组件可以只定义一个@Destroy方法。

 

另外,有状态会话bean组件必然定义一个不带参数的注释@Remove方法。当上下文结束,Seam调用这个方法。

 

最后,一个相关连的注释是@Startup,它可以被应用到任何应用或会话范围组件。@Startup注释,当上下文开始时,告诉Seam组件马上实例化组件,代替在被一个客户端第一次引用它之前的等待。

 

4.5. 有条件的安装

 

@Install注释让你控制组件的有条件的安装,在有些部署环节和不在有些环节被要求的组件。这是有用的,如果:

*你想在测试中模拟出一此基础的组件。

*你想在某些部署环节改变组件的实现。

*你想只安装有些组件,如果它们的从属是可用的(对框架作者有用)。

 

@Install通过让你指定优先和从属进行工作。一个组件的优先是一个数字,当在类路径中有同样的组件名的多个类时,Seam用它来决定安装那个组件。Seam 会选择有高优先的组件。

有一些预先确定的优先值(用升序方法):

 

1. BUILT_IN — 最低的优先组件是Seam内建组件。

2. FRAMEWORK  第三方框架定义的组件可以覆盖内建组件, 但是被应用组件覆盖。

3. APPLICATION — 缺省优先。 对大部分应用组件这是适当的。

4. DEPLOYMENT — 对部署指定的应用组件。

5. MOCK — 对测试使用的模拟对象。

Suppose we have a component named messageSender that talks to a JMS queue.

假设我们有一个组件名为messageSender,其与JMS队列会谈。

@Name("messageSender")

public class MessageSender {

    public void sendMessage() {

        //do something with JMS

    }

}

 

在我们的单元测试,我们没有一个可用JMS队列,所以我们希望“模拟”这个方法。当单元测试运行时,我们在类路径中创建一个模拟组件,但是决不会与应用程序一起部署。

@Name("messageSender")

@Install(precedence=MOCK)

public class MockMessageSender extends MessageSender {

    public void sendMessage() {

        //do nothing!

    }

}

 

这个优先帮助Seam决定使用那个版本,当它发现在类路径中的两个组件时。如果我们能精确控制在类路径中的类,这是有益的。但是,如果我正在写一个带有多个从属的可重用框架,我想不分开框架在多个jar包中。我想能决定那个与其依赖的组件被安装,并且依赖的类在类路径中是可用的。@Install注释也控制这种功能。Seam使用这种内部机制,能条件安装多数内部组件。然而,你或许在你的应用程序不需要使用它们。

 

4.6. 日志

 

谁完全不讨厌看见像这样的杂乱的代码?

 

private static final Log log = LogFactory.getLog(CreateOrderAction.class);

 

public Order createOrder(User user, Product product, int quantity) {

    if ( log.isDebugEnabled() ) {

        log.debug("Creating new order for user: " + user.username() +

            " product: " + product.name()

            + " quantity: " + quantity);

    }

    return new Order(user, product, quantity);

}

 

猜想一个简单的日志消息代码可能会是多么冗长是因难的。与实际的业务逻辑相比,有太多代码行绑定在日志。我十分惊讶,Java 社区在十年中没有提出任何更好的。Seam提供了一个日志API,明显地简化了这种代码:

 

@Logger private Log log;

       

public Order createOrder(User user, Product product, int quantity) {

    log.debug("Creating new order for user: #0 product: #1 quantity: #2", user.username(),

 product.name(), quantity);

    return new Order(user, product, quantity);

}

如果声明了一个log变量是static或者没有声明,是没有关系的——两种方式它都会工作,除了实体bean组件要求log变量是static之外。

 

注意,我们不需要杂乱的if ( log.isDebugEnabled() )防护装置,因为字符串串联发生在debug()方法内。注意我们通常也不需要明确指定log种类,因为Seam知道什么组件是注入到log内。

 

如果User Product是在当前的上下文中的可用的Seam组件,它甚至会变得更好:

@Logger private Log log;

       

public Order createOrder(User user, Product product, int quantity) {

    log.debug("Creating new order for user: #{user.username} product: #{product.name} quantity: #0", quantity);

    return new Order(user, product, quantity);

}

 

Seam日志自动选择是否发送到log4j 或者JDK日志。如果log4j 是在类路径内,Seam会使用它。如果没有,Seam会使用JDK日志。

 

4.7. 易变接口和@ReadOnly

 

多数应用程序服务器包含一个令人惊异地“突破了”HttpSession群集的实现作为特色,当应用程序明确地调用setAttribute()时,改变捆绑到会话的易变对象的状态仅仅是被复制。

 

在开发期间,这是一个不能被有效测试的程序出错的源地,因为,当故障切换发生时它们仅会出现。此外,实际复制的消息,包含整个序列化对象图表,捆绑到会话属性,那是低效的。

 

当然,EJB有状态会话bean必须执行自动“弄脏”检查,多变状态复制,一个经典的EJB容器能引入象属性级复制这样的优化。不幸地,不是所有的Seam用户有好运工作在一个支持EJB3的环境中。所以,对会话和对话范围的JavaBean和实体bean组件,Seam提供了一个额外的层,群集安全状态管理,在网页容器会话群集的顶上。

 

对会话和对话范围的JavaBean和实体bean组件,一旦在应用程序调用组件的每一次请求,Seam自动通过调用setAttribute()强迫复制发生。当然,这种策略对大部分只读组件是低效的。你能控制这种行为,通过实现org.jboss.seam.core.Mutable接口,或者通过扩充的org.jboss.seam.core.AbstractMutable,并且在组件内部写你自己的“弄脏”检查逻辑性。例如,

 

@Name("account")

public class Account extends AbstractMutable

{

    private BigDecimal balance;

   

    public void setBalance(BigDecimal balance)

    {

        setDirty(this.balance, balance);

        this.balance = balance;

    }

   

    </

分享到:
评论

相关推荐

    NET组件程序设计 第2版中文版.part3.rar )

    第4章:生命周期管理 托管堆 传统内存释放模式 .NET垃圾回收 对象终结 确定性终结 第5章:版本控制 程序集版本号 程序集部署模型 程序集强名称 Visual Studio 2005和版本划分 自定义版本策略 CLR版本划分 第6章:...

    COM技术内幕——微软组件对象模型--随书源代码

    第4章 引用计数 48 4.1 生命期控制 48 4.2 引用计数简介 49 4.2.1 引用计数接口 52 4.2.2 AddRef和Release的实现 53 4.3 何时进行引用计数 60 4.3.1 引用计数的优化 60 4.3.2 引用计数规则 63 4.4 本章小结 ...

    visual c++完全自学手册(随书源码)

    第4章 GDI函数 第5章 对话框 第6章 控件 第7章 Windows的基本输入 第8章 ActiveX控件 第9章 菜单和框架窗口 第10章 工具栏和状态栏 第11章 文档和视图的分离 第12章 文档的读和写 第13章 切分窗口和多视图 第14章 ...

    visual c++6.0技术内幕 带有NLC的文件查看器

     第21章 上下文相关帮助  第22章 动态链接库  第23章 不使用文档或视图类的MFC程序设计 第四部分 ACTIVE:COM,AUTOMATION和OLE  第24章 组件对象模型  第25章 自动化  第26章 统一数据传输:剪贴板传输和OLE...

    领域驱动—限界上下文

    - 分割限界上下文要考虑技术需求,如高并发场景下独立处理订单业务,功能重用时的账户管理,实时性要求下的价格管理,以及第三方服务集成如支付服务,以及与遗留系统的集成。 通过理解并有效应用限界上下文,...

    visual c++ 完全自学手册 古万荣 源代码

    菜单和框架窗口 第10章 工具栏和状态栏 第11章 文档和视图的分离 第12章 文档的读和写 第13章 切分窗口和多视图 第14章 打印功能 第15章 上下文相关帮助 第16章 动态链接库(DLL) 第17章 MFC类家族第四篇 ...

    javaEE(上下文_监听器_过滤器) ppt

    Java EE(Java Platform, Enterprise Edition)是用于构建企业级分布式应用程序的框架,它提供了一系列标准接口和组件模型,包括上下文、监听器和过滤器。这些概念是Java EE开发中的核心组成部分,对于理解和优化Web...

    组件编写者指南 Component Writers Guide

    多态性使得组件能适应多种不同的上下文,提高了代码的灵活性和可复用性。 4. **设计时支持**:Delphi IDE提供了丰富的设计时特性,如属性编辑器、事件编辑器等,让开发者能在运行前配置组件。学习如何编写Design-...

    CSW中文分词组件

    中文分词的难度在于汉语的灵活性和多义性,同一个词语在不同的上下文中可能有不同的含义,因此,CSW组件采用了先进的算法和技术来应对这一挑战。其核心功能包括但不限于: 1. **基于词典的分词**:CSW组件通常会...

    15 把设计模式和模型联系起来 237-246.rar

    设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的模板。这些模式在不同的项目中被反复使用,以提高代码的可读性、可维护性和可复用性。模型,另一方面,通常指的是对现实世界或系统行为的抽象...

    精通Linux设备驱动程序开发-第4章打下基础.pdf

    ### 精通Linux设备驱动程序开发:第四章——打下基础 #### 设备和驱动的概念 设备驱动程序是操作系统与硬件之间的桥梁,允许软件通过标准化接口与硬件进行交互,而无需关心硬件的具体细节。在Linux系统中,设备...

    c4模型指导书 - 可视化

    C4模型强调了软件系统的不同抽象级别,并且通过缩放级别(上下文(context)、容器(container)、组件(component))来可视化软件系统的架构。该模型由Simon Brown创造,并在《Visualising Software Architecture with ...

    FS-LDM 金融数据模型

    这可能意味着文档在转换为可读形式时遇到了技术障碍,用户在阅读时需留意并根据上下文自行纠正可能的错误。 总结以上信息,FS-LDM金融数据模型是一个专为金融机构和保险公司设计的数据仓库模型,它体现了Teradata...

    nltk完整模型数据包

    3. **taggers**:这部分包含词性标注模型,如Treebank标注器,它可以根据上下文对单词进行词汇和语法分类。词性标注是NLP中的基础步骤,对于后续的词汇分析和理解至关重要。 4. **chunkers**:Chunking是将连续的词...

    aswing组件知识点

    从上下文来看,“第19点”可能是与`columClass`相关的更详细配置或使用说明。这里我们可以推测,“第19点”可能是指如何进一步配置`columClass`,例如通过指定特定的类来实现更精细的数据处理。 #### 20. ...

    【清华】 软件工程(完) 视频.txt

    第4章需求工程 4.1功能需求和非功能需求 4.1.1功能需求 4.1.2非功能需求 4.2软件需求文档 4.3需求描述 4.3.1自然语言描述 4.3.2结构化描述 4.4需求工程过程 4.5需求导出和分析 4.5.1需求发现 4.5.2采访 4.5.3脚本 ...

    第03章 初识Java EE展示层组件JSP2.0

    5. **内置对象增强**:如`pageContext`对象,提供了更多的上下文信息和操作,便于处理请求和响应。 **JSP生命周期** 1. **翻译阶段**:服务器将JSP转换为Servlet源码。 2. **编译阶段**:编译源码为字节码,生成...

    seam_in_action

    - **第4章:组件和上下文**:讲解 Seam 中组件和上下文的基本概念,以及它们如何协作来实现业务逻辑。 - **第5章:Seam组件描述符**:介绍如何使用 Seam 组件描述符来定义组件的行为和配置。 - **第6章:绝对控制...

    sevelet技术 第四章

    ### Servlet技术第四章知识点概述 #### 一、Servlet概述与核心功能 - **Servlet定义**:Servlet是一种服务器端的小程序,用于处理客户端的HTTP请求,并返回相应的响应。它由Java编写,运行在支持Servlet的容器(如...

    精通Java EE:精通Java EE 整合应用案例\源代码第三章

    4. **EJB(Enterprise JavaBeans)**:EJB是Java EE中用于构建可部署的、组件化的、面向服务的企业级应用的组件模型。有三种类型的EJB:会话bean、实体bean和消息驱动bean,分别用于业务逻辑处理、持久化数据和异步...

Global site tag (gtag.js) - Google Analytics