EJB实例教程笔记(二)

EJB实例教程笔记(二)

电子书EJB3实例教程byLiHuoming.pdf笔记

2.4 Stateful Session Bean(有状态Bean)开发
需要每个用户都有自己的一个实例,这个实例不受其他用户影响。每个有状态Bean在Bean实例的生命周期内只服务于一个用户,Stateful Session Bean保持了用户的信息。

对于stateful Session Bean,用户每调用一次lookup()都将创建一个新的Bean实例,如果希望使用某个Bean实例,必须在客户端缓存存根。

定义接口Cart.java如下:
package com.sillycat.ejb;

import java.io.Serializable;
import java.util.List;

public interface Cart extends Serializable{
public void AddBuyItem(String productName);
public List<String> getBuyItem();
}
定义Bean文件CartBean.java如下:
package com.sillycat.ejb.impl;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateful;

import com.sillycat.ejb.Cart;

@Stateful
@Remote(Cart.class)
public class CartBean implements Cart{

private static final long serialVersionUID = 4371026853912745479L;

private List<String> buyitems = new ArrayList<String>();

public void AddBuyItem(String productName) {
buyitems.add(productName);
}

public List<String> getBuyItem() {
return buyitems;
}

}

单元测试CartTest.java
package com.sillycat.ejb;

import static org.junit.Assert.assertEquals;

import java.util.List;

import javax.naming.InitialContext;

import org.junit.BeforeClass;
import org.junit.Test;

public class CartTest {
protected static Cart cart;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
InitialContext ctx = new InitialContext();
cart = (Cart) ctx.lookup("CartBean/remote");
}

@Test
public void testBuyItems() {
cart.AddBuyItem("无聊啊");
cart.AddBuyItem("其实我喜欢写程序");
List<String> items = cart.getBuyItem();
assertEquals(2,items.size());
}
}

原本书中的例子,是要求,如果要使用同一个实例,存根,也就是lookup得到的cart要存放在session中,呵呵,到时候我想其他办法吧,放一个cart进session不是我想看到的。

2.5 激活机制(Activation mechanism)
每个有状态Bean在其生命周期内只服务于一个用户,对有状态Bean使用实例池化机制是毫无意义的。

2.6 Stateful Session Bean的生命周期
stateful Session Bean的生命周期包含三种状态:does not exist,method-ready和passivated。method-ready状态和method-ready pool之间有显著的不同。

2.7 EJB调用机制
调用远程或本地接口的方法时,接口使用的是存根(stub)对象,该存根实现了session bean的远程或本地接口。它负责将调用经过网络发送到远程EJB容器,或将请求路由到位于本地JVM内的EJB容器。存根是在部署期间使用JDK的java.lang.reflect.Proxy动态生成的。

2.8 如何改变Session Bean的JNDI名称
JBOSS提供@LocalBinding和@RemoteBinding,示例如下OperationBean.java:
package com.sillycat.ejb.impl;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import org.jboss.ejb3.annotation.LocalBinding;
import org.jboss.ejb3.annotation.RemoteBinding;

import com.sillycat.ejb.Operation;
import com.sillycat.ejb.OperationLocal;

@Stateless
@Remote(Operation.class)
@RemoteBinding(jndiBinding="easyejb/OperationRemote")
@Local(OperationLocal.class)
@LocalBinding(jndiBinding="easyejb/OperationLocal")
public class OperationBean implements Operation, OperationLocal {

private int total = 0;

public int Addup() {
total++;
return total;
}

}

如果使用weblogic/Sun Application Server/Glassfish等,用的是@Stateless.mappedName()。
由于JNDI名称与厂商有关,使用注释定义JNDI名称会有移植问题,所以建议使用ejb-jar.xml部署描述文件来定义。文件放置在jar的META-INF目录中。

2.9 Session Bean的生命周期事件
@PostConstruct
当Bean对象完成实例化后,标注这个注释的方法会被立即调用。(有状态和无状态)
@PreDestroy
容器销毁一个无用或者过期的bean实例之前调用。(有状态和无状态)
@PrePassivate
有状态的Bean实例空闲事件过长,就会发生钝化(passivate)。标注了这个注释的方法会在钝化之前被调用。Bean实例被钝化后,在一段时间内,如果没有用户对bean实例进行操作,容器会从硬盘中删除它。以后,针对该bean方法的调用,容器都会抛出例外。(有状态)
@PostActivate
当客户端再次使用已经钝化的有状态bean时,EJB容器会重新实例化一个Bean实例,并从硬盘中将之前的状态恢复,标注了这个注释的方法会在激活完成时被调用。(有状态)
@Init
它区别于@PostConstruct在于多个@Init注释方法可以同时存在于有状态session bean中,但每个bean实例只会有一个@Init注释的方法会被调用。@PostConstruct在@Init之后被调用。(有状态)
@Remote
当客户端调用了标注了@Remote注释的方法时,容器将在方法执行结束后,把bean实例删除。

