回调方法
在应用程序使用会话Bean时,对某些特定实例及用例,可能需要精确控制如对象创建、对象销毁等等事件,比如说,SearchFacade会话Bean在创建时,可能需要执行一些数据库初始化操作;而在销毁时,需要关闭一些数据库连接,在此,通过回调方法,程序就能精确控制Bean生命周期的每个阶段。回调方法可以是会话Bean中的带有callback注释的任意方法,而EJB容器将在Bean生命周期的适当阶段对其进行调用。
以下是无状态会话Bean的两种回调方法:
PostConstruct:以@PostContruct注释符表示。在Bean类中的任何方法都能标以此注释符。
PreDestroy:以@PreDestroy注释符表示。与上面一样,Bean类中的任何方法都能标以此注释符。
PostContruct回调发生在一个Bean实例在EJB容器中实例化之后,如果Bean使用了依赖性注入以获取引用资源或环境中的其他对象,PostContruct将会发生在注入完成之后及Bean类中的第一个业务方法被调用之后。
在SearchFacade会话Bean这个例子中,我们可以有一个业务方法wineSearchByCountry(),其返回特定国家的酒名列表,且有一个PostConstruct回调方法,initializeCountryWineList()将会在Bean实例化时初始化国家的酒名列表。在实际应用中,一般会从后台数据库加载这个列表,但在本文中,仅以硬编码的方式将它们压入一个HashMap中,如例6所示:
例6:PostConstruct方法 @PostConstruct public void initializeCountryWineList() { //countryMap为HashMap countryMap.put("Australia", "Sauvignon Blanc");//澳大利亚——勃朗克葡萄酒 countryMap.put("Australia", "Grenache"); countryMap.put("France","Gewurztraminer"); countryMap.put("France","Bordeaux");//法国——波尔多葡萄酒 }
而PreDestroy回调则发生在容器销毁对象池中一个不再使用或到期的Bean实例之前,这个回调方法能用于关闭为依赖性注入创建的任意连接池,且也能用于释放其他资源。
在SearchFacade会话Bean这个例子中,可以在SearchFacade Bean中添加一个PreDestroyt回调方法(如destroyWineList()),用于在Bean销毁时清除酒名列表。在实际应用时,可在PreDestroy中关闭为依赖性注入创建的任意资源,但在本文中,只是清除有国家名及酒名列表的HashMap。例7是destroyWineList()的代码:
例7:PreDestroy方法 @PreDestroy public void destroyWineList() { countryMap.clear(); }
定义在Bean类上的回调方法必须有修饰符public void <METHOD>();回调方法也能定义在Bean监听(listener)类上,这些方法必须有public void <METHOD>(Object)修饰符,而Object可以声明为实际Bean类型,其通常为运行时传递给回调方法的参数。
拦截器方法(Interceptors)
在EJB 3规范中提供一种名为拦截器的注释符,其可拦截一个业务方法的调用,会话Bean及消息驱动Bean(MDBs)都可被定义一个拦截器方法。
在一个典型的应用程序中,多处都可用到拦截器方法,我们可能需要在业务方法被调用之前或之后执行一个特定的任务,举例来说,可能会做以下这些事情:
在关键业务方法转账超过十万元时执行额外的安全检查。
做一些性能分析,以计算执行特定任务的时间。
在方法调用之前或之后作一些额外的日志记录。
即可为特定方法添加一个@AroundInvoke注释符,也可定义一个拦截类——其方法在Bean类中业务方法调用之前被调用;而一个拦截类通常在与之相联的Bean类上标以@Interceptor,如果有多个拦截类,就要使用@Interceptors了。被标以@AroundInvoke注释符的方法应有以下修饰符:public Object <METHOD>(InvocationContext) throws Exception,下面是InvocationContext的定义:
package javax.ejb; public interface InvocationContext { public Object getBean(); public java.lang.reflect.Method getMethod(); public Object[] getParameters(); public void setParameters(Object[] params); public EJBContext getEJBContext(); public java.util.Map getContextData(); public Object proceed() throws Exception; }
以下是代码中方法的详细说明:
getBean()返回方法调用时这个Bean的实例。
getMethod()返回被调用的Bean实例中的方法。
getParameters()返回方法调用的参数。
setParameters()修改用于方法调用的参数。
getEJBContext()使拦截方法可以访问Bean的EJBContext。
getContextData()允许数值在同一InvocationContext实例中的拦截器方法间传递。
proceed()调用下一个拦截器方法,如果只有一个拦截器方法,将调用目标Bean方法。
在SearchFacade会话Bean中,可添加一个拦截器方法,用于记录由客户端程序调用的每个业务方法执行的时间。例8为一个时间记录方法,将输出业务方法执行所需的时间;而InvocationContext用于获取Bean类名及调用方法名。在调用业务方法之前,将记录当前系统时间,以便与业务方法执行之后的系统时间相减。最后,使用System.out.println在控制台窗口中输出详细信息。
例8:拦截器方法 @AroundInvoke public Object TimerLog (InvocationContext ctx) throws Exception { String beanClassName = ctx.getClass().getName(); String businessMethodName = ctx.getMethod().getName(); String target = beanClassName + "." + businessMethodName ; long startTime = System.currentTimeMillis(); System.out.println ("正在调用:" + target); try { return ctx.proceed(); } finally { System.out.println("正在退出:" + target); long totalTime = System.currentTimeMillis() - startTime; System.out.println("Business method " + businessMethodName + "in " + beanClassName + "takes " + totalTime + "ms to execute"); System.out.println(“Bean类:”+beanClassName +”中的业务方法:”+ businessMethodName +”执行用了”+ totalTime + "毫秒"); } }