Enterprice JavaBeans是一个用于分布式业务应用的标准服务端组件模型。采用Enterprice JavaBeans架构编写的应用是可伸缩的、事务性的、多用户安全的。

采用ejb编写的这些应用,可以部署在任何支持Enterprice JavaBeans规范的服务器平台,如jboss、weblogic等。


EJB实际上是用于编写业务层代码。我们知道一个基于MVC结构的应用应包含显示层、控制层和业务层, 假设我们采用ejb开发基于MVC结构的应用,那么ejb就是用于开发应用的业务层.

EJB最大的特点是分布式,EJBs都是业务层的东西,对于多个显示层可以做到业务层完全不用改变。我们写的EJBs发布到EJB容器比如JBOSS中,客户端(普通java应用或者JavaWeb应用)通过JNDI找到你的EJB,这中间通过socket通信,为了降低这种消耗,可以定义本地的EJB,但是这个EJB必须和你的客户端在同一个JVM中,即一个JBoss中,比如你的JavaWeb发布到这个JBoss中。


EJB为我们提供了很多在企业开发中需要使用到的服务,如事务管理/安全/持久化/分布式等,因为这些服务由容器提供,无需我们自行开发,这样大大减少了我们的开发工作量.另外EJB提供了强制式分层解耦设计方法

EJB3.0入门_xml

EJB3.0入门_java_02

EJB3.0运行环境

EJB3.0应用需要运行在JDK1.5以上版本。

进入http://java.sun.com/javase/downloads/index_jdk5.jsp下载JDK。在页面中找到JDK5.0 Update 16(版本在不断更新中,有可能大于16),点击右边的Download。

按照安装向导提示安装,安装路径选择C:\Java\jdk1.5.0_16。Jdk安装完后,接着问你是否安装jre,也一起安装上。

右键点击“我的电脑”->“属性”->“高级”->“环境变量”,在“系统变量”里添加JAVA_HOME变量,值为JDK的安装路径,如:C:\Java\jdk1.5.0_16。

在“系统变量”里再添加CLASSPATH变量,值为:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;

在系统变量栏找到变量名为Path的选项,点“编辑”在变量值的末尾添加;%JAVA_HOME%\bin;


EJB3.0应用需要运行在EJB容器里,下面是一些JavaEE应用服务器,JavaEE应用服务器包含Web容器和EJB容器。

l Jboss ( 4.2.x 以上版本)

 是一个成熟的开源的准JavaEE应用服务器,在开源JavaEE应用服务器中所占市场份额第一。如果你打算选用开源的JavaEE应用服务器,那么jboss是最值得选择的。

l Glassfish

 是一个开源的JavaEE应用服务器,对JavaEE规范的支持非常好,其运行性能比较高。因为发展时间相对较短,市场占有率比较低。另外,它能否提供稳定的运行性能,还有待时间的考验。

l Weblogic ( 10 以上版本)

 是商业市场占有率第一的商业JavaEE应用服务器,它具有出色的稳定性,并提供了人性化的管理界面,还有企业需要使用到的众多功能。

l Sun ApplicationServer ( 9.0 以上版本)

 商业JavaEE应用服务器,如果你打算选用商业应用服务器运行ejb3,这个是不错的选择。

l Oracle ApplicationServer ( 10g 以上版本)

 商业JavaEE应用服务器,如果你的数据库是oracle,要想兼容性更好,这个是不错的选择。

l apusic 应用服务器

 这是国内的商业JavaEE应用服务器,主要在政府中占有一定的市场份额。但是其向开发人员提供的文档实在太少了。

注意:Tomcat目前只是Web容器,它不能运行EJB应用。