注释的回调方法必须返回void,不带参数,且不能抛出任何checked exception。

示例接口文件LifeCycle.java:
package com.sillycat.ejb;

public interface LifeCycle {

public String say();

public void stopSession();
}

EJB类LifeCycleBean.java如下:
package com.sillycat.ejb.impl;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Init;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;

import com.sillycat.ejb.LifeCycle;

@Stateful
@Remote(LifeCycle.class)
public class LifeCycleBean implements LifeCycle{
public String say(){
try{
Thread.sleep(1000*10);
}catch(InterruptedException e){
e.printStackTrace();
}
return "有状态会话Bean生命周期";
}

@Init
public void initialize(){
System.out.println("@Init事件");
}

@PostConstruct
public void construct(){
System.out.println("@PostConstruct事件");
}

@PreDestroy
public void exit(){
System.out.println("@PreDestroy事件");
}

@PrePassivate
public void serialize(){
System.out.println("@PrePassivate事件");
}

@PostActivate
public void activate(){
System.out.println("@PostActivate事件");
}

@Remove
public void stopSession(){
System.out.println("@Remove事件");
}
}
测试页面ejbTest2.jsp如下:
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="com.sillycat.ejb.*, javax.naming.*"%>
<%

try {
LifeCycle lifecycle = (LifeCycle) session.getAttribute("lifecycle");
if (lifecycle == null) {
InitialContext ctx = new InitialContext();
lifecycle = (LifeCycle) ctx.lookup("LifeCycleBean/remote");
session.setAttribute ("lifecycle", lifecycle);
}
out.println(lifecycle.say());
out.println("<BR>请注意观察Jboss控制台输出.等待10分钟,容器将会钝化此会话Bean,@PrePassivate注释的方法将会执行<BR>");
out.println("<font color=red>你可以调用stopSession方法把会话Bean实例删除。在删除会话Bean时,将触发@PreDestroy事件<BR></font>");
/*
lifecycle.stopSession();
*/
} catch (Exception e) {
out.println(e.getMessage());
}
%>

日志情况:
第一次点击进入:
13:47:35,933 INFO [STDOUT] @PostConstruct事件
13:47:35,935 INFO [STDOUT] @Init事件
13:57:02,775 INFO [STDOUT] @PrePassivate事件
刷新页面:
14:10:56,584 INFO [STDOUT] @PostActivate事件
这里比较奇怪的是@PostConstruct事件竟然在@Init前面了。呵呵。

2.10 拦截器(Interceptor)
拦截器可以拦截Session Bean和Message-driven bean的方法调用或生命周期事件。拦截器用于封装应用的公用行为,使这些行为与业务逻辑分离。拦截器可以是同一bean类中的方法或是一个外部类。
接口示例HelloChina.java:
package com.sillycat.ejb;

public interface HelloChina {
public String sayHello(String name);
public String myname();
}

EJB的Bean如下HelloChinaBean.java:
package com.sillycat.ejb.impl;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;

import com.sillycat.ejb.HelloChina;
import com.sillycat.ejb.HelloChinaLocal;

