终于搞明白了EJB!| 从开发角度看应用架构3:部署一个无状态的session bean

一、前言

本文仅代表作者的个人观点;

本文在书写过程中,得到了同事kylin的指导,在此表示感谢;

本文的内容仅限于技术探讨,不能作为指导生产环境的素材;

本文素材是红帽公司产品技术和手册;

本文分为系列文章,将会有多篇,初步预计将会有9篇。 本文的第二节中,原文引用了网络上的部分内容,原文链接:https://www.cnblogs.com/strugglion/p/6027318.html

二、啥是EJB

企业Java Bean(EJB)是一种Java EE组件,通常用于在企业应用程序中封装业务逻辑。EJB与Java SE中的简单Java bean不同,开发人员必须明确地实现多线程、并发、事务和安全等概念,应用程序服务器在运行时提供了这些功能,使开发人员可以专注于编写应用程序的业务逻辑。

使用EJB来模拟企业应用程序的业务逻辑有以下几个优点:

  • EJB提供底层系统服务,如多线程和并发,而不需要开发人员为这些服务明确编写代码。这对于有大量用户同时访问应用程序的企业应用程序很重要。
  • 业务逻辑被封装到一个便携式组件中,该组件可以以对客户端透明的方式分布到多台计算机上,并允许您在大量客户端同时访问应用程序时负载均衡请求。
  • 有了EJB以后,客户端可以专注于用户界面方面而不需要混合业务逻辑,客户端代码被简化了。
  • EJB为企业应用程序提供事务功能,其中许多用户同时访问应用程序,而应用程序服务器通过使用事务来确保数据完整性。
  • EJB组件被以RBAC方式调用。应用程序服务器为验证和授权服务提供了一个API,而不需要开发人员写代码调用。
  • EJB可以被多种不同类型的客户端访问,包括独立的远程客户端、其他Java EE组件或使用SOAP或REST等标准协议的Web服务客户端。

EJB主要有两种类型:

Java EE规范定义了两种不同类型的EJB:

  • Session bean:从客户端调用时执行操作。 通常,应用程序的核心业务逻辑是作为高级API(会话Facade模式)公开的,可以分发并且可以通过多种协议(RMI,JNDI,Web服务)访问。
  • Message Driven Bean,MDB:用于Java EE应用程序中各组件之间的异步通信,可用于接收与Java消息传递服务(JMS)兼容的消息,并根据接收消息的内容采取一些操作。

接下来,我们有用大白话解释一下EJB。

【以下内容为网络内容原文引用】

我们先看一下,EJB 的官方解释:

商务软件的核心部分是它的业务逻辑。业务逻辑抽象了整个商务过程的流程,并使用计算机语言将他们实现。

J2EE 对于这个问题的处理方法是将业务逻辑从客户端软件中抽取出来,封装在一个组件中。这个组件运行在一个独立的服务器上,客户端软件通过网络调用组件提供的服务以实现业务逻辑,而客户端软件的功能单纯到只负责发送调用请求和显示处理结果。在J2EE 中,这个运行在一个独立的服务器上,并封装了业务逻辑的组件就是EJB(Enterprise JavaBean)组件。这其中我们主要关注这么几点,我们来逐条剖析:

剖析1:所谓:"业务逻辑"

我们注意到在EJB 的概念中主要提到的就是"业务逻辑"的封装,而这个业务逻辑到底是什么?说的那么悬乎,其实这个所谓的"业务逻辑"我们完全可以理解成执行特定任务的"类"。

剖析2:所谓:"将业务逻辑从客户端软件中抽取出来,封装在组件中……运行在一个服务器上"

既然我们知道了"业务逻辑"的概念就是执行特定任务的"类",那么,什么叫"从客户端软件中抽取出来"?其实,这个就是把原来放到客户端的"类",拿出来不放到客户端了,放到一个组件中,并将这个组件放到一个服务器上去运行。

变成大白话就是,"把你编写的软件中那些需要执行制定的任务的类,不放到客户端软件上了,而是给他打成包放到一个服务器上了"。EJB 就是将那些"类"放到一个服务器上,用C/S 形式的软件客户端对服务器上的"类"进行调用。

EJB 是运行在独立服务器上的组件,客户端是通过网络对EJB 对象进行调用的。在Java中,能够实现远程对象调用的技术是RMI,而EJB 技术基础正是RMI。通过RMI 技术,J2EE将EJB 组件创建为远程对象,客户端就可以通过网络调用EJB 对象了。

那么,什么是RMI?

先看两个概念:

  • 对象的序列化
  • 分布式计算与RPC