进入http://labs.jboss.com/jbossas/downloads/下载页面,选择jboss4.2.3.GA下载(大小为95MB),如果你安装的jdk是1.5版本,请选择jboss-4.2.3.GA.zip文件下载。如果你安装的jdk是1.6版本,请选择jboss-4.2.3.GA-jdk6.zip文件下载。下载后直接解压缩文件即可完成安装,为了避免应用出现莫名的错误,解压缩的路径最好不要带有空格和中文,如“Program Files”。


进入jboss的bin目录,找到启动脚本run.bat。双击run.bat即可启动jboss,此时启动的配置项为default。

       如果启动出错,请检查下列情况:

  1.端口是否被占用,如:8080、1099

  2是否为JDK设置的JAVA_HOME和ClassPath系统变量,如果没有,请按照前面视频介绍的方法设置。

  3.检查jboss所用的jdk是否1.5以上版本,这些信息可以在jboss的控制台观察到。

       4.如果启动时出现这样的错误:“findstr不是内部或外部命令,也不是可运行的程序或批处理文件”。那么应该在系统变量Path中追加“%SystemRoot%\system32;%SystemRoot%;”。

      

        最后的办法是重装机器上的 JDK ,祝你好运。


在“系统变量”里添加 JBOSS_HOME变量,值为Jboss的安装路径,如:D:\JavaEEServer\jboss。

l 为了方便输入 jboss 的命令,我们把 jboss 的 bin 目录添加到系统路径里。 在系统变量一栏找到变量名为 Path 的选项,点“编辑”在变量值的末尾添加: ;%JBOSS_HOME%\bin;

开发EJB依赖的jar文件

 可以在jboss安装路径的client目录下找到,通常会把client目录下的所有jar文件添加到项目的类路径下.


EJB中的三种bean


1.会话bean(session bean)

  负责与客户端交互,是编写业务逻辑的地方,在会话bean中可以通过jdbc直接操作数据库,但大多数情况下都是通过实体bean来完成对数据库的操作.

2.实体bean(entity bean)

  它实际上属于java持久化规范(简称JPA)里的技术,JPA的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate、TopLink等ORM框架各自为营的局面。

3.消息驱动bean(message-driven bean)

  它是专门用于异步处理java消息的组件.具有处理大量并发消息的能力.

两种会话bean


• 无状态会话 bean

 平常,我们使用最多的是无状态bean,因为它的bean实例可供多个用户使用,所以它的性能比有状态bean高.正因为一个bean实例被多个用户使用.那么,前一个用户设置的值有可能被后一个用户所修改,所以它无法正确保存某个用户设置的值,因此是无状态的.无状态bean使用实例池技术管理bean

• 有状态会话 bean

有状态bean平常使用的并不多,因为它的一个bean实例只供一个用户使用,所以性能开销比较大,正因为它的实例只被一个用户使用,用户为它设置的值是不会被其他用户修改,所以可以正确保存用户设置的值,因此是有状态的.有状态bean使用激活(activation)管理bean


            每个用户都有自己的Bean实例,相当于有多少个用户就有多少个Bean实例为之服务。如果不采取任何机制,这样必然会导致服务器资源严重消耗,最后落个down机罢工。为了解决这个问题,EJB引入了激活机制。该机制实现的原理是这样的,在EJB服务器需要节省资源时,就从内存中收回bean实例,将其所保持的会话状态序列化到硬盘中,并且释放其所占有的内存。若此时客户端对EJB再次发起请求,EJB容器会重新实例化一个Bean实例,并从硬盘中将之前的状态恢复。


客户端编程

public static void main(String[] args) {

  Properties props = new Properties();

  props.setProperty("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");//与jboss进行通讯

  props.setProperty("java.naming.provider.url","localhost:1099");//设置命名服务器的url,jndi通过后半部分就知道jboss所在的地址和使用的端口号

try {

  InitialContextctx =newInitialContext(props);

  HelloWorldhelloworld = (HelloWorld)ctx.lookup("HelloWorldBean/remote");

  System.out.println(helloworld.sayHello("佛山"));

} catch (NamingException e) {

  System.out.println(e.getMessage());

}

}