@Stateless
@Remote(HelloChina.class)
@Local(HelloChinaLocal.class)
@Interceptors(HelloInterceptor.class)
//@Interceptors({A.class,B.class})
public class HelloChinaBean implements HelloChina, HelloChinaLocal {

public String myname() {
return "sillycat";
}

//@Interceptors(HelloInterceptor.class) 拦截某一方法
public String sayHello(String name) {
return name + ",hello ejb.";
}

}
这里@Interceptors({A.class,B.class})是多个拦截类的写法,另外也可以把这个标签写在特定的方法上,表示只拦截这个方法。
拦截器类HelloInterceptor.java如下:
package com.sillycat.ejb.impl;

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class HelloInterceptor {

@AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
System.out.println("*** HelloInterceptor intercepting");
long start = System.currentTimeMillis();
try {
if (ctx.getMethod().getName().equals("sayHello")) {
System.out.println("*** sayHello 已经被调用! *** ");
String name = (String)ctx.getParameters()[0];
//get the first parameters
if("boss".equals(name)){
throw new Exception("the method will not go any further.");
}
}
if (ctx.getMethod().getName().equals("myname")) {
System.out.println("*** myname 已经被调用! *** ");
}
return ctx.proceed();
} catch (Exception e) {
throw e;
} finally {
long time = System.currentTimeMillis() - start;
System.out.println("用时:" + time + "ms");
}
}
}
中途如果抛出错误,没有调用ctx.proceed(),那么将不会调用最终的业务方法。
if("boss".equals(name)){
throw new Exception("the method will not go any further.");
}
javax.interceptor.InvocationContext封装了客户端所调用业务方法的信息。
getTarget() 指向背调用的bean实例
getMethod()指向被拦截的业务方法
getParameters()获取被拦截业务方法的参数
setParameters()设置被拦截业务方法的参数
getContextData()方法返回一个Map对象,它在整个方法调用期间都可以访问。位于同一个方法调用内的不同拦截器之间,可以利用它来传递上下文相关的数据。

我们的测试类HelloChinaTest.java如下:
package com.sillycat.ejb;

import javax.naming.InitialContext;

import org.junit.BeforeClass;
import org.junit.Test;

public class HelloChinaTest {

protected static HelloChina helloChina;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
InitialContext ctx = new InitialContext();
helloChina = (HelloChina) ctx.lookup("HelloChinaBean/remote");
}

@Test
public void testSayHello() {
System.out.println(helloChina.sayHello("sillycat"));
System.out.println(helloChina.myname());
System.out.println(helloChina.sayHello("boss"));
}
}

也可以将拦截方法直接放到业务方法类里面,直接将拦截类的方法拷贝过去就行了,加上@AroundInvoke标签。

默认拦截器
在一个EJB模块中有很多的EJB,如果对所有EJB方法进行拦截,就要再ejb-jar.xml中配置了,这里支持通配符。

禁用拦截器
如果配置ejb-jar.xml给很多EJB都加上了拦截,但是某些EJB我们又不想拦截,那么就要使用@ExcludeDefaultInterceptors注释。

@Stateless
@Remote(HelloChina.class)
@Local(HelloChinaLocal.class)
@ExcludeDefaultInterceptors

这里仅仅是不执行默认拦截器,如果自身定义了拦截器,还是要执行的。

