EJB3.0规范引入了两个重要概念:依赖注入(DI:Dependency Injection)和截获器(Interceptor)。
依赖注入是从开源社区的一些著名轻量级容器(如Spring、Pico container)中所发展出来的概念,其主要思想就是由容器而不是对象本身来负责处理对象之间的依赖关系。与传统的服务定位器相比,依赖注入具有易测 试、弱侵入性等优点,这也就是为什么在最新的Java EE 5规范中引入它的原因。
对于EJB3.0来说,依赖注入就是由容器负责查找被依赖的对象并注入到依赖bean中,而bean本身不再需要进行JNDI或者context查询。此外,依赖注入发生在任何业务方法被调用之前,而且支持setter方法注入和域注入两种方式。
通过与标注结合使用,在bean类中声明依赖注入是非常简单的 (当然,也可以在部署描述符文件中声明依赖注入):
@EJB用于注入EJB业务对象
@PersistenceUnit 用于注入EntityManagerFactory
@PersistenceContext 用于注入EntityManager
@Resource 用于注入其它资源对象,如连接工厂、消息目标等
示例
@Stateless
public class ServiceBean implements Service {
private javax.sql.DataSource myDS;
@Resource(mappedName=“LocalDataSource")
public void setMyDS(javax.sql.DataSource ds) {this.myDS = ds;}
@EJB(beanName=“AccountBean")
private Account account;
}
在无状态会话bean ServiceBean中,声明了两个依赖:一个是数据源,一个是业务接口。在运行期间,EJB3.0容器一旦创建了ServiceBean的实例,就会分别通过方法注入和域注入将数据源对象和业务对象注入到ServiceBean中。
作为EJB3.0中提出的新概念,截获器是可以对bean的业务方法和生命周期事件进行拦截的组件。截获器需要由@Interceptors 或 发布描述符文件中相关的标签指定。截获器可以带有状态而且可以进行依赖注入,其生命周期与其所绑定的EJB bean实例的生命周期一致。
定义在截获器中用于拦截目的的方法被称为截获器方法,其中,针对业务方法的截获器方法通过@AroundInvoke标注或发布描述符文件中相 关的标签指定;针对生命周期回调的截获器方法通过@PostConstruct, @PreDestroy等标注或发布描述符文件中对应的标签指定。
截获器分为四类:
- 缺省截获器:可作用于ejb-jar中定义的所有EJB bean。缺省截获器只能定义在DD中,不存在相应的标注
- 类级截获器:只能作用于所指定的EJB bean
- 方法级截获器:只能作用于所指定的某个EJB bean业务方法,方法级截获器不能用于拦截bean的生命周期事件
- bean 级截获器:又被称为自我截获器,因为截获器同时就是EJB bean本身,此时相关的截获器方法只能作用于该EJB
截获器链的调用顺序
因为可以为EJB定义多个截获器,所以存在截获器链的调用顺序问题,缺省情况下,以下原则被遵循(请参考下图):
图1:截获器链的缺省调用顺序
- 调用顺序依次是缺省截获器、类级截获器、方法级截获器以及bean级截获器
- 对于同类别的截获器,按照声明的顺序调用
- 总是优先调用父类的截获器方法。
此外,EJB3.0规范还提供了更灵活的选项,详细信息请参考EJB3.0规范中“截获器”一节:
- 在发布描述符文件中设置“exclude-default-interceptors” 可以取消对缺省截获器的调用,而应用“exclude-class-interceptors”则取消对类级截获器的调用
- 为了替换缺省的截获器链调用顺序,可以设置发布描述符文件的“interceptor-order”,下图给出了一种可能的截获器链调用顺序。
图2:由interceptor-order指定的一种可能的截获器链调用顺序
示例
@Stateless(name="Trader")
@Interceptors(ClassInterceptor.class)
public class TraderBean implements Trader {
@Interceptors(MethodInterceptor.class)
public String sell(String stockSymbol) {
…
}
@AroundInvoke
public Object selfInterceptor(InvocationContext) throws Exception {
…
}
}
在无状态会话bean TraderBean中,声明了两个截获器:一个类级截获器,一个方法级截获器,此外,通过使用@AroundInvoke标注,TraderBean的 方法selfInterceptor被声明为截获器方法,所以TraderBean就是bean级截获器。