Java EE组件生命周期和与并发相关的详细信息对于经验丰富的专业人员而言可能不是新知识,但是对于初学者而言,这可能需要花费一些时间。
就EJB而言,了解其生命周期 (以及相关的并发场景)对于确保使用EJB的正确用法和解决方案设计至关重要。 容易滥用它们!
我将在这篇文章中快速介绍无 状态豆和有状态豆,暂时跳过Lima Beans!
- 有状态会话Bean –生命周期+并发处理
- 无状态 bean –仅用于并发模型,因为我在之前的一篇文章中简要介绍了生命周期。
有状态会话Bean的生命周期中有哪些不同的状态?
- 不存在
- 准备
- 钝化的
是什么触发了他们?
这是一个快速的表格快照和一个高级图表。 有关更多详细信息,请继续阅读。 。 。
注意 :DNE –不存在, R –就绪, P –钝化,SFSB –有状态会话Bean
国家过渡 | 扳机 | 回呼 |
---|---|---|
DNE转R | 首次通过JNDI或DI访问SFSB实例时 | @PostConstruct |
R到DNE | 容器关闭,客户端调用用@Remove注释的方法,Bean达到由DD或@StatefulTimeout指定的空闲超时阈值 | @PreDestroy |
R到P | EJB容器会钝化闲置的bean,并根据特定算法将其从活动内存中删除 | @PrePassivate |
P到DNE | Bean达到由DD或@StatefulTimeout指定的空闲超时阈值 | 注意 :不会调用@PreDestroy注释方法 |
从P到R | 客户端被钝化但尚未超时后调用SFSB实例时 | @PostActivate |
注意 :如果SFSB在请求处理期间引发异常,则其实例将被破坏,即进入DNE状态。 在这种情况下,不会调用@PreDestroy注释方法
现在,我们对SFSB的生命周期有了一些了解,让我们尝试看一下这些Bean在负载下的行为,即当多个用户一次使用该应用程序时,它转化为并发访问SFSB实例。
有状态会话Bean:并发管理
线程安全是EJB的核心功能之一。 需要注意的一点是,此线程安全性是免费的,并且不需要任何与并发相关的构造由Bean开发人员自己进行编码 (有一些例外 )。 就SFSB而言,EJB容器确保在特定时间只有一个线程可以访问bean实例。
在此示例中,我们尝试通过JMeter调用测试Servlet来模拟对SFSB单个实例的并发访问 。 Servlet通过DI注入bean并在其上调用方法。 SFSB方法仅使用Thread.sleep()假装好像正在执行某些操作。
package com.abhirockzz.wordpress.ejb.lifecycle.stateful;import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateful;@Stateful
public class MyStatefulBean {public MyStatefulBean() {}public void act() {System.out.println("Entered MyStatefulBean/act() on " + new Date().toString() + " . SFSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException ex) {Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex);}System.out.println("Exit MyStatefulBean/act() on " + new Date().toString() + " . SFSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());}
}
package com.abhirockzz.wordpress.ejb.lifecycle.stateful;import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet(name = "SFSBTestServlet", urlPatterns = {"/SFSBTestServlet"})
public class SFSBTestServlet extends HttpServlet {public SFSBTestServlet() {}@InjectMyStatefulBean mySFSB;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("Entered SFSBTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());mySFSB.act();}}
观察结果
- 由于Servlet本身不是线程安全的,因此实际上有多个线程将进入doGet()方法
- 并发访问SFSB的单个实例(通过hashCode结果证明)(请参阅记录的语句中的线程名称)
- 但是,只有一个线程将能够访问SFSB实例–当SFSB方法返回时,其他线程等待其轮换。 通过控制台上的日志语句可以明显看到此延迟
无状态豆呢?
这些bean 本质上是线程安全的 。 为什么呢 这是因为默认情况下,容器确保每个新请求都由 Bean 的新实例提供服务。 请记住,客户端可以通过3种可能的方式获得对无状态bean的引用-DI,JNDI或通过远程接口(RMI)。 在所有这些情况下,都是容器(代理)拦截了该调用–因此,即使看似多个线程正在访问同一bean实例,它实际上也不是同一实例!
package com.abhirockzz.wordpress.ejb.lifecycle.stateless;import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;@Stateless
public class MyStatelesslBean {public void act() {System.out.println("Entered MyStatelesslBean/act() on " + new Date().toString() + " . SLSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException ex) {Logger.getLogger(MyStatelesslBean.class.getName()).log(Level.SEVERE, null, ex);}System.out.println("Exit MyStatelesslBean/act() on " + new Date().toString() + " . SLSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());}
}
package com.abhirockzz.wordpress.ejb.lifecycle.stateless;import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet(name = "SLSBTestServlet", urlPatterns = {"/SLSBTestServlet"})
public class SLSBTestServlet extends HttpServlet {@InjectMyStatelesslBean slsb;@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("Entered SLSBTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());slsb.act();}}
观察结果
- 由于Servlet本身不是线程安全的,因此实际上有多个线程将进入doGet()方法
- 容器正在选择SLSB的不同实例 (通过hashCode结果显而易见)来管理并发请求(请参见记录的语句中的线程名称)。
- 尽管有并发请求,但每个请求线程都由一个新实例提供服务
目前为止就这样了! 我计划在以后的文章中介绍Singleton Session bean。 敬请关注 。 。 。 。
谢谢阅读!
翻译自: https://www.javacodegeeks.com/2014/08/ejb-3-x-lifecycle-and-concurrency-models-part-1.html