通过JNDI访问Jboss中配置的DataSource

2014/10/24 11:07
阅读数 984

一直困惑于以下几个问题:

  1. 在客户端通过JNDI获得的究竟是原对象引用,还是序列化之后的另一个对象,或者是像RMI中的stub?

  2. 在本地访问JNDI和远程访问JNDI有什么区别?

  3. web.xml中的resource-ref元素到底有什么用,并且应该如何使用?

 

花了一些时间,做了一些试验,得了一些结果,消了一些困惑,当然还写了一些博客。本文将以Jboss 5和MySQL为例,先在Jboss 5 中配置好MySQL的DataSource,再通过远程调试方式启动Jboss,另外写了一个很小的web程序和一个独立的main函数来查看 DataSource的可访问性。本文假设在Jboss中配置MySQL DataSource的jndi-name=MySqlDs。

 

以调试模式启动Jboss时,需要将 ${Jboss 根目录}/bin/run.sh文件中加入以下调试参数:

 

JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n $JAVA_OPTS"

 

此时,Jboss的调试监听端口为8787,在IDE中(比如IntelliJ IDEA或Eclipse)中,开启调试,并将监听端口也设成8787,此后便可以调试你的应用程序了。

 

(一)在服务器外部(另一个JVM)中访问DataSource

笔者已经在另一篇博客中谈到了在服务器外部访问DataSource的情况,这里再做一个小结:在服务器外部是可以访问DataSource的,但是在Jboss中配置MySQL时,需要在mysql-ds.xml文件中加入:

 

   <use-java-context>false</use-java-context>


这样告诉Jboss,将DataSource绑定在Global名字空间,表示该资源可以被服务器外部所访问。如果use-java- context=true,那么表示DataSource被绑定在了java:名字空间,此时的DataSource只能被服务器内部访问,即部署在 Jboss中的web应用才可以访问。

 

笔者写了一个独立于服务器的小程序,通过JNDI访问到Jboss中的DataSource,通过调试,发现通过context.lookup("MySqlDs")所获得的对象事实上并不是一个DataSource对象,而是一个JRMPInvokerProxy对象,该对象实现了Serializable接口,所以从远程访问JNDI时,返回的是一个序列化之后的JRMPInvokerProxy对象,而不是DataSource对象本身。另外,JRMP为Java Remote Method Protocol(Java远程方法协议)的简称,主要用于查找和引用远程对象,比如RMI便工作于JRMP的上层。而通过 JRMPInvokerProxy的名字可以看出,客户端实际上使用了Java的动态代理来完成对DataSource的访问。

 

(二)在服务器内部访问DataSource

在服务器内部,可以通过两种方式访问DataSource,一种和外部访问时一样,给出DataSource在JNDI中的原配置名称 (MySqlDs)即可;另一种也是通过JNDI的方式,但是通过额外地配置web.xml文件和jboss-web.xml文件来完成,这样只是多加了 一个新的逻辑层,当然访问DataSource的代码也略有差别。

 

(1)在服务器内部直接访问DataSource

在(一)中我们讲到,如果要在服务器外部访问DataSource,需要将DataSource绑定在Global空间下。在服务器内部是没有这样 的限制的,我们既可以将use-java-context设置成false(Global名字空间),也可以将其设置成true(java:名字空间), 只是他们的JNDI全名称不一样。对于配置在Global名字空间中的DataSource,访问时和服务器外部访问一样,直接用jndi-name就行 了:context.lookup("MySqlDs")。而当DataSource被配置在java:名字空间中时,我们则需要额外加上java:前 缀,即context.lookup("java:/MySqlDs")。

 

除了上面的不同外,配置在Global名字空间和配置在java:名字空间的DataSource访问原理也不一样。当把DataSource配置在Global名字空间时,通过lookup获得的DataSource实际上为一个WrapperDataSource对象,WrapperDataSource类实现了Referenceable接口。通过该接口,WrapperDataSource对象维持了一个到真正DataSource的引用,关于此更多的资料请参考这个网页

 

