EJB3学习总结(1)
现状
EJB2得到了较广泛的应用,但真正用对场合的项目不多,那些强调分布式,即业务逻辑和Web是在分布在不同物理层的大型项目。更多得是使用在中小型web应用之上。
EJB3在Spring、Hibernate等一系列轻量级框架运动的发展后现身了,EJB3是基于POJO组件的,同时提供了事务、安全、ORM和分布式等诸多特点,同时AOP、DI及Annotation等特性也进一步提高了EJB3的易用性。
EJB3规范
EJB3规范包含3个技术文档:
1. EJB3 Simplified API
2. EJB3 Core Contracts & Requirement
3. Java Persistence API
EJB3组件模型
1. Session Bean:执行业务服务、控制事务及资源访问
2. Message Driven Bean:异步调用,通过JMS关联消息队列(Queue)及Topic来响应外部事件
3. Entity:具有唯一标示的实体,是持久化的基础。
SessionBean和MDBean统称为Enterprise Bean,这点不同于EJB2规范。EntityBean已经划分由持久化Provider去管理和控制,不再由EJB容器管理。
EJB3框架
它提供了对EJB3组件的各种支持,包括容器事务、安全服务、资源池的管理(包括线程池、连接池、实例池)以及组件生命周期的管理、并发支持等。
EJB3的核心features
1. 声明式的元数据:通过Java5的Annotation或XML来声明式地去指定Enterprise Bean和Entity Bean的行为和特性。如果同时使用两种方式时,XML描述具有更高的优先级。
2. 按异常配置:对于大量可使用default配置的地方都可以省去繁琐的配置,只有需要不按默认行为的才需要显式得通过注解或XML来进行描述。强调用户只有需要配置时才进行配置,可以使代码更为简洁。
3. 良好的可伸缩性:EJB3的实现中在三个方面保证了良好的伸缩性,(1)通过资源池最大程度上对重新对象的重用;(2)使用持久化及缓存避免重复查询和重复创建实体;(3)优化的锁定策略,避免对DB的并发锁定。
4. JTA(Java Transaction API)定义了分布式事务的标准API。EJB容器作为JTA的事务管理器。
5. 通过声明的方式来控制方法级别的访问控制,达到多层安全性。
6. 实体Bean被替换成POJO,简单、轻量,不用再去实现专门的接口,同时可以脱离EJB容器。
7. SessionBean也更加灵活,不再需要主接口(Home Interface).
8. 依赖注入(dependency injection),可以通过Annotation或XML的方式将依赖数据“推(push)”到bean。例如:将EntityManager注入到SessionBean中,以使会话可以与持久化单元进行交互。
9. 拦截器和回调(Call-back):通过拦截器来完成某些回调方法。
10. 对于SessionBean和MDB,不在需要主方法(ejbCreate()),使用默认构造器来替代。同时也不需要再扩展专有接口。
11. 对于EntityBean,主接口(Home interface)也被替换成EntityManager,后者是一个单例实例工厂,可以管理实体Bean的生命周期。
12. EJB3的分布式计算模型:EJB3也基于RMI远程服务,远程接口方法按值传递以提供粗粒度的模型。
EJB3角色
1. 定义Enterprise Bean及相关meta-data的三种角色:
(1) 企业Bean提供者(Enterprise Bean provider),负责去定义和实现业务逻辑和结构;负责定义实体的持久化结构及互相关系。
(2) 应用装配者(Application assembler)。
EJB3的会话Bean
EJB3中,SessionBean包括两种类型,Stateful SessionBean和Stateless SessionBean。
顾名思义,Stateless SessionBean不需维持客户请求的会话状态;而Stateful SessionBean则需要维持特定客户请求的会话状态,同时bean实例也是用客户请求绑定的。
Stateless SessionBean
无状态会话Bean由两个元素组成:业务接口,用来定义所提供的服务;bean类,是对服务接口的实现。注意此处,不需像EJB2.x中分别实现EJBObject和SessionBean接口。
通过示例,通过定义本地接口和(或)远程接口来定义业务接口,这里Local接口和Remote接口的选择遵循一个原则:如果业务的请求者与SessionBean处在同一个JVM中,则可以使用本地接口,反之则必须使用远程接口。
原则上,如果同时使用了本地接口和远程接口,则必须保证二者定义的接口一致,同时由实现的Bean实现这些方法。
无状态会话Bean无需实现EJB特定的接口或扩展类,只需在类级别使用注解——@Stateless即可。同时也在本地接口及远程接口的类级别添加注解——@Local和@Remote。
通过前面对EJB3特性的介绍,可以知道EJB3对DI(Dependency Injection)的良好支持!在EJB3中,可以将各类资源注入到会话Bean中,这些资源可以是其他的会话Bean、数据源或者是JMS中的队列(Queue)等。要实现依赖注入可以通过添加注解,也可以在XML配置文件中进行描述,但需注意的是,如果二者都进行的配置则以XML文件中的描述为准。
以注解的方式为例,只需添加@Resource注解即可,注入可以通过两种方式:实例变量和setter方法上。
回调(Call-back),通过回调,可以对Bean在其生命周期内各个阶段进行更细粒度的控制和管理。使用回调方法也很简单,回调方法没有多余的限制,只需添加正确的注解即可。
无状态会话Bean两个主要的用于回调方法的注解分别是:@PostConstruct和@PreDestroy。其中@PostConstruct的方法会在该bean被实例化后回调执行,但需要注意的是,如果该bean有配置了需要注入的资源,那该回调方法则会紧跟着资源的注入之后而执行。
@PreDestroy的回调方法则是在容器即将销毁bean实例之前被调用,主要用来做一些善后的工作,比如对资源的关闭和清理。(补充,在有状态会话Bean中,该方法是在最后一个带有@Remove注解的方法调用后才被调用,之后容器销毁bean实例。)
另一个关键的元素是拦截器(Interceptor),拦截器的使用也很便捷,通过添加正确的注解即可。拦截器的概念与其他JavaEE的框架或规范中的一致,即拦截业务方法的调用,可以在拦截点附加新的业务逻辑,结合依赖注入特性,可以充分得做到关注点分离(Separation of Concerns)。Enterprise Bean中会话Bean和消息驱动Bean可以定义拦截器方法。
拦截器注解可以添加到方法级别,也可以添加到类级别。被标注的方法在被调用时会被拦截器类拦截,并插队式的先去调用拦截器的方法。对于用到的拦截器类需要添加@Interceptor注解,如果有多个拦截器则使用@Interceptors。
以@AroundInvoke注解为例,拦截器方法需要关注InvocationContext接口,通过它可以获得被拦截的bean类(Class)、bean中的方法(Method)等,需要强调的是其中的proceed()方法,通过它将拦截请求往后传递,或者到拦截器链中的下一个,或者是结束拦截调用真正的bean方法。
EJB3规范中定义了两种类型的异常,分别是应用异常和系统异常。应用异常是业务逻辑中产生的checked exception;而系统异常则是EJB系统级产生的异常,同时系统异常都是RemoteException和RuntimeException的子类,是unchecked exception。
有状态会话Bean(Stateful SessionBean)在特性及细节上与无状态会话Bean很相似。
会话Bean的用户视图
访问会话Bean的用户视图可以有三种形式:
1. 通过Remote接口,远程客户具有位置无关性。
2. 通过Local接口,这两种方式中请求方可以是其他的EJB组件,可以是Servlet、JSP等。需要注意的是,本地客户具有位置依赖性。
3. WebService方式,可以将会话Bean发布成为一个WebService,供客户调用。
客户请求会话Bean时,或者通过依赖注入或者通过查找JNDI,来获得会话Bean的stub对象,请求是通过stub来进行调用的。对于无状态会话Bean,每次请求将获得新的stub,而有状态会话Bean,则在请求方缓存stub,这样才能使容器知道该返回哪个与客户相关联的bean实例。
通过依赖注入获得会话Bean业务接口的方式是添加注解@EJB,注意要与@Resource区分开。
相比查找JNDI,使用注入的方式会更加简洁,通常对于远程请求使用JNDI更适合。
有状态会话Bean(Stateful SessionBean)
通过实现SessionSynchronization接口,可以在事务点上获得EJB容器的通知:afterBegin,在新事物开始时;beforeCompletion,在事物提交前;afterCompletion,在事物执行完之后。
有状态会话Bean中的回调方法除了PostConstruct和PreDestroy外,还有PreActivate和PrePassivate,分别使用@PreActivate和@PrePassivate来注解。
前两个回调方法的细节与无状态会话Bean一致,分别在(1)实例化Bean之后并执行完资源注入后执行;(2)@Remove方法执行完毕之后。
对于有状态会话Bean中的@Remove方法,也是一个管理bean生命周期的方法,调用该方法后,容器将会从实例池中将该bean删除。
带有@PrePassivate注解的方法会由EJB容器调用,当某个有状态会话Bean实例长时间空闲,则容器调用该方法将此bean实例钝化,并将状态缓存起来。
当客户请求再次需要使用被钝化的某bean实例时,容器调用该bean的@PreActivate方法,返回一个创建好的并带有状态的新实例。
有状态会话Bean的拦截器方法需要注意的是,如果实现SessionSynchronization接口的bean,afterBegin始终发生在@AroundInvoke的任何方法前。