生命周期回调
在生命周期回调方面单例会话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托管的并发性比容器托管的并发性更好(这取决于开发人员的水平)。细粒度的控制并发性将产生更好的性能,而使用容器托管的并发性时,所有业务方法都将与某种锁定相关,无论是否涉及状态操作,所以性能上会有差距。简而言之如果开发阶段想节省时间那么就老老实实用容器托管,如果想细粒度维护并发性那么就自己写,如果考虑后期维护成本就用容器托管,如果担心以后系统出现并发性能问题那就自己写,……