出现硬编码了,解决:创建一个jndi.properties,内容如下:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory

java.naming.provider.url=localhost:1099

这样设置后就不要以下几行了

  Properties props = new Properties();

 

  props.setProperty("java.naming.provider.url","localhost:1099");


EJB3.0入门_应用服务器_03

EJB3.0入门_java_04

EJB3.0入门_java_05

EJB3.0入门_xml_06

设置JNDI访问环境信息

在进行JNDI查找前,我们必须设置应用服务器的上下文信息,主要是设置JNDI驱动的类名(java.naming.factory.initial)和命名服务提供者的URL(java.naming.provider.url) 。

l java.naming.factory.initial 或 Context.INITIAL_CONTEXT_FACTORY :环境属性名,用于指定 InitialContext 工厂(作者称它为 JNDI 驱动更容易理解),它类似于 JDBC 指定数据库驱动类。

因为本例子连接的是JbossNS(命名服务的实现者),所以使用Jboss提供的驱动类:org.jnp.interfaces.NamingContextFactory

l java.naming.provider.url 或 Context.PROVIDER_URL :环境属性名,包含提供命名服务的主机地址和端口号。它类似于 JDBC 指定数据库的连接 URL 。连接到 JbossNS 的 URL 格式为: jnp://host:port ,该 URL 的“ jnp: ”部分是指使用的协议, JBoss 使用的是基于 Socket/RMI 的协议。 host 为主机的地址, port 为 JNDI 服务的端口。除了 host 之外,其他部分都是可以不写的。

l

下面是数据库访问例子:

Class.forName("org.gjt.mm.mysql.Driver").newInstance();

Properties props = new Properties();

props.put("user","root");

props.put("password","123456");

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/itcast", props);


如同数据库一样,根据访问命名服务器的不同,为上下文设置的驱动类和URL也是不同的,如下面是访问Sun应用服务器的上下文信息:

Properties props = new Properties();

props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");

props.setProperty("java.naming.provider.url", "localhost:3700");

InitialContext = newInitialContext(props);

HelloWorldhelloworld = (HelloWorld)ctx.lookup("com.foshanshop.ejb3.HelloWorld");

如果客户端运行在应用服务器内,我们不需要为InitialContext设置应用服务器的上下文信息,也不建议设置。因为应用服务器启动时会把JNDI驱动类等上下文信息添加进系统属性,创建InitialContext对象时如果没有指定Properties参数,InitialContext内部会调用System.getProperty()方法从系统属性里获取必要的上下文信息。对本例子而言,你可以省略传入props参数,之所以给InitialContext设置参数,目的是引出相关知识点,便于教学。在实际应用中,如果给InitialContext设置了参数,反而会带来不可移植的问题。

注:创建InitialContext对象时如果没有指定Properties参数,InitialContext还会在classpath下寻找jndi.properties文件,并从该文件中加载应用服务器的上下文信息。这样避免了硬编码为InitialContext设置Properties参数。

jndi.properties的配置如下:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory

java.naming.provider.url=localhost:1099

java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces

当EJB发布到Jboss时,如果我们没有为它指定全局JNDI名称或修改过其默认EJB名称,Jboss就会按照默认的命名规则为EJB生成全局JNDI名称,默认的命名规则如下:

如果把EJB作为模块打包进后缀为*.ear的JAVA EE企业应用文件,默认的全局JNDI名称是

l 本地接口: EAR-FILE-BASE-NAME/EJB-CLASS-NAME/local

l 远程接口: EAR-FILE-BASE-NAME/EJB-CLASS-NAME/remote

      EAR-FILE-BASE-NAME为ear文件的名称,EJB-CLASS-NAME为EJB的非限定类名。

例:把HelloWorld应用作为EJB模块打包进名为HelloWorld.ear的企业应用文件,它的远程接口的JNDI名称是:HelloWorld/HelloWorldBean/remote