名词1:对象的序列化 对象的序列化概念:对象的序列化过程就是将对象状态转换成字节流和从字节流恢复对象。将对象状态转换成字节流之后,可以用java.io 包中的各种字节流类将其保存到文件中,或者通过网络连接将对象数据发送到另一个主机。 上面的说法有点"八股",我们不妨再用白话解释一下:对象的序列化就是将你程序中实例化的某个类的对象,比如,你自定一个类MyClass,或者任何一个类的对象,将它转换成字节数组,也就是说可以放到一个byte 数组中,这时候,你既然已经把一个对象放到了byte数组中,那么你当然就可以随便处置了它了,用得最多的就是把他发送到网络上远程的计算机上了。

名词2:分布式计算与RPC RPC 并不是一个纯粹的Java 概念,因为在Java 诞生之前就已经有了RPC 的这个概念,RPC是"Remote Procedure Call"的缩写,也就是"远程过程调用"。在Java 之前的大多数编程语言,如,Fortran、C、COBOL 等等,都是过程性的语言,而不是面向对象的。所以,这些编程语言很自然地用过程表示工作,如,函数或子程序,让其在网络上另一台机器上执行。说白了,就是本地计算机调用远程计算机上的一个函数。

名词3:二者结合就是RMI RMI 英文全称是"Remote Method Invocation",它的中文名称是"远程方法调用",它就是利用Java 对象序列化的机制实现分布式计算,实现远程类对象的实例化以及调用的方法。说的更清楚些,就是利用对象序列化来实现远程调用,也就是上面两个概念的结合体,利用这个方法来调用远程的类的时候,就不需要编写Socket 程序了,也不需要把对象进行序列化操作,直接调用就行了非常方便。

远程方法调用是一种计算机之间对象互相调用对方函数,启动对方进程的一种机制,使用这种机制,某一台计算机上的对象在调用另外一台计算机上的方法时,使用的程序语法规则和在本地机上对象间的方法调用的语法规则一样。

通过RMI 技术,J2EE 将EJB 组件创建为远程对象,EJB 虽然用了RMI 技术,但是却只需要定义远程接口而无需生成他们的实现类,这样就将RMI 技术中的一些细节问题屏蔽了。但不管怎么说,EJB 的基础仍然是RMI,所以,如果你想了解EJB 的原理,只要把RMI的原理搞清楚就行了。你也就弄清楚了什么时候用EJB 什么时候不需要用EJB 了。

既然已经知道了,RMI 是将各种任务与功能的类放到不同的服务器上,然后通过各个服务器间建立的调用规则实现分布式的运算,也就明白EJB 所谓的"服务群集"的概念。就是将原来在一个计算机上运算的几个类,分别放到其他计算机上去运行,以便分担运行这几个类所需要占用的CPU 和内存资源。同时,也可以将不同的软件功能模块放到不同的服务器上,当需要修改某些功能的时候直接修改这些服务器上的类就行了,修改以后所有客户端的软件都被修改了。

如下情况尽量就不要使用EJB 了: 1、较为简单的纯Web 应用开发,不需要用EJB。 2、需要与其他服务程序配合使用的应用,但调用或返回的自定义的网络协议可以解决的应用程序,不需要使用EJB。 3、较多人并发访问的C/S 结构的应用程序,尽量不要使用EJB。

总结:

a.EJB实现原理: 就是把原来放到客户端实现的代码放到服务器端,并依靠RMI进行通信。

b.RMI实现原理 :就是通过Java对象可序列化机制实现分布计算。

c.服务器集群: 就是通过RMI的通信,连接不同功能模块的服务器,以实现一个完整的功能。

【以上内容为网络内容原文引用】

三、Session bean

Session Bean为客户端提供接口,并封装可由多个客户端在本地或通过不同协议远程调用的业务逻辑方法。Session EJB可以以客户端透明的方式跨多台机器进行集群和部署。 Java EE标准没有正式定义应该如何聚集EJB的底层细节。 每个应用程序服务器都提供了自己的集群和高可用性机制。 Session bean的接口通常暴露封装应用程序核心业务逻辑的高级API。

有三种不同类型的Session bean,这取决于应用程序用例,可以在Java EE兼容的应用程序服务器上部署:

  • Stateless Session Bean(SLSB)

SLSB不会在调用之间与客户端保持对话状态。当客户端与Stateless Session Bean进行交互并调用其上的方法时,应用程序服务器将从预先实例化的无状态会话Bean池中分配一个实例。一旦客户端完成调用并断开连接,bean实例将被释放回池中或销毁。

无状态会话bean在应用程序必须为大量客户端同时访问bean的业务方法的情况下非常有用。它们通常可以比有状态会话bean更好地扩展,因为应用程序服务器不必维护状态,并且Bean可以在大型部署中的多台计算机上分布。

