EJB3 - Session bean

EJB3 - Session bean

EJB3 - Session bean

description

其實session bean是最一開始就看的, 回過頭來看再記重點有點心浮氣躁.

reference

EJB3 in Action - CH3 - Building business logic with session beans

Focal 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的定義
    1. 屬性有name, mappedName, description
    2. name屬性用來指定bean的name, 有的container用來和JNDI綁定. 如果name沒有設定就會是bean的class name.
    3. mappedName是vender-specific(特定于厂商), 也就是依不同container有不同的情形. 以GlassFish來說是將mappedName的值綁定到JNDI name.
  • @Local: stateless session bean的local interface, local interface表示這個session bean和client放在同一個JVM上執行.
  • @Remote: 當client存在於container外的JVM時就必須使用@Remote
    1. 一個@Remote interface可以繼承java.rmi.Remote
       public interface TestRemote extends Remote { ... } 
      就算程序裡面沒寫繼承Remote, container還是會在byte code階段插入繼承Remote的動作
    2. 沒有程序上繼承java.rmi.Remote的好處就是不用處理java.rmi.RemoteException
    3. @Remote business interface有個需求就是所有的參數與回傳值都必須是Serializable, 因為這樣才能通過RMI
  • @WebService: 透過@WebService可讓session bean成為SOAP-based web service. 唯一要做的就是在interface上加上@WebService.
  • 不能讓一個business interface同時@Local又@Remote或是又@WebService, 不過可以透過interface的繼承改變要使用的是@Remote session bean還是@Local的 session bean.
  • 放session bean的lifecycle callback
  • 可以有多個@PostConstruct, @PreDestroy (不過我試起來一個session bean就只能一個lifecycle callback有效, 頂多除了callback以外還指定interceptor, 就加上interceptor的一個lifecycle callback有效.)
  • lifecycle callback要符合pattern: void < METHOD >()
  • interceptor內的lifecycle callback要符合pattern: Object < METHOD >(InvocationContext) throws Exception, 然後記得回傳InvocationContext.proceed, 除非打算不繼續執行. (可參考EJB3 Interceptor)
  • session bean的lifecycle callback不可有checked exception, interceptor的則可以.
  • session bean的lifecycle callback不可有傳入參數, interceptor則要傳入InvocationContext, 否則有java.lang.IllegalArgumentException: wrong number of arguments
  • stateless session bean與stateful session bean的差別主要在於container管理的方式, stateless session bean起始之後會被container放進pool, 等client要使用的時候再從pool取出, 用完再放回pool. stateful session bean則是讓一個client擁有一個stateful session bean直到client離開或stateful session bean destroy為止. stateful session bean與client是one to one的關係.
  • stateful session bean需要付出代價, stateless session bean由於所有client共用session bean比較能節省資源, stateful session bean則因為與client是one to one的關係所以比較耗資源. 一旦container判斷消耗資源太多或佔用資源太久就會開始執行passivate的動作.
  • 由於stateful session bean比較耗資源, 所以注意要在stateful session bean加上@Remove method, 當呼叫此method, container就會負責將此method destroy以節省資源.
  • 由於stateful session bean在passivate的時候會做serialize的動作, 所以注意stateful session bean的class 成员必須實做Serializable或必須是原始类型. 否則在passivate的時候會出現例如[NRU-stateful.SimpleStatefulBean]: passivateEJB(), Exception caught 的exception, 就是因為無法serialize該object的關係. 如果要使用不須serialize的class 成员只要用transient声明該class member或在@PrePassivate把class member改成null再於@PostActivate設定回來即可.
  • 使用stateful session bean的方式幾乎和stateless session bean的方式幾乎一樣, 唯一不一樣的是stateful session bean的business interface只能使用@Local與@Remote而不能用@WebService. 因為SOAP-based web Service本來就不是stateful因此無法使用.
  • stateful session bean的生命週期中有重要的一點就是container在destroy一個passivate的時候會先將該stateful session bean先activate再passivate.
    @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造成效能變差太多.
  • 你可能感兴趣的:(EJB3 - Session bean)