如果把EJB应用打包成后缀为*.jar的模块文件,默认的全局JNDI名称是

l 本地接口: EJB-CLASS-NAME/local

l 远程接口: EJB-CLASS-NAME/remote

例:把HelloWorld应用打包成HelloWorld.jar文件,它的远程接口的JNDI名称是:HelloWorldBean/remote

使用注释改变会话bean的JNDI名

如果我们没有指定EJB的JNDI名称,当EJB发布到应用服务器时,应用服务器会按默认规则为EJB生成全局JNDI名称。当我们需要自定义JNDI名称时,可以这样做

如果EJB在Jboss中使用,可以使用Jboss提供的@LocalBinding和@RemoteBinding注释,@LocalBinding注释指定Session Bean的Local接口的JNDI名称,@RemoteBinding注释指定Session Bean的Remote接口的JNDI名称,如下:

@Stateless

@Remote ({Operation.class})

@RemoteBinding (jndiBinding="foshanshop/RemoteOperation")

@Local ({LocalOperation.class})

@LocalBinding (jndiBinding="foshanshop/LocalOperation")

public class OperationBean implements Operation,LocalOperation {

}



使用xml部署描述文件改变SessionBean的JNDI名称

由于JNDI名称与厂商有关,如果使用注释定义JNDI名称会带来移植问题,因此建议使用ejb-jar.xml部署描述文件进行定义,该文件必须放置在jar的META-INF目录下

<?xml version="1.0"encoding="UTF-8"?>

<ejb-jar xmlns=​​http://java.sun.com/xml/ns/javaee​​ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

                         http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"  version="3.0">

<enterprise-beans>

 <session>

   <ejb-name>HelloWorldBean</ejb-name>

   <mapped-name>HelloWorldBean</mapped-name>

 </session>

</enterprise-beans>

</ejb-jar>

ejb-name为EJB名称,mapped-name为bean的JNDI名称。目前jboss不支持在ejb-jar.xml通过mapped-name指定JNDI名称,但我们可以使用他专属的部署描述文件jboss.xml进行定义,如下:

<?xml version="1.0"encoding="UTF-8" ?>

<!DOCTYPE jboss PUBLIC "-//JBoss//DTDJBOSS 4.2//EN“ "http://www.jboss.org/j2ee/dtd/jboss_4_2.dtd">

<jboss><enterprise-beans> <session>

     <ejb-name>HelloWorldBean</ejb-name>

    <jndi-name>hello/remote</jndi-name>

</session></enterprise-beans></jboss>

本地接口的bean

@Local ({HelloWorldLocal.class})

EJB3.0入门_xml_07

EJB的调用机制


由于EJB的调用过程对开发者来说是透明的,以至于我们错误地认为:lookup()方法返回的对象就是bean实例。实际上,客户端与Session bean交互,它并不直接与Bean实例打交道,而是经由bean的远程或本地接口。当你调用远程或本地接口的方法时,接口使用的是存根(stub)对象。该存根实现了session bean的远程或本地接口。它负责将方法调用经过网络发送到远程EJB容器,或将请求路由到位于本地JVM内的EJB容器。存根是在部署期间使用JDK所带的java.lang.reflect.Proxy动态生成。利用RMI、远程代理实现远程调用。

EJB3.0入门_xml_08


第一步:客户端调用远程接口的SayHello()方法(实际上调用的是存根对象的SayHello()方法,该存根实现了HelloWorld的远程接口)。

第二步:方法调用经过IIOPRuntime被转换成CORBA IIOP消息发往应用服务器。

第三步:应用服务器接收到请求消息后,交由骨架(skeleton)处理。骨架通过IIOP Runtime解析协议消息,并根据协议要求调用bean实例的SayHello()方法。

第四步:骨架(skeleton)将SayHello()方法的返回值经过IIOPRuntime转换成CORBA IIOP应答消息发回客户端。