当把DataSource配置到java:名字空间时,虽然通过lookup获得的DataSource也是一个 WrapperDataSource对象,但是它并不维持到任何对象的引用,此时WrapperDataSource对象可以说就是配置的 DataDource本身。原因很简单,既然是在同一个JVM中访问,既然java:名字空间中的JNDI资源本来就是用于服务器内如使用的,那直接指向 配置的DataSource对象不就行了。

 

(2)通过配置web.xml和jboss-web.xml访问DataSource

上文中我们在调用lookup方法时,直接使用了DataSource的JNDI名称。这种方式不好的地方在于:如果之后我们需要修改 DataSource的名字,比如我们需要从MySQL迁移到Ocracl,那么DataSource的名称自然应该从MySqlDs改为 OrcacleDs,此时我们就需要在程序中修改DataSource的名字,然后重新部署程序。为了解决这个问题,我们可以在中间引入一个逻辑层,通过 该逻辑层,我们不需要修改程序,只需要修改web.xml和jboss-web.xml(在其它服务器中应该也有相应配置文件,比如GlassFish中 的sun-web.xml),此时服务器会自动监视到这些配置文件的修改,然后做相应的配置变化处理。这样,我们便省去了重新部署应用程序的必要。另外, 通过引入这个逻辑层,我们也可将程序的开发和部署分离开。

 

通过配置web.xml和 jboss-web.xml访问DataSource的基本原理为:现在jboss-web.xml中为DataSource指定一个逻辑名称,再在 web.xml文件中引入该逻辑名称,以使该逻辑名称对应用程序可见。这样在程序中调用lookup方法时,只需要使用这个逻辑名称即可。

 

具体方法如下:

先配置jboss-web.xml文件,和web.xml文件一样,该文件应该位于WEB-INF目录下。

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

<jboss-web>

<resource-ref>

  <res-ref-name>MySqlDS-WhateverName</res-ref-name>

  <res-type>javax.sql.DataSource</res-type>

  <jndi-name>java:/MySqlDS</jndi-name>

</resource-ref>

</jboss-web>

 

以上配置中,res-ref-name即为DataSource的逻辑名,我们可以任意给定。jndi-name为DataSource在mysql-ds.xml中配置时的JNDI名称。

 

再配置web.xml文件,在web.xml文件中加入以下配置:

<resource-ref>

  <description>MySQL Connection</description>

  <res-ref-name>MySqlDS-WhateverName</res-ref-name>

  <res-type>javax.sql.DataSource</res-type>

  <res-auth>Container</res-auth>

</resource-ref>

 

这里需要注意的是,web.xml文件中的res-ref-name应该和jboss.xml文件中的res-ref-name保持一致。

 

根据Java EE标准,服务器会为每个组件(比如某个Servlet,某个EJB等)创建一个属于它自己的Environment Naming Context (ENC),这个ENV本身也被放在服务器JNDI资源中。然后将该组件使用的JNDI资源放在这个ENC下。通过上面对web.xml和jboss- web.xml的配置,我们实际上是将DataSource资源放在Servlet容器组件中,即将DataSource的逻辑名放在了Servlet的 ENV中,并对每个Servlet组件可见。这样在程序中访问DataSource时,我们不仅可以通过直接访问DataSource在配置时的JNDI 名,还可以先获得ENV,再从ENV中访问DataSource,代码如下:

 

Context envContext  = (Context)ctx.lookup("java:/comp/env");

DataSource dataSource = (DataSource)envContext.lookup("MySqlDS-WhateverName");

 

以上程序中,先获得ENV,ENV的JNDI名字始终为java:/comp/env,比如在Servlet中调用 ctx.lookup("java:/comp/env")时,获得的是该Servlet的ENV,如果在EJB中调用相同的 ctx.lookup("java:/comp/env"),则获得了该EJB组件的ENV,关于ENV的更多信息,请参考此文档

获得ENV之后,我们便可以访问DataSource了,请注意,此时我们只需要给出DataSource在web.xml中配置的逻辑名(res-ref-name)即可,使用其它的名字,比如MySqlDs和java:/MySQLDs,反而会出错。

 

(三)总结

 

对于服务器外部访问DataSource,请参考表1:


对于在服务器内部访问DataSource,请参考表2:


在服务器内部通过配置web.xml和jboss-web.xml方式访问DataSource,请参考表3:


展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部