- 浏览: 81751 次
- 性别:
- 来自: 重庆
-
最新评论
-
禀圣含华:
怎么弄空格呢?___________然后我固定这些空格,怎么弄 ...
第18章. iText PDF 生成 -
super0555:
managed
第13章 Seam 应用程序框架 -
Javakeith:
代码呢?共享下代码呗!
第18章. iText PDF 生成 -
tonyshen277:
同感啊。还在做Win32下的开发,楼主所说的一个都不能少啊。
转自baidu wqqq 给软件开发初学者 -
kdboy:
的确,把人放在宇宙中去看,从上帝的眼中去看,人的确是非常渺小, ...
人生是什么
第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请求生命周期的事件上下文是最重要的一个事件上下文例子,并且是你常见的一个。关联到事件上下文的组件在请求结束时被销毁,但是对请求的最小生命周期,它们的状态是可用的和明确的。
当你通过RMI(Remote 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为会话范围会话bean和JavaBeans序列化请求(并且侦听和中止任何发生的死锁情况)。然而,应用范围组件没有缺省行为,因为应用范围组件不会常常维持不稳定状态,并且全局级别的同步是极其昂贵的。不过,你可以在任何会话bean或JavaBean组件上通过增加@Synchronized注释,强迫序列化线程模型。
并发模型意味着AJAX客户端能安全使用不稳定的会话和对话状态,就开发者的这部分而言,不需要任何特殊的工作了。
4.2. Seam 组件
Seam组件是POJOs(Plain Old Java Objects 简单老式Java对象)。特别,它们是JavaBeans 或EJB 3.0 实体 beans。虽然Seam组件不要求组件是EJBs,并且,甚至没有EJB 3.0兼容容器也能被使用,在思想上,Seam是为EJB 3.0设计的,并包含深度集成的EJB 3.0。Seam支持下列组件类型。
* 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应用程序会定义实体bean是Seam组件。
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必须拦截组件的调用。对JavaBeans,Seam是在处在对组件的实例的全控制状态下,并且,不需要特别的配置。对实体,因为双向注入和上下文声明没有定义,拦截没有被要求。对会话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;
}
</
发表评论
-
第31章. Seam内建组件
2009-05-28 11:51 1499第31章. Seam内建组件 本章描述Seam内建组件 ... -
第30章. Seam注释
2009-05-26 20:21 1636第30章. Seam注释 在你编写一个Seam应用程 ... -
第31章. Seam内建组件
2009-05-26 20:21 1468第31章. Seam内建组件 本章描述Seam内建组件 ... -
第29章. 配置Seam和打包Seam应用程序
2009-05-26 20:19 1582第29章. 配置Seam和打包Seam应用程序 配置 ... -
第28章. Hibernate搜索
2009-05-26 20:17 153928.1. 介绍 如Apache Lucen ... -
第27章. Spring框架集成
2009-05-26 20:16 1446第27章. Spring框架集成 Spring集成 ... -
第26章. Seam和Google网页工具
2009-05-26 20:15 1244第26章. Seam和Google网页工具 ... -
第25章. 远程
2009-05-26 20:15 1327第25章. 远程 Seam 提供了一个从网页远程访 ... -
第24章. Web服务
2009-05-26 20:15 1715第24章. Web服务 Seam 集成了JBossWS, ... -
第23章. 缓存
2009-05-26 20:14 1465第23章. 缓存 在 ... -
第22章. 异步和消息
2009-05-26 20:12 2170第22章. 异步和消息 Seam 使异步执行来自网页请 ... -
第21章. Email
2009-05-26 20:11 1372现在Seam包含一个用于模板和发送邮件的可选组件。 Emai ... -
第 20章. RSS支持
2009-05-26 20:10 835第 20章. RSS支持 20.1. 安装 20.2. ... -
第19章. 微软的Excel 电子表格应用程序支持
2009-05-26 20:07 1854第19章. 微软的Excel 电子表格应用程序支持 S ... -
第18章. iText PDF 生成
2009-05-26 20:01 6530第18章. iText PDF 生成 18.1. 使用PDF ... -
第17章. Seam文本
2009-05-26 19:59 1040第17章. Seam文本 面向协作站点需要一种更有友好的 ... -
第16章. 国际化、本地化和主题
2009-05-26 19:57 1437第16章. 国际化、本地化和主题 Seam使构建国际化应用程 ... -
第15章 安全(2)
2009-05-26 19:56 942第15章 安全(2) 15.5. 错误消息 安全 ... -
第15章 安全(1)
2009-05-26 19:53 102415.1. 概述 Seam安全API为你基于Seam的 ... -
第14章 Seam 和 JBoss 规则
2009-05-26 19:50 1460第14章 Seam 和 JBoss 规则 Seam使从 ...
相关推荐
第4章:生命周期管理 托管堆 传统内存释放模式 .NET垃圾回收 对象终结 确定性终结 第5章:版本控制 程序集版本号 程序集部署模型 程序集强名称 Visual Studio 2005和版本划分 自定义版本策略 CLR版本划分 第6章:...
第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 本章小结 ...
第4章 GDI函数 第5章 对话框 第6章 控件 第7章 Windows的基本输入 第8章 ActiveX控件 第9章 菜单和框架窗口 第10章 工具栏和状态栏 第11章 文档和视图的分离 第12章 文档的读和写 第13章 切分窗口和多视图 第14章 ...
第21章 上下文相关帮助 第22章 动态链接库 第23章 不使用文档或视图类的MFC程序设计 第四部分 ACTIVE:COM,AUTOMATION和OLE 第24章 组件对象模型 第25章 自动化 第26章 统一数据传输:剪贴板传输和OLE...
- 分割限界上下文要考虑技术需求,如高并发场景下独立处理订单业务,功能重用时的账户管理,实时性要求下的价格管理,以及第三方服务集成如支付服务,以及与遗留系统的集成。 通过理解并有效应用限界上下文,...
菜单和框架窗口 第10章 工具栏和状态栏 第11章 文档和视图的分离 第12章 文档的读和写 第13章 切分窗口和多视图 第14章 打印功能 第15章 上下文相关帮助 第16章 动态链接库(DLL) 第17章 MFC类家族第四篇 ...
Java EE(Java Platform, Enterprise Edition)是用于构建企业级分布式应用程序的框架,它提供了一系列标准接口和组件模型,包括上下文、监听器和过滤器。这些概念是Java EE开发中的核心组成部分,对于理解和优化Web...
多态性使得组件能适应多种不同的上下文,提高了代码的灵活性和可复用性。 4. **设计时支持**:Delphi IDE提供了丰富的设计时特性,如属性编辑器、事件编辑器等,让开发者能在运行前配置组件。学习如何编写Design-...
中文分词的难度在于汉语的灵活性和多义性,同一个词语在不同的上下文中可能有不同的含义,因此,CSW组件采用了先进的算法和技术来应对这一挑战。其核心功能包括但不限于: 1. **基于词典的分词**:CSW组件通常会...
设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的模板。这些模式在不同的项目中被反复使用,以提高代码的可读性、可维护性和可复用性。模型,另一方面,通常指的是对现实世界或系统行为的抽象...
### 精通Linux设备驱动程序开发:第四章——打下基础 #### 设备和驱动的概念 设备驱动程序是操作系统与硬件之间的桥梁,允许软件通过标准化接口与硬件进行交互,而无需关心硬件的具体细节。在Linux系统中,设备...
C4模型强调了软件系统的不同抽象级别,并且通过缩放级别(上下文(context)、容器(container)、组件(component))来可视化软件系统的架构。该模型由Simon Brown创造,并在《Visualising Software Architecture with ...
这可能意味着文档在转换为可读形式时遇到了技术障碍,用户在阅读时需留意并根据上下文自行纠正可能的错误。 总结以上信息,FS-LDM金融数据模型是一个专为金融机构和保险公司设计的数据仓库模型,它体现了Teradata...
3. **taggers**:这部分包含词性标注模型,如Treebank标注器,它可以根据上下文对单词进行词汇和语法分类。词性标注是NLP中的基础步骤,对于后续的词汇分析和理解至关重要。 4. **chunkers**:Chunking是将连续的词...
从上下文来看,“第19点”可能是与`columClass`相关的更详细配置或使用说明。这里我们可以推测,“第19点”可能是指如何进一步配置`columClass`,例如通过指定特定的类来实现更精细的数据处理。 #### 20. ...
第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脚本 ...
5. **内置对象增强**:如`pageContext`对象,提供了更多的上下文信息和操作,便于处理请求和响应。 **JSP生命周期** 1. **翻译阶段**:服务器将JSP转换为Servlet源码。 2. **编译阶段**:编译源码为字节码,生成...
- **第4章:组件和上下文**:讲解 Seam 中组件和上下文的基本概念,以及它们如何协作来实现业务逻辑。 - **第5章:Seam组件描述符**:介绍如何使用 Seam 组件描述符来定义组件的行为和配置。 - **第6章:绝对控制...
### Servlet技术第四章知识点概述 #### 一、Servlet概述与核心功能 - **Servlet定义**:Servlet是一种服务器端的小程序,用于处理客户端的HTTP请求,并返回相应的响应。它由Java编写,运行在支持Servlet的容器(如...
4. **EJB(Enterprise JavaBeans)**:EJB是Java EE中用于构建可部署的、组件化的、面向服务的企业级应用的组件模型。有三种类型的EJB:会话bean、实体bean和消息驱动bean,分别用于业务逻辑处理、持久化数据和异步...