第五步:存根对象通过IIOP Runtime将CORBAIIOP应答消息解析成返回值。


EJB中的依赖注入


注入ejb:

@Stateless

@Remote (Injection.class)

publicclassInjectionBeanimplementsInjection {

       @EJB(beanName="HelloBean")LocalHellohelloworld;

       publicStringSayHello() {

           returnhelloworld.SayHello("注入者");

       }

}

注入数据源:

@Resource(mappedName = "java:/DefaultMySqlDS")DataSourcemyDb;

        Connection conn = null;

        try {

            conn =myDb.getConnection();

            Statementstmt = conn.createStatement();

            ResultSetrs =stmt.executeQuery("SELECTstudentNameFROM student");

            if (rs.next()) str =rs.getString(1); rs.close();

            stmt.close();

        } catch (SQLException e) {

            e.printStackTrace();

        }finally{

            try {

               if(null!=conn && !conn.isClosed())conn.close();

            } catch (SQLException e) {

               e.printStackTrace();

            }

         }


配置数据源



<?xml version="1.0"encoding="UTF-8"?>

<datasources>

  <local-tx-datasource>

    <jndi-name>DefaultMySqlDS</jndi-name>

    <connection-url>jdbc:mysql://localhost:3306/itcast?useUnicode=true&amp;characterEncoding=UTF-8</connection-url>

   <driver-class>org.gjt.mm.mysql.Driver</driver-class>

   <user-name>root</user-name>

   <password>123456</password>

   <min-pool-size>3</min-pool-size>

   <max-pool-size>100</max-pool-size>

  <!--

     TRANSACTION_READ_UNCOMMITTED

     TRANSACTION_READ_COMMITTED

     TRANSACTION_REPEATABLE_READ

     TRANSACTION_SERIALIZABLE

   

   <transaction-isolation>TRANSACTION_SERIALIZABLE</transaction-isolation>-->

  <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>

    <metadata>

      <type-mapping>mySQL</type-mapping>

    </metadata>

  </local-tx-datasource>

</datasources>

实体Bean

它属于java持久化规范(简称JPA)里的技术,实体bean通过元数据在javabean和数据库表之间建立起映射关系,然后Java程序员就可以随心所欲的使用面向对象的编程思想来操纵数据库。 JPA的出现主要是为了简化现有的持久化开发工作和整合ORM技术,目前实现的JPA规范的主流产品有Hibernate、TopLink和OpenJPA,在jboss中采用了Hibernate作为其持久化实现产品。

添加JPA的配置文件persistence.xml

根据JPA规范的要求:在实体bean应用中,我们需要在应用的类路径下的META-INF目录加入持久化配置文件persistence.xml

<?xml version="1.0"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"version="1.0">

 

</persistence>

 <propertyname="hibernate.hbm2ddl.auto" value="create-drop"/>

<!-- 显示最终执行的SQL -->

<property name="hibernate.show_sql" value="true"/>

<!-- 格式化显示的SQL -->

<property name="hibernate.format_sql" value="true"/>


JMS(Java消息服务)


Java 消息服务(Java Message Service,简称JMS)是用于访问企业消息系统的开发商中立的API。企业消息系统可以协助应用软件通过网络进行消息交互。

JMS的编程过程很简单,概括为:应用程序A发送一条消息到消息服务器的某个目得地(Destination),然后消息服务器把消息转发给应用程序B。因为应用程序A和应用程序B没有直接的代码关连,所以两者实现了解偶。如下图:

EJB3.0入门_应用服务器_09


消息传递系统的中心就是消息。一条Message由三个部分组成:

头(header),属性(property)和主体(body)。

消息有下面几种类型,他们都是派生自Message接口。

StreamMessage:一种主体中包含Java基元值流的消息。其填充和读取均按顺序进行。