需要注意的是,使用无状态EJB时,不要定义需要在多个客户端之间共享的有状态数据元素和构造(例如,持有缓存的类地图数据结构)。如果需要共享状态数据,需要使用有状态会话bean或单例会话bean,这些类型的用例将得到更加恰当的解决。

无状态会话bean也是将SOAP或REST服务端点公开给Web服务客户端的首选方法。简单的注释被添加到bean类和方法中以实现此功能,而无需编写用于Web服务通信的样板代码。

  • Stateful Session Beans (SFSB)

与无状态会话bean相反,有状态的session bean通过多个调用与客户端保持会话状态。有状态bean实例数量和客户端数量之间存在一对一的关系。当客户端完成与bean的交互并断开连接时,bean实例被销毁。一个新的客户端产生一个拥有自己独特状态的新的有状态bean。应用程序服务器确保每个客户端为每个方法调用接收同一个有状态会话bean的实例。

有状态会话bean用于交互状态必须在交互期间与客户端一起维护的场景中。例如,购物车bean跟踪客户在电子商务应用程序中添加到购物车的项目数量。每个客户的购物车都被封装在bean的状态中,并且在客户添加、更新或移除购物项目时更新状态。

当开发人员构建有状态的EJB时,任何class级别的属性都必须作为私有的作用域,并且创建getter和setter方法以提供对这些属性的访问。这是在Java开发中使用的常见模式,但在使用支持JSF(Java Server Faces)页面的EJB时也会自动合并。在JSF源代码中使用表达式语言(EL)将表单字段映射到EJB属性时,可以通过getter和setter自动访问EJB属性,而无需显式使用方法名称。

下面以getter和setter方法显示有状态会话bean的示例:

@Stateful@Named("hello")public class Hello {	private String name;

	@Inject
	private PersonService personService;

	public void sayHello() throws IllegalStateException, SecurityException, SystemException {
		String response = personService.hello(name);
		FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(response));
	}	

               public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}
  • Singleton Session Beans

单例会话bean会在每个应用程序中实例化一次,并存在于应用程序的生命周期中。 每个客户端对单身bean的请求都发送到同一个实例。 Singleton会话Bean用于跨多个客户端共享单个企业bean实例的情况。

与由应用程序服务器合并的无状态会话bean不同,内存中只有一个单例会话bean实例。 与无状态会话bean类似,单例会话bean也可以用于实现Web服务端点。 开发人员可以提供注释来指示应用程序服务器在启动时必须初始化bean作为性能优化(例如,数据库连接,JNDI查找,JMS远程连接工厂创建等等)。

四、Message Driven Beans

消息驱动Bean(MDB)使Java EE应用程序异步处理消息。一旦部署在应用程序服务器上,它将监听JMS消息,并为每个收到的消息执行一个操作(调用MDB的onMessage方法)。 MDB为应用程序开发提供事件驱动的松散耦合模型。 MDB不会被注入或从客户端代码中调用,但会由收到的消息触发。

MDB是无状态的,不与客户保持任何对话状态。应用程序服务器维护一个MDB池,并通过分配和返回池中的实例并管理它们的生命周期。他们也可以参与事务处理,并且应用程序服务器根据消息处理的结果负责消息重新传送和消息接收确认。

有许多可以使用MDB的用例。最流行的是解耦系统,并防止它们的API通过直接调用而被紧密耦合。相反,两个系统可以通过以异步方式传递消息来进行通信,这确保了两个系统可以独立进化而不会相互影响。

五、实验展示:创建一个无状态的EJB

通过maven导入一个项目stateless-ejb:

查看这个ejb的overview:

查看依赖:

注意到EJB API被声明为一个依赖于所提供的范围的依赖项。 这是因为JBoss EAP实现了完整的Java EE概要文件,因此在运行时提供了必要的EJB库。

这也告诉Maven不要将这些库打包到最终的WAR文件中。

查看源码:

stateless-ejb→src→main→webapp文件夹并双击index.xhtml文件。

<h:form id="form">
	<p class="input">
		<h:outputLabel value="Enter your name:" for="name" />
		<h:inputText value="#{hello.name}" id="name" required="true" requiredMessage="Name is required"/>
	</p>
	<br class="clear"/>
	<br class="clear"/>
	<p class="input">		
<h:commandButton action="#{hello.sayHello()}"  value="Submit" styleClass="btn" />
	</p>
	<br class="clear"/>
	<br class="clear"/>
	<h:messages styleClass="messages"/>
</h:form>

上面标黄部分,表示:在Web表单提交时调用表达式语言(EL)值#{hello.sayHello()}。

查看原始代码:----Hello.java

在JBDS左侧窗格的Project Explorer选项卡中展开的stateless-ejb项目中,选择stateless-ejb→Java Resources→src / main / java→com.redhat.training.ui并展开它。 双击Hello.java文件。

