会话bean(session bean),可以分为无状态会话bean和有状态会话bean
负责与客户端交互,是编写业务逻辑的地方,在会话bean中可以通过jdbc直接操作数据库,但大多数情况下都是通过实体bean来完成对数据库的操作.
平常,我们使用最多的是无状态bean,因为它的bean实例可供多个用户使用,所以它的性能比有状态bean高.正因为一个bean实例被多个用户使用.那么,前一个用户设置的值有可能被后一个用户所修改,所以它无法正确保存某个用户设置的值,因此是无状态的.
有状态bean平常使用的并不多,因为它的一个bean实例只供一个用户使用,所以性能开销比较大,正因为它的实例只被一个用户使用,用户为它设置的值是不会被其他用户修改,所以可以正确保存用户设置的值,因此是有状态的.
如何开发一个无状态会话bean?1.可以在jboss安装路径的client目录下找到,通常会把client目录下的所有jar文件添加到项目的类路径下.
2.开发接口和实现类:
public String sayHello(String name);
}
@Remote(HelloWorld.class)
public class HelloWorldBean implements HelloWorld {
@Override
public String sayHello(String name) {
return name+"说:你好啊!";
}
}
public class EJBClient {
public static void main(String[] args) {
try {
InitialContext ctx = new InitialContext();
HelloWorld helloworld = (HelloWorld) ctx
.lookup("HelloWorldBean/remote");//参数里面的是JNDI名称
System.out.println(helloworld.sayHello("张三"));
} catch (NamingException e) {
System.out.println(e.getMessage());
}
}
}
重要的几点:
1.在进行JNDI查找前,我们必须设置应用服务器的上下文信息,主要是设置JNDI驱动的类名(java.naming.factory.initial)和命名服务提供者的URL (java.naming.provider.url) 。
如同数据库一样,根据访问命名服务器的不同,为上下文设置的驱动类和URL也是不同的
创建InitialContext对象时如果没有指定Properties参数,InitialContext还会在classpath下寻找jndi.properties文件,并从该文件中加载应用服务器的上下文信息。这样避免了硬编码为InitialContext设置Properties参数。
jndi.properties的配置如下:(这个是用作为jboss应用服务器的上下文信息)
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
2.当EJB发布到Jboss 时,如果我们没有为它指定全局JNDI名称或修改过其默认EJB名称,Jboss就会按照默认的命名规则为EJB生成全局JNDI名称,默认的命名规则如下:
如果把EJB作为模块打包进后缀为*.ear的JAVA EE企业应用文件,默认的全局JNDI名称是
EAR-FILE-BASE-NAME为ear文件的名称,EJB-CLASS-NAME为EJB的非限定类名。
例:把HelloWorld应用作为EJB模块打包进名为HelloWorld.ear的企业应用文件,它的远程接口的JNDI 名称是:HelloWorld/HelloWorldBean/remote
如果把EJB应用打包成后缀为*.jar的模块文件,默认的全局JNDI名称是
例:把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>
具有Local接口的Session bean首先了解一下通过远程接口调用ejb的过程,首先客户端需要与ejb建立起socket通信,在通信管道上他们之间需要来回发送IIOP协议消息,因为数据要在网络进行传输,存放数据的java对象必须要进行序列化。
在这个过程中我们看到,有网络通信的开销、协议解析的开销、对象序列化的开销。因为ejb是分布式技术,它允许客户端与ejb应用在不同一机器上面,所以这些性能开销也是必然的。但是在实际生产中,不可避免存在这种情况:客户端与EJB应用运行在同一个jboss中。这时候客户端访问ejb是否有必要走上面的网络通信呢?据我们所知,这时候客户端与ejb是在同一个jvm内,他们之间完全可以通过内存进行交互,这样就可以避免网络通信的性能开销。既然我们都想到了这一点,EJB专家组也想到了这一点,所以引入了本地接口。通过本地接口调用ejb,直接在内存中交互,这样就能避免因网络通信所造成的各种性能开销。但是有一点,大家必须注意,只有客户端与EJB应用在同一个JVM内运行的时候,我们才能调用本地接口,否则只能调用远程接口。谈到这里,有同学会问什么情况下客户端与EJB应用是在同一个JVM?简单地说只要客户端与ejb发布在同一个jboss内,我们就认为他们是在同一个JVM
@Stateless
@Local ({HelloWorldLocal.class})
public class HelloWorldBean implements HelloWorldLocal {
public String say (String name) {
return name +"说:你好!世界,这是我的第一个EJB3哦.";
}
}
如何访问具有本地接口的会话bean呢?新建一个web项目,在jsp里面进行访问 ,然后打好war包,把打好的war包放到jboss的default/deploy目录下面,这样确保客户端与EJB应用运行在同一个jboss中
<%@ page import="cn.itcast.ejb3.HelloWorldLocal, javax.naming.*, java.util.Properties"%>
<%
try {
InitialContext ctx = new InitialContext();
HelloWorldLocal helloworld = (HelloWorldLocal) ctx.lookup("HelloWorldBean/local");
out.println(helloworld.sayHello("佛山人"));
} catch (NamingException e) {
out.println(e.getMessage());
}
%>
然后访问这个jsp页面即可
一般做应用的时候是既做一个本地接口,也做一个远程接口如果客户端应用与ejb部署在同一个jboss内的话,会优先应用本地接口,否则用远程接口.