EJB3~单例会话bean

来源:互联网 发布:mac装虚拟机破解版 编辑:程序博客网 时间:2024/04/29 08:29

       在EJB3.1之前在会话bean的种类中是没有单例会话bean的,有的只是有状态会话bean以及无状态会话bean。毋庸置疑无状态会话bean以其优秀的性能被普遍使用,但是人们发现在无状态会话bean在使用过程中有两个最常值得关注的问题。按照官方一点儿的话说那就是一、bean池的感知开销。二、无法通过静态字段共享状态。其中第二点很好理解,因为无状态会话bean在每次请求的时候都需要从实例池中拿到一个实例对请求进行服务所以不可能存在像普通Java变量那样通过共享静态字段共享不同请求之间的状态。第一点所谓的感知开销是指客户端发起请求的时候bean池不知道还有多少个实例可用,bean池所做的就是将请求放在一个队列中然后等待可用的实例,然后将这个实例拿出来对请求进行服务。

单例会话bean的出现实现了单个共享的bean实例,为上面两个问题提供了解决方案,这个共享bean既可以并发访问(解决第一个关于bean池感知开销的问题),也可以作为一种共享状态的机制(共享不同会话之间的状态)。单例会话bean与无状态会话bean的生命周期回调是相同的但生命周期是完全不同的,为了能够进行同步在单例会话bean中还了复杂的开发人员控制锁(developer-controlledlocking)。

与其他会话bean不同,单例会话bean在应用程序初始化期间迅速创建单例,同时在应用程序关闭之前一直存在。可以这么认为,当应用程序启动这个单例就一直存在知道服务器停止。也就是说单例会话bean一旦创建了,它就将在容器删除它之前一直存在,无论在业务方法执行过程中是否发生任何异常。这是它与其他会话bean类型之间的一个关键差异,因为永远不会在系统异常事件中重新创建这种bean实例。

因为单例会话bean的声明周期很长,并且能够共享实例,所以常见应用程序状态的理想储存位置就落在了它的头上,无论是只读(read-only)状态还是读写(read-write)状态。为了确保访问这种状态,单例会话bean提供了许多并发选项,它们取决于应用程序开发人员的需要。考虑到性能,方法可能完全不同步,或者由容器自动地锁定和管理。

1.定义单例会话bean

和无状态和有状态会话bean的模型一样,单例会话bean使用@Singleton注解来定义。单例会话bean可以包括一个本地业务接口或者使用一个无接口视图。下面代码显示了一个简单的具有无接口视图的单例会话bean,用于跟踪Web站点的访问次数。

@Singleton  public class HitCounter {      int count;        public void increment() { ++count; }            @Lock(LockType.READ)      public int getCount() { return count; }        public void reset() { count = 0; }  }  
如果将上面程序中的HitCounterbean与前面定义的无状态与有状态会话bean进行比较,那么立刻可以看到两个差异。与无状态会话bean不同,它存在计数字段形式的状态,用于捕获访问计数(保存了会话的状态,而且在不同会话之间是共享的)。与有状态会话bean也不同,没有包含@Remove注解以标识用于完成会话的业务方法(表明单例会话bean不会被销毁)。

默认情况下,容器将管理业务方法的同步以确保不会发生数据破坏。在此例中,这意味着对于bean的所有访问进行序列化,从而保证在任何时间只有一个客户端调用该实例的业务方法。

单例会话bean的生命周期关系到整个应用程序的生命周期。容器决定何时创建单例实例的时间,(如果该bean同时包括@Startup注解,那么应用程序将会强制在启动时迅速初始化这个单例会话bean的实例)。容器当然也可以创建不被@Startup注解标记的在系统应用程序启动时初始化的singleton,但这是特定于不同的服务器供应商的,JavaEE中没有做表述。

当多个单例会话bean互相依赖时,需要告知容器对它们进行实例化的顺序。这时可以通过在bean类中的添加@DependsOn注解来实现,其中列出了其他必须提前创建的单例会话bean的名称(如果多个记得逗号隔开)。

生命周期回调

在生命周期回调方面单例会话bean与无状态会话bean在大体上是一样的,都拥有下面两个回调PostConstruct和PreDestroy。这连个回调分别是容器在服务器初始化bean实例之后以及释放bean实例之前调用。对于单例会话bean来说PostConstruct只会调用一次,而PreDestroy仅当应用程序整个关闭时才会调用,因此它也只会被调用一次,而无状态会话bean的这两个生命周期回调在创建和销毁bean实例时则需要经常被调用。

单例并发性

由于单例会话bean的特殊性(有且仅有一个实例),所以并发性是单例会话bean不得不考虑的问题。对于单例会话bean来说它可以使用容器托管(Container-managed)的并发性或bean托管(bean-managed)的并发性(concurrency)。当然默认是容器托管的,(容器大部分情况下认为使用者是“懒惰”+“白痴”)这相当于在所有业务方法之上的写锁定(write lock)这就使得所有业务方法的调用均被序列化,因此在任何时间内只有一个客户端可以访问bean。(在处理同步问题的时候各个服务器厂商有着自己的方法再次不在叙述。)

当然,对于单例会话bean来说并非所有业务方法都是为了更改bean的状态(笔者更偏向于理解成bean中的各个属性)。那些不能更改bean状态的业务方法(比如getter方法)是希望杯允许以并行方式运行的,这时@Lock(LockType.READ)注解可以用来声明这种访问是安全的,是可以并行访问的。

@Singleton  public class HitCounter {      int count;        public void increment() { ++count; }            @Lock(LockType.READ)      public int getCount() { return count; }        public void reset() { count = 0; }  }  

上面的代码显示了覆盖业务方法锁定语义HitCounter bean。这种情况下,getCount()方法标记为@Lock(LockType.READ),表明多个客户端可以安全地并发执行该方法。剩余的方法仍然是默认@Lock(LockType.WRITE),容器将确定读取和写入方法调用是互斥的。

容器是固定的,如果开发人员希望能够更加细粒度地控制并发性,那么应该把单例会话bean配置为使用bean托管的并发性,这其实很简单只需要在bean类上添加@Concurrency Management(ConcurrencyManagementtype.BEAN)注解就可以了。这时将禁用容器托管的并发性,@Lock注解就失去了作用。此时开发人员就可以决定使用合适的Java并发机制来确保数据安全了。

开发速度和细粒度控制是一对矛盾,在许多情况下,bean托管的并发性比容器托管的并发性更好(这取决于开发人员的水平)。细粒度的控制并发性将产生更好的性能,而使用容器托管的并发性时,所有业务方法都将与某种锁定相关,无论是否涉及状态操作,所以性能上会有差距。简而言之如果开发阶段想节省时间那么就老老实实用容器托管,如果想细粒度维护并发性那么就自己写,如果考虑后期维护成本就用容器托管,如果担心以后系统出现并发性能问题那就自己写,……


转自:http://blog.csdn.net/beijiguangyong/article/details/41630293
1 0
原创粉丝点击