在hello.java中,观察到无状态EJB是使用@EJB注释注入的。

查看HelloBean.java文件。

在JBDS左侧窗格的Project Explorer选项卡中展开的stateless-ejb项目中,选择stateless-ejb→Java Resources→src / main / java→com.redhat.training.ejb并展开它。 双击HelloBean.java文件

这个bean定义了公共方法sayHello,它简单地回显了作为输入发送的字符串

接下来,启动EAP:

接下来,运行JUnit测试并检查结果。

查看测试类EJBTest.java

在JBDS左侧窗格的Project Explorer选项卡中展开的stateless-ejb项目中,选择stateless-ejb→Java Resources→src / test / java→com.redhat.training.ejb,然后双击EJBTest.java文件。

测试类用@RunWith(Arquillian.class)注释,以确保JBDS使用Arquillian Runner将应用程序部署到服务器进行测试。

而原始的Hello.ava这段源码中缺少对EJB的引用,需要添加如下内容,让EJB container能够识别到这个无状态的session EJB:

接下来,对代码进行junit测试

测试类用@RunWith(Arquillian.class)注释,以确保JBDS使用Arquillian Runner将应用程序部署到服务器进行测试。

测试通过:

通过运行以下命令,使用Maven在JBoss EAP上对应用程序执行deploy操作:

war在EAP上部署成功:

通过浏览器,可以访问部署好的应用:

输入david,提交:

返回值:Hello david!

所以,整体调用的流程是:

1.打开浏览器,在浏览器地址中输入应用地址:localhost:8080/stateless-ejb

2. 在方框中输入“david”,点击submit。

3.web container中的serverlet响应请求,并调用rest api,将信息传给app server;

4.信息传给hello.java定义的类-hello。而hello这个class并不“干活”,直接将前端请求转给EJB:HelloBean.java中定义的HelloBean

5. HelloBean将接受到前端输入的“david”信息,进行拼接,拼接成 Hello david,并返回。

6.最终,IE浏览器页面看到:Hello david

参考文件:

1.https://www.cnblogs.com/strugglion/p/6027318.html

魏新宇

  • "大魏分享"运营者、红帽资深解决方案架构师
  • 专注开源云计算、容器及自动化运维在金融行业的推广
  • 拥有MBA、ITIL V3、Cobit5、C-STAR、TOGAF9.1(鉴定级)等管理认证。
  • 拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、AIX、HPUX等技术认证。

原文发布于微信公众号 - 大魏分享(david-share)

原文发表时间:2018-06-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏屈定‘s Blog

工作--如何封装第三方服务?

业务开发中经常会对接某某第三方服务,因此会经常写一些SDK供服务使用,一种比较好的做法就是使用命令模式封装第三方服务,命令模式对于调用方来说简洁明了,也正是封装...

662
来自专栏软件开发

JavaSE学习总结(一)——Java基础

一、Java是什么 Java 是由 Sun Microsystems 在 1995 年首先发布的编程语言和计算平台。Java 是一项用于开发应用程序的技术,可以...

1885
来自专栏微服务生态

跟着小程学微服务-Mock自动化系统的原理及实现

在之前的文章 http://www.jianshu.com/p/c128ed5c394e 中已经介绍了“自动化Mock系统0.9版本”,今天我将和大家一起探讨我...

903
来自专栏Phoenix的Android之旅

其实热修复就这么简单

上几篇内容介绍了Java的ClassLoader和相关的知识点,总的来说 · Java加载class逻辑是双亲委托模式 · 对于不在class path中的cl...

651
来自专栏Java架构师学习

深度剖析Swagger原理swagger简介

swagger确实是个好东西,可以跟据业务代码自动生成相关的api接口文档,尤其用于restful风格中的项目,开发人员几乎可以不用专门去维护rest api,...

1322
来自专栏张善友的专栏

Castle Core 4.0.0 alpha001发布

    时隔一年多以后Castle 项目又开始活跃,最近刚发布了Castle Core 4.0.0 的alpha版本, https://github.com/c...

2085
来自专栏ImportSource

消费者驱动的微服务契约测试套件Spring Cloud Contract

在微服务架构下,你的服务可能由不同的团队提供和维护,在这种情况下,接口的开发和维护可能会带来一些问题,比如服务端调整架构或接口调整而对消费者不透明,导致接口调用...

33012
来自专栏美团技术团队

美团外卖Android Lint代码检查实践

3175
来自专栏orientlu

BLE 广播格式定义

低功耗蓝牙两类报文 : 广播报文 和 数据报文。 本文讨论广播报文数据段,不包括完整报文其他部分,比如前导,接入地址等

812
来自专栏Android群英传

Andromeda:适用于多进程架构的组件通信框架(上)

962

扫码关注云+社区