MapMessage:一种主体中包含一组名-值对的消息。没有定义条目顺序。

TextMessage:一种主体中包含Java字符串的消息(例如,XML消息)。

ObjectMessage:一种主体中包含序列化Java对象的消息。

BytesMessage:一种主体中包含连续字节流的消息。

JMS 支持两种消息传递模型:点对点(point-to-point,简称PTP)和发布/订阅(publish/subscribe,简称pub/sub)。这两种消息传递模型非常相似,但有以下区别:

PTP 消息传递模型规定了一条消息只能传递给一个接收方。采用javax.jms.Queue表示。

Pub/sub 消息传递模型允许一条消息传递给多个接收方。采用javax.jms.Topic表示

这两种模型都通过扩展公用基类来实现。例如:javax.jms.Queue和javax.jms.Topic都扩展自javax.jms.Destination类。

P2P

EJB3.0入门_java_10

P/S

EJB3.0入门_java_11

配置目标位置

开始JMS编程前,我们需要先配置消息到达的目标地址(Destination),因为只有目标地址存在了,我们才能发送消息到这个地址。由于每个应用服务器关于目标地址的配置方式都有所不同,下面以jboss为例,配置一个queue类型的目标地址。

<?xml version="1.0"encoding="UTF-8"?>

<server> 

  <mbean code="org.jboss.mq.server.jmx.Queue"

         name="jboss.mq.destination:service=Queue,name=foshanshop">

    <attributename="JNDIName">queue/foshanshop</attribute>  

    <dependsoptional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>

  </mbean>

</server>

Jboss使用一个XML文件配置队列地址,文件的取名格式应遵守*-service.xml

<attribute name="JNDIName">属性指定了该目标地址的全局JNDI名称。如果你不指定JNDIName属性,jboss会为你生成一个默认的全局JNDI,其名称由“queue”+“/”+目标地址名称组成。另外在任何队列或主题被部署之前,应用服务器必须先部署Destination Manager Mbean,所以我们通过<depends>节点声明这一依赖。

发送消息

一般发送消息有以下步骤:

(1) 得到一个JNDI初始化上下文(Context)

  InitialContextctx = newInitialContext();

(2) 根据上下文查找一个连接工厂QueueConnectionFactory。该连接工厂是由JMS提供的,不需我们自己创建,每个厂商都为它绑定了一个全局JNDI,我们通过它的全局JNDI便可获取它;

  QueueConnectionFactoryfactory = (QueueConnectionFactory)ctx.lookup("QueueConnectionFactory");

(3) 从连接工厂得到一个连接QueueConnection

  conn =factory.createQueueConnection();

(4) 通过连接来建立一个会话(Session);

  session = conn.createQueueSession(false,QueueSession.AUTO_ACKNOWLEDGE);

  这句代码意思是:建立不需要事务的并且能自动确认消息已接收的会话。

(5) 查找目标地址:

  例子对应代码:Destinationdestination = (Destination )ctx.lookup("queue/foshanshop");

(6) 根据会话以及目标地址来建立消息生产者MessageProducer(QueueSender和TopicPublisher都扩展自MessageProducer接口)

例子对应代码:

MessageProducerproducer =session.createProducer(destination);

TextMessagemsg =session.createTextMessage("您好,这是我的第一个消息驱动Bean");

producer.send(msg);

采用消息驱动Bean接收消息



消息驱动Bean(MDB)是设计用来专门处理基于消息请求的组件。它和无状态Session Bean一样也使用了实例池技术,容器可以使用一定数量的bean实例并发处理成百上千个JMS消息。正因为MDB具有处理大量并发消息的能力,所以非常适合应用在一些消息网关产品。如果一个业务执行的时间很长,而执行结果无需实时向用户反馈时,也很适合使用MDB。如订单成功后给用户发送一封电子邮件或发送一条短信等。

一个MDB通常要实现MessageListener接口,该接口定义了onMessage()方法。Bean通过它来处理收到的JMS消息。

