前面我们讲了ResourceLink,web.xml中的resource-ref,今天我们继续钻研一下web.xml,看看还有啥与JNDI系统相关的东西。
在我们编写web.xml的时候,在web.xml中还有这样的一个配置:
<resource-ref> <description>Employees Database for HR Applications</description> <res-ref-name>jdbc/EmployeeDB</res-ref-name> <res-ref-type>javax.sql.DataSource</res-ref-type> <res-auth>Container</res-auth> </resource-ref> <resource-env-ref> <resource-env-ref-name>jms/StockQueue</resource-env-ref-name> <resource-env-type>javax.jms.Queue</resource-env-ref-type> </resource-env-ref>
不禁要问,还有一个resource-env-ref,和resource-ref区分不开,这个resource-env-ref是干啥的?
JAVA EE规范写的很抽象,resource-env-ref叫做管理对象引用,resource-ref叫做资源工厂对象引用,不知道你们是否能够理解,我觉得一般人都理解不了这两个概念:

我们可以通过Tomcat的源码,细致的分析一下这两个名词;
首先需要着重说的一点是,resource-ref前面我们已经讲过,Tomcat的web.xml中的resource-ref不起什么作用,可以说是被Tomcat自身实现掩盖了,resource-env-ref也是一样的,这两块的功能与Tomcat的自身功能实现重合了。
但是,仅仅是Tomcat这么实现的,其它应用服务器未必是这一思路,所以,我们还需要刨根问底的看看这两个标签的意义,通过Tomcat的源码可以出来窥见端倪。
先来说说resource-ref,其引用的就是Tomcat的StandardServer和StandardContext这两个级别定义的Resource,其流程前面已经讲过,在客户端进行lookup的时候,会通过NamingManager.getObjectInstance方法,路由到ResourceFactory(ObjectFactory)对数据源进行创建,我们回过头来再看看代码:

在ResourceFactory中,会根据Reference的类型,再找对应的对象工厂去创建。
从上述的代码中,我们可以发现,当是javax.sql.DataSource的时候,说明当前的引用的是一个数据源。
那么,这里就从当前的环境变量中查找对应的DataSourceFactory,在Tomcat中刚好有一个DBCP的工厂:

ResourceFactory将数据源对象的构造交给了DBCP的BasicDataSourceFactory来构造,配置文件定义的一大串关于数据源的配置这下也有了用处了;
总结一下,可以总结为resource-ref引用的资源都是通过资源工厂进行创建的JNDI资源引用,这也印证了上面的JAVA EE规范中的说明,只要有resource-ref,对象的创建必然通过资源工厂来创建。
我们再来看看resource-env-ref的流程,以前面定义的JMS的QUEUE为例,它也需要在Tomcat的StandardServer或者StandardContext这两个级别定义Resource,Resource定义的是消息队列的JMS的queue的队列。
那我们在web.xml中通过resource-env-ref去引用这个对象,实质在JNDI树节点创建的对象是ResourceEnvRef,它实际上就是Reference的子类,二者几乎没有任何差别:

不同的一点就是ObjectFactory工厂不同,我们来看看最后NamingManager.getObjectInstance使用的是哪个工厂:

