EJB3 - Session bean
description
其實session bean是最一開始就看的, 回過頭來看再記重點有點心浮氣躁.reference
EJB3 in Action - CH3 - Building business logic with session beansFocal Points
session bean一定要有一個以上的interface與一個實現 一個session bean可以有多個interface, 所以當客戶端調用一個@Local的interface, 就是使用local的session bean. 使用@Remote或@WebService就是用remote或web service的session bean. session bean一定要是concrete class, 一定要有無參构造函数, 不能是abstract或final. session bean可以是其他session bean或POJO的subclass. session bean的business method與lifecycle callback可定在super class或session bean class裡. session bean的annotation的繼承是有條件的, 就是class level比方說@Stateless, @Stateful會被忽略, 不過lifecycle callback會被繼承下來. session bean的business method name不能以ejb開頭, 比方說不能是ejbDoit() session bean的method必須是public且不能是static或final 使用一個remote business interface要注意argument與return type必須實作Serizable 所有session bean都有的生命週期是creation / destruction stateful bean比stateless bean又多了passivation / activation stateless bean的lifecycle callback為@PostConstruct, @PreDestroy stateful bean的lifecycle callback為@PostConstruct, @PreDestroy, @PostActivate, @PrePassivate @PostConstruct, @PreDestroy比較簡單.就是container实例化session bean後调用@PostContruct, container移除session bean前會调用@PreDestroy @PostActivate, @PrePassivate比較特別. 一旦container判斷一個stateful bean停用了而決定要暫時讓這個session bean失去效用, 這個動作叫钝化. 而container讓已經失效的stateful bean再度生效就叫激活. 所以@PostActivate就是activation後调用的method, @PrePassivate就是钝化前呼叫的method. lifecycle callback可以是public, private, protected, package-protected lifecycle callback主要用來替session bean準備实例化後需要的資源以及從container移除前要釋放的資源. lifecycle callback除了放在session bean以外也可放在分開的interceptor class stateless session bean有pool, 也就是說有一定數量的stateless session bean在container的pool中 @Stateless的定義- 屬性有name, mappedName, description
- name屬性用來指定bean的name, 有的container用來和JNDI綁定. 如果name沒有設定就會是bean的class name.
- mappedName是vender-specific(特定于厂商), 也就是依不同container有不同的情形. 以GlassFish來說是將mappedName的值綁定到JNDI name.
- 一個@Remote interface可以繼承java.rmi.Remote
public interface TestRemote extends Remote { ... }
就算程序裡面沒寫繼承Remote, container還是會在byte code階段插入繼承Remote的動作 - 沒有程序上繼承java.rmi.Remote的好處就是不用處理java.rmi.RemoteException
- @Remote business interface有個需求就是所有的參數與回傳值都必須是Serializable, 因為這樣才能通過RMI
@Stateful(name="SimpleStatefulBean", mappedName="ejb/SimpleStatefulBean") public class SimpleStatefulBean implements SimpleStatefulRemote { private Logger logger = Logger.getLogger("SimpleStatefulBean"); private byte[] b = new byte[100000]; { for ( int i = 0; i < b.length; i++ ) { b[i] = (byte) 100; } } public String simpleShow() { return this + ":This is simple show" + b; } @PostConstruct public void postConstruct() { logger.info("create " + this); } @PreDestroy public void preDestroy() { logger.info("destroy " + this); } @PostActivate public void postActivate() { logger.info("activate " + this); } @PrePassivate public void prePassivate() { logger.info("passivate " + this); } @Remove public void remove() { logger.info("remove " + this); } }放interceptor的lifecycle callback
@Stateless
@Interceptors(value={SimpleInterceptor.class})
public class SimpleStatelessBean implements SimpleStatelessLocal {
private Logger logger = Logger.getLogger("SimpleStatelessBean");
@Resource(name="TestQueueConnectionFactory")
private ConnectionFactory connectionFactory;
@Resource(name="jms/TestQueueDestination")
private Destination destination;
public String simpleShow() {
try {
Connection conn = connectionFactory.createConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(destination);
TextMessage message = session.createTextMessage();
message.setText("This is text message");
messageProducer.send(message);
messageProducer.close();
session.close();
conn.close();
} catch (JMSException ex) {
throw new RuntimeException( ex );
}
return this + ":This is simple show";
}
}
public class SimpleInterceptor {
Logger logger = Logger.getLogger("SimpleStatefulBeanInterceptor");
@PostConstruct
public void onCreate(InvocationContext ic) {
try {
logger.info("create " + this);
ic.proceed();
} catch (Exception ex) {
Logger.getLogger(SimpleInterceptor.class.getName()).log(Level.SEVERE, null, ex);
}
}
@AroundInvoke
public Object aroundInvoke(InvocationContext ctx) throws Exception {
logger.info(ctx + " is invoked.");
return ctx.proceed();
}
@PreDestroy
public void onDestroy(InvocationContext ic) {
try {
logger.info("destroy " + this);
ic.proceed();
} catch (Exception ex) {
Logger.getLogger(SimpleInterceptor.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
stateful bean的@PrePassivate, @PostActivate也可放interceptor
@Interceptors(value={SimpleInterceptor.class})
@Stateful(name="SimpleStatefulBean", mappedName="ejb/SimpleStatefulBean")
public class SimpleStatefulBean implements SimpleStatefulRemote {
private byte[] b = new byte[100000];
{
for ( int i = 0; i < b.length; i++ ) {
b[i] = (byte) 100;
}
}
public String simpleShow() {
return this + ":This is simple show" + b;
}
@Remove
public void remove() {
Logger.getLogger("SimpleStatefulBean").info("remove " + this);
}
}
public class SimpleInterceptor {
@PostConstruct
public void onCreate(InvocationContext ic) {
try {
Logger.getLogger(SimpleInterceptor.class.getName()).info("create " + this);
ic.proceed();
} catch (Exception ex) {
Logger.getLogger(SimpleInterceptor.class.getName()).log(Level.SEVERE, null, ex);
}
}
@PostActivate
public void onActivate(InvocationContext ic) {
try {
Logger.getLogger(SimpleInterceptor.class.getName()).info("activate " + this);
ic.proceed();
} catch (Exception ex) {
Logger.getLogger(SimpleInterceptor.class.getName()).log(Level.SEVERE, null, ex);
}
}
@PrePassivate
public void onPassivate(InvocationContext ic) {
try {
Logger.getLogger(SimpleInterceptor.class.getName()).info("passivate " + this);
ic.proceed();
} catch (Exception ex) {
Logger.getLogger(SimpleInterceptor.class.getName()).log(Level.SEVERE, null, ex);
}
}
@AroundInvoke
public Object aroundInvoke(InvocationContext ctx) throws Exception {
Logger.getLogger(SimpleInterceptor.class.getName()).info(ctx + " is invoked.");
return ctx.proceed();
}
@PreDestroy
public void onDestroy(InvocationContext ic) {
try {
Logger.getLogger(SimpleInterceptor.class.getName()).info("destroy " + this);
ic.proceed();
} catch (Exception ex) {
Logger.getLogger(SimpleInterceptor.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@EJB用來注入session bean到client code. @EJB有幾個屬性: name, beanInterface, beanName. name用來指定JNDI name, beanName則是當一個interface有兩個實作時用來決定要注入哪個實作. 使用@EJB注入的時候如果沒有指定JNDI name, container就會用interface name當成JNDI name注入. 如果要注入同個interface不同的實作可透過指定JNDI name或beanName
@Stateless(name="SimpleStatelessBean1") public class SimpleStatelessBean1 implements SimpleStatelessLocal { ... } @Stateless(name="SimpleStatelessBean2") public class SimpleStatelessBean2 implements SimpleStatelessLocal { ... } public class SimpleStatelessServlet extends HttpServlet { @EJB(beanName="SimpleStatelessBean1") private SimpleStatelessLocal simpleStatelessLocal1; @EJB(beanName="SimpleStatelessBean2") private SimpleStatelessLocal simpleStatelessLocal2; ... }可注入stateless bean或stateful bean到其他的stateful bean. 但不能注入stateful bean到stateless bean, 因為這樣stateful session bean就會被所有client分享. 注入stateful bean到另一個stateful bean時, 一旦持有注入的stateful bean destroy了, 被持有的stateful bean也會一起destroy. 如果不用stateful bean可將狀態放在DB中或放在server side的檔案或放HttpSession, 不過要注意清除不必要的資源. 儲存conversation state的時候要注意儲存的值愈小愈好, 例如primitive. stateful bean要記得定義@Remove method. 調整server到stateful bean效能最佳的狀態, 小心頻繁的passivate / activate造成效能變差太多.