在OECP(Open Enterprise Components Plateform,开放的企业级组件平台)项目的架构体系中,各业务组件需要有运行在不同的EJB容器中的能力,在组件层构建起一个业务处理的分布式集群环境。
我们知道在客户端我们需要通过JNDI来调用EJB容器中的session bean,在EJB3中,获得JNDI上下文主要有两种方式。(默认采用jboss的获取方式)
1、通过程序编码的方式
public static InitialContext getInitialContext() throws NamingException{
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
return new InitialContext(p);
}
客户端通过InitialContext ctx = getInitialContext();的方式来获取上下文。
2、通过配置属性文件的方式
在应用的src下新建一个jndi.properties的资源文件(注意文件名必须是jndi.properties)
内容:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost
客户端只要通过下面的方法就能获得上下文对象了
InitialContext ctx = new InitialContext();
为了方便在程序开发的过程中的测试,我们一般情况下会封装一个获得JNDI上下文的工具类来使用。比如我们构建的一个如下的EJBFinder.java的类:
public class EJBFinder {
public static InitialContext getInitialContext() throws NamingException{
Properties p = new Properties();
p.put("server", "jboss");
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES,
"org.jboss.naming:org.jpn.interfaces");
p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
InitialContext ctx = new InitialContext(p);
return ctx;
}
public static Object findEJB(String ejbname) throws NamingException{
return getInitialContext().lookup(ejbname);
}
}
这样的一个工具类给我们测试EJB组件提供了很大的方便,不用我们再去重复的去构建JNDI上下文的环境。但是当EJB组件开发完成后,正式部署到正式环境中,客户端调用EJB组件怎样去调用呢?现在我们分析这其中出现的问题。
1、 客户端不在同一个JVM环境中,应该怎样处理?
2、 Session Bean对于客户端是隐藏的,开发客户端的程序员怎么能非常方便的获得Session Bean的绑定名?怎么能方便的得到JNDI上下文呢?
3、 各EJB组件分别部署在不同的服务器上,我们怎么能方便的调用?
4、 各EJB组件如果运行在不同的EJB容器中,我们怎么能方便的得到对应的JNDI上下文环境?
针对这些问题,我们逐步的去分析解决。
客户端不在同一个JVM中这是普遍出现的情况,我们可能将web客户端运行在Tomcat上,而EJB运行在JBoss容器中,对于这种情况,我们如果采用上面介绍的第一种情况来获得JNDI上下文就非常不可行,因为EJB的环境的变化会导致太多程序代码的改动,为日后的维护埋下了隐患。所以只能采用第二种方式,将JNDI上下文信息配置在属性文件中,即使以后EJB的环境更换,也可以通过修改配置文件来实现这种改变。
通过JNDI的方式来调用EJB的Session Bean,必然少不了为Session Bean绑定JNDI名。默认的情况下(JBoss),容器会以实现类的类名作为JNDI名。比如getIntialContext().lookup(“LoginBean/remote”); LoginBean就是Login接口的实现类的名字。
@Stateless
@Remote(Login.class)
public class LoginBean implements Login{}
如果这样去做的话,我们开发客户端的程序员就必须要知道这个Session Bean接口的实现类是什么名字,这不是一种好的方式,违背了我们隐藏实现的编程思想。才开始大家建议,约定Session Bean的命名格式为接口+Bean后缀,这是一个比较可行的解决方案。但是一个约定好的命名格式,只能给我们的是一个已知的字符串,我们利用这个字符串所能做的事情太少了。我们利用@Stateless中的name属性,可以为Session Bean起个别名,这个东西应该很好的利用起来。我们再一次讨论,对于客户端来说,什么是透明的呢?无疑是接口,我们能不能把接口的名字作为Session Bean的别名呢。这个主意不错,大家一致通过,看了网上的一些例子,好多人也是采用这种方式。但是也有不好的方面,如果一个Session Bean实现多个接口就不是很好处理了。我们只能再次约定,一个Session Bean只能对应着一个远程接口。这样我们只知道接口就能方便的调用EJB了。
但问题又接踵而至,我们使用EJB作为组件开发技术,很大程度上是利用它强大的分布式计算的能力,这就要求每个EJB组件有可能运行在不同的服务器上,我们调用这些EJB组件,只采用一套上下文配置的方式是非常不可行的。我们需要为每个组件配置各自的JNDI上下文环境。怎么去做呢?在客户端我们为每个组件建立一个Properties文件,里面包含了该组件的JNDI配置。我们调用不同的组件时,我们就去获取该组件的JNDI配置,这已经很好的解决了相关的问题,完全可以适应EJB分布式的环境,但是有没有更好的方法让用户不再去选择每个组件的配置,让他们感觉和本地调用一样简单呢。于是我们在Session Bean接口上做了一些文章,通过自定义annotation的方式,来为每个接口标志相关的组件信息,通过程序解析annotation来获得组件的信息来进行相关的处理。我们定义一个@ComponentInfo的元数据来标注组件的相关信息。
@Target( { TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentInfo {
String name();
String description() default "";
}
name属性为组件的名字,description顾名思义就是组件的描述,通过作用在每个接口上的这个annotation,我们就可以获得组件名称。我们约定客户端的JNDI配置的属性文件的文件名要和这里的组件名称对应,这样我们就可以自动找到该组件需要的JNDI配置。
为了方便EJB组件的开发,我们可以为每个EJB组件做一个全局的接口。将该annotation作用在最上层父类的TYPE上,而该组件所有的Session Bean接口都继承这个接口。
问题都解决了吗?我们不断地寻找问题。我们在这里不得不对我们尊敬的SUN一口恶骂,骂它做事情什么时候能做的彻底,为什么不统一标准,让所有的EJB容器都采用同样格式的JNDI获取方式。现在问题来了,不同的容器存在自己特有的lookup实现方式,太郁闷了,这样就要将EJB组件和容器绑死了,这为以后EJB组件的移植设置了巨大的障碍。我们思考着,这个问题似曾相识,对,用工厂模式来解决这种移植性的问题,对不同的实现通过工厂的方式进行集中管理,从而方便系统的移植。说干就干,我们建立一个公共的接口:
public interface JNDIFinder {
public Object lookup(InitialContext ctx, Class clazz)throws NamingException;
}
我们为每个容器建立自己的JNDI查找实现类:JbossJNDIFinderImpl.java,WeblogicJNDIFinderImpl.java
然后再建立JNDIFinder的工厂来进行管理和获取:JNDIFactory.java
public static JNDIFinder getFinder(String server) {
if (JBOSS_SERVER.equals(server)) {
return new JbossJNDIFinderImpl();
} else if (WEBLOGIC_SERVER.equals(server)) {
return new WeblogicJNDIFinderImpl();
}
return new JbossJNDIFinderImpl();// 默认返回jboss的实现
}
而getFinder(String server)中的server我们可以将它配置到我们的JNDI配置文件中:
server=jboss
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jpn.interfaces
java.naming.provider.url=jnp://localhost:1099
万事俱备只欠东风了,我们最后把这些要统一封装成一个最简单方法来进行调用。我们建立一个工具类JNDIUtil.java,在该类中,我们向外暴露一个静态方法:
public static Object lookup(Class clazz) throws NamingException
大功告成了,现在只需要一个简单的调用Login login =(Login)JNDIUtil.lookup(Login.class); 就解决了上面提出的相关问题,实现了跨EJB服务器的分布式EJB组件的调用。(后面将附上该方案的相关源代码)
当然,这个解决方案还需要经过实际开发的检验,我们也将不断地优化该方案,也希望更多人讨论交流,提出宝贵的意见,目的是为了更好的解决该问题。
原文:
源代码:
分享到:
相关推荐
在JBuilder2007中新建一个动态Web项目,然后创建一个Servlet,通过编写相应的代码来调用Session Bean中的`sayHello()`方法。通过浏览器访问Servlet,如果能正确显示“Hello, 我是LuoDaijun!!”,则表示Session Bean...
`EJBClient`可能是示例中的客户端应用,它负责调用SessionBean的方法。客户端通常需要引用EJB的远程接口,并通过JNDI查找找到Bean的实例。在EJB3.0中,这通常涉及以下步骤: - 导入远程接口。 - 使用`...
在EJB 3中,开发者可以使用注解(@Stateless或@Stateful)轻松地创建和管理这两种类型的Bean。 Entity Bean是EJB中的持久化组件,它们通常映射到数据库中的表,用于处理业务对象的持久化存储。EJB 3引入了JPA(Java...
EJB3中的一个重要特性是它可以跨网络进行分布,这使得应用程序能够利用多台服务器的资源,提高可伸缩性和可用性。 首先,了解EJB3的核心概念是必要的。EJB3包括三种主要类型:Stateless Session Beans(无状态会话...
3. **部署描述符** - 编写ejb-jar.xml文件,其中包含关于EJB的元数据,如Bean的类型、JNDI名称等。 4. **容器配置** - 在应用服务器中配置EJB容器,指定EJB的部署位置和资源。 5. **客户端调用** - 客户端通过JNDI...
在"ejb sessionbean demo"中,我们主要探讨的是如何使用EJB的Session Bean进行开发和演示。Session Bean通常用于实现业务逻辑,它们可以是无状态的,意味着每个请求都会创建一个新的Bean实例,不保留任何先前会话的...
本资料包"基于Java的EJB中有、无状态SessionBean的两个例子.zip"包含的是关于有状态和无状态SessionBean的实例代码,帮助开发者理解和掌握这两种类型SessionBean的用法。 1. **无状态SessionBean(Stateless ...
Java EJB中有、无状态SessionBean的两个例子,的无状态SessionBean可,会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等; 在有状态SessionBean中,用累加...
【ejb入门录像 sessionBean】是针对企业级Java应用(Enterprise JavaBeans,简称EJB)技术的初学者设计的一段教学视频。EJB是Java EE(Java Platform, Enterprise Edition)框架的重要组成部分,它提供了一种标准的...
5. 客户端会话管理:客户端需要通过`@EJB`注解或者JNDI查找获取Bean的引用,并保持这个引用,以便后续调用。 **示例分析** 压缩包中的例子可能包含了两个项目,分别演示了无状态和有状态Session Bean的实现。无状态...
- EJBClient是客户端用来访问和交互Session Bean的模块,通过JNDI查找服务定位到远程bean,并通过Home接口实例化bean,然后通过Remote接口调用其方法。 8. **源码和工具** - 标签提到的"源码"可能指的是开发者...
EJB可以通过JNDI(Java Naming and Directory Interface)查找机制来获取bean的引用,然后调用其方法。在Java SE环境中,可以使用Java EE的API(如EJBContainer)来创建和访问EJB。此外,EJB还可以通过注解(@EJB)的...
- **客户端代码**:展示了如何通过JNDI查找和调用Session Bean的方法。 通过这两个示例,你可以学习如何在实际项目中创建和使用这两种类型的Session Bean,以及它们在处理并发请求和资源管理方面的差异。有状态...
- 创建:当客户端第一次调用Session Bean时,容器会创建一个新的实例。 - 激活:容器会调用Bean的`ejbCreate`方法进行初始化。 - 使用:客户端可以通过接口调用Bean的方法。 - 停用:如果Bean是无状态的,每次...
在EJB中,SessionBean是一种重要的组件,它代表了客户端会话的状态。本压缩包提供了两个关于Java EJB中状态ful(有状态)和stateless(无状态)SessionBean的实例,帮助开发者深入理解这两种类型的Bean。 一、有...
- 客户端代码:展示了如何通过JNDI查找和调用Session Bean。 4. 操作流程 - 编写并配置Bean类和接口。 - 在Java EE应用服务器(如Tomcat、Glassfish、WildFly)中部署ejb-jar文件。 - 客户端通过JNDI查找服务,...
5. 调用Session Bean:在客户端代码中,通过JNDI查找服务并调用bean的方法。 至于“homework8”这个文件,可能是本次学习任务相关的作业或示例代码。它可能包含了一个或多个使用JavaBean和Session Bean的实例,通过...
3. 使用:客户端调用Session Bean的方法,执行业务逻辑。 4. 销毁:当不再需要Session Bean或者服务器资源紧张时,服务器可能会销毁该实例。对于Stateful Session Beans,服务器通常会在会话超时或客户端显式结束...
在有状态和无状态SessionBean中,开发者可以通过设置方法上的`@TransactionAttribute`注解来控制事务行为,如要求方法在一个新的事务中运行或在当前事务中运行。 总的来说,这个压缩包提供了一个了解和学习Java EJB...