package javax.jms;

public interface MessageListener {

    public void onMessage(Messagemessage);

}

当容器检测到bean守候的目标地址有消息到达时,容器调用onMessage()方法,将消息作为参数传入MDB。MDB在onMessage()中决定如何处理该消息。你可以使用注释指定MDB监听哪一个目标地址(Destination)。当MDB部署时,容器将读取其中的配置信息。

@MessageDriven(activationConfig =

{

  @ActivationConfigProperty(propertyName="destinationType",

    propertyValue="javax.jms.Queue"),

  @ActivationConfigProperty(propertyName="destination",

    propertyValue="queue/foshanshop"),

  @ActivationConfigProperty(propertyName="acknowledgeMode",

    propertyValue="Auto-acknowledge")

})

public class PrintBean implementsMessageListener {

    public void onMessage(Messagemsg) {

WebService服务


Web服务也是一种分布式技术,它与EJB最大的不同是,Web服务属于行业规范,可以跨平台及语言。而EJB属于java平台的规范,尽管理论上可以跨平台,但实现起来比较复杂,所以其应用范围局限在了java平台。看上去两者好像是互相竞争的关系,其实不是。它们两者的偏重点不一样,Web服务偏重的是这个系统对外提供什么功能,而EJB偏重的是如何使用一个个组件组装这些功能。就好比一个硬盘,它对外提供的是存储服务,这是web服务的关注点,对于怎样组装这个硬盘,怎样构造这些小零件,web服务并不关心,但这些却是EJB所关注的。

JavaEE为web service提供了两种不同的编程模型:EJB容器模型及Web容器模型,这里将以最新的JAX-WS2.x规范(Java API for XML-based Web Services)介绍webservice的开发。

@WebService(targetNamespace="http://ws.foshanshop.com",

  name = "HelloWorld",

  serviceName = "HelloWorldService")

客户端


开发步骤如下:

1.  在应用的类路径下放入JAX-WS的jar文件(下载路径:https://jax-ws.dev.java.net)。如果你使用的是JDK6,这一步可以省略,因为JDK6已经绑定了JAX-WS。目前JDK6绑定的JAX-WS版本是2.0。这意味着,当某些应用使用的JAX-WS版本高于2.0时,就有可能发生版本问题,这时,你需要升级JDK6中的JAX-WS版本,方法如下:

下载最高版本的JAX-WS,在产品lib目录中找到jaxws-api.jar和jaxb-api.jar,把这两个文件copy到JDK6_HOME/jre/lib/endorsed目录下。

      

  如果有同学使用开发工具是myeclipse,这时应该把myeclipse所使用的jdk更改为你系统安装的jdk。

2.  利用Web Service客户端生成工具生成辅助类。

3.  第三步:借助辅助类调用web service。


这里我们使用wsimport工具的Ant任务类生成辅助类

<?xml version="1.0"encoding="UTF-8"?>

<project name="WSClient" default="wsclientgen"basedir=".">

   <propertyname="src.dir" value="${basedir}/src" />

   <path id="build.classpath"description="设置类路径">

        <fileset dir="${basedir}/lib">

            <includename="*.jar"/>

        </fileset>

    </path>

    <targetname="wsclientgen"description="生成webservice客户端辅助代码,执行后请刷新项目">

      <taskdef name="wsimport"classname="com.sun.tools.ws.ant.WsImport"classpathref="build.classpath"/>

      <wsimportwsdl="http://localhost:8080/WsHelloWorld/HelloWorldBean?wsdl"

      sourcedestdir="${src.dir}" package="com.foshanshop.ws.client"keep="true"

      verbose="true" destdir="${basedir}/bin"/>

    </target>

</project>




把jboss集成进eclipse中

EJB3.0入门_应用服务器_12

EJB3.0入门_应用服务器_13

EJB3.0入门_xml_14

EJB3.0入门_java_15

EJB3.0入门_java_16

EJB3.0入门_java_17


通过ANT提高EJB应用的开发效率

    对应用进行编译打包发布

build.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<!--.表示同一目录 ..表示在上一级目录-->

<project name="HelloWorld" basedir=".">
<property name="src.dir" value="${basedir}\src"/><!--指定项目源文件所在的路径 -->
<property environment="env"/><!--指定到系统变量 -->
<property name="jboss.home" value="${env.JBOSS_HOME}"/><!--指定jboss的安装目录 -->
<property name="jboss.server.config" value="default"/><!--jboss的配置项,default默认配置-->
<property name="build.dir" value="${basedir}\build"/><!--class存放的目录-->

<!--jar路径-->
<path id="build.classpath">
<fileset dir="${jboss.home}\client">
<include name="*.jar"/><!--包含client下所有jar文件-->
</fileset>
<pathelement location="${build.dir}"/><!--定位class文件所在的目录-->
</path>

<target name="prepare" >
<delete dir="${build.dir}"/><!--如果有就删除build目录-->
<mkdir dir="${build.dir}"/><!--创建build目录-->
</target>


<target name="compile" depends="prepare" description="编译"><!--depends表示依赖前面的工作-->
<javac srcdir="${src.dir}" destdir="${build.dir}" includeantruntime="on">
<classpath refid="build.classpath"/><!--引入jar、class文件-->
</javac>
</target>

<target name="ejbjar" depends="compile" description="创建ejb发布包">
<jar jarfile="${basedir}\${ant.project.name}.jar">
<fileset dir="${build.dir}">
<include name="**/*.class"/>
</fileset>

<!--把persistence.xml打包到META-INF目录下-->

<metainf dir="${src.dir}\META-INF"></metainf>
</jar>
</target>

<target name="deploy" depends="ejbjar" description="ejb发布">
<copy file="${basedir}\${ant.project.name}.jar" todir="${jboss.home}\server\${jboss.server.config}\deploy"/>
</target>

<target name="undeploy" description="卸载ejb">
<delete file="${jboss.home}\server\${jboss.server.config}\deploy\${ant.project.name}.jar"/>
</target>
</project>


EJB3.0入门_java_18

EJB3.0入门_java_19

创建ejb发布包

EJB3.0入门_java_20

EJB3.0入门_xml_21

EJB3.0入门_应用服务器_22

创建完后启动jboss

EJB3.0入门_java_23

发布

EJB3.0入门_java_24

EJB3.0入门_java_25

卸载ejb

EJB3.0入门_java_26

EJB3.0入门_xml_27




开发具有本地接口的无状态bean

EJB3.0入门_xml_28

EJB3.0入门_应用服务器_29

EJB3.0入门_应用服务器_30

EJB3.0入门_应用服务器_31

出现错误:

Could not obtain connection to any of these urls: localhost:1099 and discovery failed with error: javax.naming.CommunicationException: Receive timed out [Root exception is java.net.SocketTimeoutException: Receive timed out]

需要客户端和ejb部署在同一个jboss中


考虑到大部分客户端应用都是javaweb,在这建立web项目作为ejb的客户端并部署到jboss中

EJB3.0入门_应用服务器_32

Test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="javax.naming.*,cn.itcast.ejb3.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<%
try {
InitialContext ctx =new InitialContext();
HelloWorld helloworld = (HelloWorld)ctx.lookup("HelloWorldBean/local");
out.println(helloworld.sayHello("本地人"));//代理对象
} catch (Exception e) {
out.println(e.getMessage());
}
%>
</body>
</html>

把项目导出成一个war包

EJB3.0入门_xml_33

拷贝到jboss的发布目录D:\jboss-4.2.3.GA\server\default\deploy

控制台输出:

EJB3.0入门_应用服务器_34

EJB3.0入门_应用服务器_35