拦截生命周期事件
Files contained in javax.ejb.jar: META-INF/MANIFEST.MF javax.ejb.AccessLocalException.class javax.ejb.AccessTimeout.class javax.ejb.ActivationConfigProperty.class javax.ejb.AfterBegin.class javax.ejb.AfterCompletion.class javax.ejb.ApplicationException.class javax.ejb.Asynchronous.class javax.ejb.AsyncResult.class javax.ejb.BeforeCompletion.class javax.ejb.ConcurrencyManagement.class javax.ejb.ConcurrencyManagementType.class javax.ejb.ConcurrentAccessException.class javax.ejb.ConcurrentAccessTimeoutException.class javax.ejb.CreateException.class javax.ejb.DependsOn.class javax.ejb.DuplicateKeyException.class javax.ejb.EJB.class javax.ejb.EJBAccessException.class javax.ejb.EJBContext.class javax.ejb.EJBException.class javax.ejb.EJBHome.class javax.ejb.EJBLocalHome.class javax.ejb.EJBLocalObject.class javax.ejb.EJBMetaData.class javax.ejb.EJBObject.class javax.ejb.EJBs.class javax.ejb.EJBTransactionRequiredException.class javax.ejb.EJBTransactionRolledbackException.class javax.ejb.embeddable.EJBContainer.class javax.ejb.EnterpriseBean.class javax.ejb.EntityBean.class javax.ejb.EntityContext.class javax.ejb.FinderException.class javax.ejb.Handle.class javax.ejb.HomeHandle.class javax.ejb.IllegalLoopbackException.class javax.ejb.Init.class javax.ejb.Local.class javax.ejb.LocalBean.class javax.ejb.LocalHome.class javax.ejb.Lock.class javax.ejb.LockType.class javax.ejb.MessageDriven.class javax.ejb.MessageDrivenBean.class javax.ejb.MessageDrivenContext.class javax.ejb.NoMoreTimeoutsException.class javax.ejb.NoSuchEJBException.class javax.ejb.NoSuchEntityException.class javax.ejb.NoSuchObjectLocalException.class javax.ejb.ObjectNotFoundException.class javax.ejb.PostActivate.class javax.ejb.PrePassivate.class javax.ejb.Remote.class javax.ejb.RemoteHome.class javax.ejb.Remove.class javax.ejb.RemoveException.class javax.ejb.Schedule.class javax.ejb.ScheduleExpression.class javax.ejb.Schedules.class javax.ejb.SessionBean.class javax.ejb.SessionContext.class javax.ejb.SessionSynchronization.class javax.ejb.Singleton.class javax.ejb.spi.EJBContainerProvider.class javax.ejb.spi.HandleDelegate.class javax.ejb.Startup.class javax.ejb.Stateful.class javax.ejb.StatefulTimeout.class javax.ejb.Stateless.class javax.ejb.TimedObject.class javax.ejb.Timeout.class javax.ejb.Timer.class javax.ejb.TimerConfig.class javax.ejb.TimerHandle.class javax.ejb.TimerService.class javax.ejb.TransactionAttribute.class javax.ejb.TransactionAttributeType.class javax.ejb.TransactionManagement.class javax.ejb.TransactionManagementType.class javax.ejb.TransactionRequiredLocalException.class javax.ejb.TransactionRolledbackLocalException.class javax.interceptor.AroundInvoke.class javax.interceptor.AroundTimeout.class javax.interceptor.ExcludeClassInterceptors.class javax.interceptor.ExcludeDefaultInterceptors.class javax.interceptor.Interceptor.class javax.interceptor.InterceptorBinding.class javax.interceptor.Interceptors.class javax.interceptor.InvocationContext.class javax.xml.rpc.handler.GenericHandler.class javax.xml.rpc.handler.Handler.class javax.xml.rpc.handler.HandlerChain.class javax.xml.rpc.handler.HandlerInfo.class javax.xml.rpc.handler.HandlerRegistry.class javax.xml.rpc.handler.MessageContext.class
Files contained in javax.ejb.jar: META-INF/MANIFEST.MF javax.ejb.AccessLocalException.class javax.ejb.AccessTimeout.class javax.ejb.ActivationConfigProperty.class javax.ejb.AfterBegin.class javax.ejb.AfterCompletion.class javax.ejb.ApplicationException.class javax.ejb.Asynchronous.class javax.ejb.AsyncResult.class javax.ejb.BeforeCompletion.class javax.ejb.ConcurrencyManagement.class javax.ejb.ConcurrencyManagementType.class javax.ejb.ConcurrentAccessException.class javax.ejb.ConcurrentAccessTimeoutException.class javax.ejb.CreateException.class javax.ejb.DependsOn.class javax.ejb.DuplicateKeyException.class javax.ejb.EJB.class javax.ejb.EJBAccessException.class javax.ejb.EJBContext.class javax.ejb.EJBException.class javax.ejb.EJBHome.class javax.ejb.EJBLocalHome.class javax.ejb.EJBLocalObject.class javax.ejb.EJBMetaData.class javax.ejb.EJBObject.class javax.ejb.EJBs.class javax.ejb.EJBTransactionRequiredException.class javax.ejb.EJBTransactionRolledbackException.class javax.ejb.embeddable.EJBContainer.class javax.ejb.EnterpriseBean.class javax.ejb.EntityBean.class javax.ejb.EntityContext.class javax.ejb.FinderException.class javax.ejb.Handle.class javax.ejb.HomeHandle.class javax.ejb.IllegalLoopbackException.class javax.ejb.Init.class javax.ejb.Local.class javax.ejb.LocalBean.class javax.ejb.LocalHome.class javax.ejb.Lock.class javax.ejb.LockType.class javax.ejb.MessageDriven.class javax.ejb.MessageDrivenBean.class javax.ejb.MessageDrivenContext.class javax.ejb.NoMoreTimeoutsException.class javax.ejb.NoSuchEJBException.class javax.ejb.NoSuchEntityException.class javax.ejb.NoSuchObjectLocalException.class javax.ejb.ObjectNotFoundException.class javax.ejb.PostActivate.class javax.ejb.PrePassivate.class javax.ejb.Remote.class javax.ejb.RemoteHome.class javax.ejb.Remove.class javax.ejb.RemoveException.class javax.ejb.Schedule.class javax.ejb.ScheduleExpression.class javax.ejb.Schedules.class javax.ejb.SessionBean.class javax.ejb.SessionContext.class javax.ejb.Session
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值