首页 > 代码库 > EJB和Web容器中的资源或组件是如何查找的?

EJB和Web容器中的资源或组件是如何查找的?

JavaEE中资源或组件是如何通过JNDI查找的一直都没完全弄清楚,这段时间花时间总算把它弄清楚了,总结如下:

(a).JavaEE1.5以前:

ServletA.java:

InitialContext ic=new InitialContext();
DataSource ds=(DataSource)ic.lookup("java:comp/env/jdbc/FooDS");
web.xml:

<resource-ref>
    <res-ref-name>jdbc/FooDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
</resource-ref>
sun-web.xml:
<resource-ref>
    <res-ref-name>jdbc/FooDS</res-ref-name>
    <jndi-name>jdbc/OracleDS</jndi-name>
</resource-ref>
其中sun-web.xml是厂商特定的配置文件,不是JavaEE规范要求的,主要的作用是将容器中需要用到的资源或组件与应用服务器定义的资源或组件关联,这样的配置文件包括:
GlassFish&Sun AppServer: sun-web.xml  sun-ejb-jar.xml  sun-application-client.xml

<res-ref-name> is the name of a component dependency  and is relative to java:comp/env
<jndi-name>      is the name of a physical AppServer resource and is relative to the root of the AppServer‘s global namespace

(b).从JavaEE 5开始可以使用Annotations:
@Resource(name="jdbc/FooDS")
private DataSource ds;
等同于web.xml或ejb-jar.xml文件中的如下定义
<resource-ref>
    <res-ref-name>jdbc/FooDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <injection-target>
        <injection-target-class>com.acme.ServletA</injection-target-class>
        <injection-target-name>ds</injection-target-name>
    </injection-target>
</resource-ref>

其中name属性的默认值为:
FIELD: <class-name>/<field-name>
METHOD: <class-name>/<setter-property-name>
TYPE:  No defaulting. name() is required.
例如:

@Resource 
private DataSource ds;
等同于:
@Resource(name="com.acme.ServletA/ds")
private DataSource ds;

(c).可以使用mapped-name直接关联到在应用服务器上注册的资源或组件的全局名

// TYPE-level @Resource in Servlet
@Resource(name="jdbc/FooDS", type=javax.sql.DataSource.class, mappedName="jdbc/OracleDS")
public class ServletA{ ... }
等同于:
<resource-ref>
    <res-ref-name>jdbc/FooDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <mapped-name>jdbc/OracleDS</mapped-name>
</resource-ref>

使用这个功能的问题如下:
(1)不是JavaEE5规范要求必须实现的功能
(2)没有统一的格式和语法,各厂商可以自己定义
(3)可以被优先级更高的配置覆盖,annotation < standard.xml < sun-*.xml

也可以使用mapped-name发布资源或组件在应用服务器上的全局名
FooBean.java:
@Stateless(mappedName="ejb/MyFooBean")
public class FooBean implements Foo{
public String hello() { return "hello,world!/n"; }
}
MyAppClient.java:
@EJB(mappedName="ejb/MyFooBean")
private static Foo foo;

(d).对于没有使用mapped-name,同时也没有在厂商特定的配置文件中设置关联的资源或组件,应用服务器采用如下规则:
(1).Session beans & ejb-refs
    jndi-name 自动设置成 Home/Business接口名
(2).@Resource, resource-ref, message-destination-ref, etc.
    jndi-name 自动设置成资源的 java:comp/env名

例如:
@Resource(name="jdbc/FooDS")
private DataSource ds;
等同于sun-web.xml:
<resource-ref>
    <res-ref-name>jdbc/FooDS</res-ref-name>
    <jndi-name>jdbc/FooDS</jndi-name>
</resource-ref>

@Stateless(name="FooBean")
public class FooBean implements Foo{ .. }
等同于sun-ejb-jar.xml:
<ejb>
    <ejb-name>FooBean</ejb-name>
    <jndi-name>com.acme.Foo</jndi-name>
</ejb>


@EJB(name="ejb/Foo")
private static Foo foo;
等同于sun-application-client.xml:

<ejb-ref>
    <ejb-ref-name>ejb/Foo</ejb-ref-name>
    <jndi-name>com.acme.Foo</jndi-name>
</ejb-ref>


(e).采用Global JNDI

 java:global[/<app-name>]/<module-name>/<bean-name>!<fully-qualified-interface-name>
<bean-name> corresponds to the session bean‘s EJB name.  It defaults to the unqualified name of the session bean class.  
It can be explicitly specified using the name attribute of the @Stateless/@Stateful/@Singleton annotation.  

Alternatively, if the ejb-jar.xml is being used to define the component, <bean-name> corresponds to the <ejb-name> element of ejb-jar.xml.


(f).如果同一个资源或组件用Annotation,javaEE规范定义的配置文件,厂商特定的配置文件中都定义了,那到底用哪个一般遵循如下优先顺序:

Annotation < javaEE规范定义的配置文件 < 厂商特定的配置文件

例如glassfish中:

@Resouce <  <resource-ref> in web.xml<  <resource-ref> in sun-web.xml

glassfish, weblogic, websphere都遵循上面的顺序,只不过厂商特定的配置文件不同而已。



参考文档:
https://glassfish.java.net/javaee5/ejb/EJB_FAQ.html
https://glassfish.java.net/javaee5/ejb/compdependencies_xmlforum_nov15.pdf

EJB和Web容器中的资源或组件是如何查找的?