导读
因为JBOSS6,7版本在EJB的本地调用和远程调用的写法上不同于之前的版本。所以在本篇文章中将给出代码实例,仅供参考。
环境
MyEclipse10
JBOSS7
理论知识
远程调用(Remote Access)
过程
远程客户端
运行在跟EJB不同的机器或不同的JVM进程上
它可以是Web组件(如:JSP、Servlet)、应用客户端或其他的EJB
对客户端来说,EJB的位置时透明的。
为了创建一个能够被远程客户端访问的EJB,你必须用@Remote注解来定义这些EJB。
参数传递方式
传值
原因:
因为客户端和服务端属于不同的进程,所以内存空间不能共享。
客户端的User对象经过序列化和反序列化之后,变成了服务端的另一个User对象,当服务端对User对象的某个属性进行修改后,客户端的User对象的该属性是没有变化的。在后面的代码中我们会体现这一点。
注意事项
传递的参数如果是对象的话,需要实现序列化的接口implements Serializable
本地调用
过程
本地客户端
与EJB运行在同一个JVM进程上
它可以是Web组件(如:JSP、Servlet)或其他的EJB
对客户端来说,EJB的位置时透明的。
为了创建一个能够被远程客户端访问的EJB,你必须用@Local注解来定义这些EJB。
参数传递方式
传址
原因:传递的是对象的引用,不论是客户端或者是服务端对对象进行修改,都修改的是同一份。
代码
EJB的接口和实现
不同于之前的JBOSS版本,这里的EJB不能同时被声明为@Remote和@Local
项目目录
User.java(一定要实现Serializable接口)
package com.tgb.ejb;
import java.io.Serializable;
@SuppressWarnings("serial")
public class User implements Serializable {
private String username;
private int id;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
UserManagerRemote.java(远程调用接口)
package com.tgb.ejb;
public interface UserManagerRemote {
public void addUser(User user);
}
UserManagerLocal.java(本地调用接口)
package com.tgb.ejb;
public interface UserManagerLocal {
public void addUser(User user);
}
UserManagerBean.java(实现)
package com.tgb.ejb;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;
@Stateless
@Local(UserManagerLocal.class)
@Remote(UserManagerRemote.class)
public class UserManagerBean implements UserManagerRemote,UserManagerLocal {
@Override
public void addUser(User user) {
System.out.println("user.username="+user.getUsername());
user.setId(9);
}
}
写完之后记得要部署到JBOSS,并运行JBOSS。
远程调用
常见一个JavaProject
具体步骤参考上一篇博文【EJB系列】(一)——JBOSS7中开发一个简单的EJB应用
结果
尽管服务端执行了setId(9),但是客户端getId()得到的仍是0。
本地调用
创建Web Project
编写并配置Servlet
UserServlet.java
package com.tgb.web;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.tgb.ejb.User;
import com.tgb.ejb.UserManagerLocal;
@SuppressWarnings("serial")
public class UserServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}
web.xml
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.tgb.web.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/servlet/UserServlet</url-pattern>
</servlet-mapping>
部署项目访问Servlet
我的项目的访问连接时http://localhost:8091/ejb_03_webclient/servlet/UserServlet
项目的端口号要根据JBOSS的配置文件standalone.xml来
打包jar文件到lib目录下
将EJB项目“ejb_03”的四个java文件导出成jar包并且添加到此项目的WEB-INF的lib路径下
编写客户端
final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES,"org.jboss.ejb.client.naming");//让JNDI API知道是由谁来管理我们用来查找JNDI 名字的命名空间的。
Context context;
try {
context = new InitialContext(jndiProperties);
//appName 和 moduleName分别就打包的格式而定
//如果是.ear就是appName,其它的是moduleName(.jar,.war)
final String appName = "";
final String moduleName = "ejb_03_webclient";
final String distinctName = "";
//实现类名
final String beanName = "UserManagerBean";
System.out.println(beanName);
//接口类名
final String viewClassName = UserManagerLocal.class.getName();
System.out.println(viewClassName);
String jndi = "java:module/" + beanName + "!" + viewClassName;
System.out.println(jndi);
UserManagerLocal userManagerLocal;
userManagerLocal = (UserManagerLocal) context.lookup(jndi);
User user=new User();
user.setUsername("许晨阳");
userManagerLocal.addUser(user);
System.out.println("用户id:"+user.getId());
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
运行JBOSS
通过控制台我们可以看到我们部署后的EJB在JNDI中的路径,上面的代码中我们用的是其中的一个路径,剩余的这两个也可以:
java:global/ejb_03_webclient/UserManagerBean!com.tgb.ejb.UserManagerLocal
java:app/ejb_03_webclient/UserManagerBean!com.tgb.ejb.UserManagerLocal
我们看到最后输入的用户id为9,说明服务端的改变,用户端可以看到,说明操作的是同一个User对象。
总结
在这个过程中遇到的问题,包括:
1.本地调用时不需要引入jboss-client.jar,也不需要配置jboss-ejb-client.properties,但是要引入ejb_03.jar,而且要放到lib目录中,否则会报ClassDefNotFound,ClassCastException等错误。
2.另一个就是context.lookup时传递的名称。
远程调用的时候是:
String jndi = "ejb:" + appName + "/" + moduleName + "/"
+ distinctName + "/" + beanName + "!" + viewClassName;
本地调用的时候:
去掉前面的”ejb:”。换成“java:global”或“java:app”或“java:module”,但是要注意moduleName为ejb_03_webclient而不是“ejb_03”。具体路径的拼接,参考我们控制台输出的那三条路径,稍有不同。
参考
JNDI Reference
[AS7.1.1] EJB JNDI Lookup confusion: remote vs local