可以看到ResourceEnvFactory啥也没有实现。
到这里,我们就明白了,resource-env-ref关联的直接就是对应的Resource资源,其并没有通过什么工厂创建一说,也就是说Queue的引用应该随着JMS队列的启动,直接就绑定在Tomcat的JNDI树上了,你通过resource-env-ref引用的就是这个Queue,无需工厂创建什么ObjectFactory转化之类的(虽然Tomcat流程也有什么工厂,但这些啥都没实现,就相当于没有一样);
关于这一点,我通过源码分析得到了这样的结论,我觉得应该说的很清楚了,国外的论坛里有很多哥们也叽叽歪歪的议论了一大堆,我也把他们的说法粘贴下来,实质说的就是我这个意思:
resource-ref is for "resource manager connection factory" objects that can give you connections to a resource manager. The typical example is for javax.sql.DataSource from which you can get JDBC connections (javax.sql.Connection).
The resource-ref is so that you can have a 'logical name' used in code -- that the deployer then binds to the *actual* resource factory configured into the server. So when you think of resource-ref, think of "Programmer's made-up/fake name for the datasource." Since the programmer may not know, at the time of coding, what the *real* name is going to be (and since the component model demands that the name of these things is something that can be configured at deploy-time without changing code.)
So... what's the deal with resource-env-ref?
1) It is new to EJB 2.0
2) It was added because of message-driven beans/JMS
3) It has the WORST name, designed, I'm certain, just to confuse you.
For example, a resource manager connection factory is certainly an entry in the bean's environment -- so it would be perfectly logical to assume that resource-env-ref is an appropriate name for what is actually resource-ref.
But...you have to memorize it differently.
A resource-env-ref is for "administered objects associated with resources", and the most obvious example (and the reason it was added) was for JMS destinations. The idea is the same as resource-ref (or security-role-ref) in that it stands for "programmer's made-up/fake name", only instead of a name for a resource factory, it is a name for a 'resource'.
The main difference:
resource-ref:
things that give you CONNECTIONS (including URL connection - JavaMail connection factory, JDBC connection -- DataSource, and -- for the really confusing one -- QueueConnectionFactory for obtaining JMS connections.)
resource-env-ref:
things that give you access to a resource, but which are NOT factories that give you connections. In other words, with resource-ref you are getting something that you then can use to GET to a resource, but with resource-env-ref, you get right to the resource without going through a factory.
The way I think of it, for the purposes of the exam is:
resource-ref: "Programmer's made-up name for the database driver"
[just a way to remember, not the exact technically correct way to say it]
resource-env-ref: "Programmer's made-up name for the JMS destination"
From what I've heard, the resource-env-ref really should have just been called JMS-resource-ref, but that would have been more limiting. Much clearer, though.
The only resource example used in the spec is for JMS destination, and although I'm sure there may be other examples, I have no idea what they might be.
=======================
So, yes its confusing, but hopefully you can simply "burn-in" the differences between the two. I think the most important thing to remember is that wherever you see 'ref' you can think of "Programmer's made-up / lookup name"
.....
最后值得说的一个是,其实web.xml中还有一个不是资源引用,是能直接往JNDI树上绑东西,这个标签就是

这个可以看到env-entry-value就是绑定的JNDI节点的值,当然类型也仅仅支持标准类型,这里的源码分析就不再缀余了,没什么可说的,前面我们分析的太多了,都类似,只不过这里加一个类型判断而已。
总结:
本文讲解了web.xml中的resource-env-ref和resource-ref的实质区别,resource-env-ref引用的是直接的资源对象,说白了,也就是通过JNDI资源查找之后不会经过什么ObjectFactory(ResourceFactory)工厂进行创建,一般引用的类似于JMS queue这种,而resource-ref引用的资源对象是需要ObjectFactory(ResourceFactory)工厂进行创建的,类似数据源这些,javamail这些;
因此,从JAVA EE规范的角度来讲,resource-ref引用的叫做资源工厂对象,resource-env-ref引用的叫做管理对象;
但实质,二者都是绑定在JNDI树的一个javaBean对象而已(载有信息体的一个Reference对象),其只有在lookup的时候,才对资源引用进行获取;
Tomcat的实现弱化了这两个标签的功能,但作为JAVA EE应用建议在web.xml中加上这两个标签,只要目的是为了可移植;
说白了,无论是Tomcat自定义的一些Resource标签,还是web.xml中的env-entry,再或者自己手动将资源往JNDI树上绑,都是一样的,你只需要看树上有啥就查啥,没有就查不到,从这一点也印证了为什么商用应用服务器中,能有一棵展示出来的JNDI树的重要性有多有用啦!




