21. 依赖注入
当 EJB 需要使用其他 EJB 或资源的时候,必须通过 JNDI 查找或者注入注释来实现,而不能使用 new object 的方式,因为 EJB 实例的创建与销毁都是由容器进行管理的。
22. ENC 注册项
1) EJB 容器有一个自己的内部注册表( internal registry ),称为 Enterprise Naming Context ( ENC )。容器在 ENC 中维护某些外部环境的应用,可以认为 ENC 是容器的私人地址簿。先往 ENC 中登记资源,然后再从 ENC 中获取资源的引用。
2) 要往 ENC 添加引用的注册项,可以通过:
l ejb-jar.xml (用于 EJB 模块)
l web.xml (用于 WEB 模块)
l 注入注释
3) 使用 ejb-jar.xml 添加注册项:
< enterprise-beans >
< session >
< ejb-name > InjectionTestBean </ ejb-name >
< ejb-ref >
< ejb-ref-name > ejb/HelloWorldBean1 </ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< remote > cn.gengv.ejb3ex.HelloWorld </ remote >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-ref >
< ejb-local-ref >
< ejb-ref-name > ejb/HelloWorldBeanL </ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< local > cn.gengv.ejb3ex.HelloWorldLocal </ local >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-local-ref >
</ session >
< session >
< ejb-name > InjectionTestBean3 </ ejb-name >
< ejb-ref >
< ejb-ref-name > ejb/HelloWorldBean3 </ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< remote > cn.gengv.ejb3ex.HelloWorld </ remote >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-ref >
</ session >
</ enterprise-beans >
4) 使用 @EJB 注释添加注册项
@Stateless
@EJB (
name = "ejb/HelloWorldBean2" ,
beanName = "HelloWorldBean" ,
beanInterface = HelloWorldLocal. class
)
public class InjectionTestBean2 implements InjectionTestLocal {
}
5) 对应关系:
单独使用 ejb-jar.xml 和 @EJB 注释都可以在 ENC 中添加注册项。
l @EJB 的 name() ==> <ejb-ref-name>
l @EJB 的 beanName() ==> <ejb-link>
l @EJB 的 beanInstance() ==> <remote> 或 <local>
23. 获取 ENC 中的资源引用
1) 两种途径: JNDI 查找 和 @EJB 注入
2) JNDI 查找:
l 使用 InitialContext 的 lookup() 方法,根据注册项引用名称进行 JNDI 查找;
l <ejb-ref-name> 或 @EJB.name() ,注册的应用名称,都是相对于 java:comp/env 上下文的;例如:
HelloWorld hw = (HelloWorld)ctx.lookup("java:comp/env/ejb/HelloWorldBean1");
l 注意,在不同的 EJB 中调用 lookup , java:comp/env 会被解析为不同的上下文环境。例如在 EJB1 中调用 lookup(“java:comp/env”) 获取的就是 EJB1 的 ENC ;在 EJB2 中调用 lookup(“java:comp/env”) 获取的就是 EJB2 的 ENC 。
3) @EJB 注入:
l 只要给成员变量或者 setter 方法上标注 @EJB 注释, EJB 容器就会在 Bean 实例被创建时,自动往 Bean 的 ENC 中添加一个注册项,同时把 EJB 的引用直接注入成员变变量或 setter 方法中。
l 依赖注入只能用于本地命名服务中,因此不能注入远程服务器对象。
24. @EJB 依赖注入
1) @EJB 的属性:
l name() :表示被引用的 EJB 的 JNDI ENC 注册项名称,该名称相对于 java:comp/env 上下文。
l beanInterface() :指定使用的 Bean 接口。该属性通常用来区分所使用的 EJB 引用是远程还是本地的。如果 @EJB 用在 Bean 类级别,则必须指定该属性;如果用在成员变量或者 setter 方法上时,可以省略。(个人理解,如果用在类级别而不指定该属性,则 @EJB 没有任何依据,来指定何种接口,隐式添加到 ENC 注册项;而在成员变量或方法级别, @EJB 至少可以通过成员变量的类型或 setter 的参数类型,指定该接口类型,隐式添加到 ENC 注册项)
l beanName() :指定被引用的 EJB 的名称,其值与 @Stateless.name() 、 @Stateful.name() 或 ejb-jar.xml 中 <ejb-name> 元素的值相等。
l mappedName() :表示 EJB 的全局 JNDI 名称,而全局 JNDI 名称与容器厂商有关,不利于移植,因此不推荐使用,
2) @EJB 注释工作时,会自动引发容器在 JNDI ENC 中为被注入的元素创建一个注册项。
l 注册项名称:由 @EJB.name() 指定,如果没有指定名称,则容器会根据被注入的成员变量或 setter 方法的名称,设置 ENC 名。默认的 ENC 名称由被注入的类的全限定名和成员变量或方法的基础名组成。例如:
cn.gengv.ejb3ex.impl.InjectionTestBean/hw
l 注册项引用接口:由 @EJB.beanInterface() 指定,如果没有指定,容器会根据被注入成员变量类型或 setter 方法的参数类型来设定。
l 注册项引用的 EJB 名称:由 @EJB.beanName() 指定。
l 综上情况,使用 @EJB 注入而自动产生的注册项应该与如下 xml 设置的效果相同:
< enterprise-beans >
< session >
< ejb-name > InjectionTestBean </ ejb-name >
< ejb-ref >
< ejb-ref-name >
cn.gengv.ejb3ex.impl.InjectionTestBean/hw
</ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< remote >
cn.gengv.ejb3ex.HelloWorld
</ remote >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-ref >
</ session >
</ enterprise-beans >
25. @EJB 注释属性的应用场合
1) 不带任何属性
当接口在 EJB-JAR 内只被一个 Bean 类使用的时候,可以这样引用。
对于 @EJB HelloWorldLocal hw;
l 唯一标识符就是 HelloWorldLocal 接口,应用服务器首先在注入方的 EJB-JAR 中查找使用 HelloWorldLocal 作为本地或远程接口的 EJB 类。如果有多个 EJB 类使用了该接口,则容器会抛出一个部署异常。
l 如果 EJB-JAR 作为 EAR 的一个模块被部署,则容器也会在其他 EJB-JAR 中查找使用了该接口作为本地或远程接口的 EJB 类。同样,如果多于一个,也会引发异常。
l 如果容器没能在 EAR 中找到该 EJB 引用,则它会继续在其他全局 EJB-JAR 中继续寻找。
l 如果指定了 beanName() , JBoss 会遵循相同的过程,但它使用的唯一标示符是 beanName() 的值;如果指定了 mappedName() ,则 JBoss 不会进行任何搜索,而是根据 mappedName 值,直接从全局 JNDI 中获取 EJB 引用。
2) 带 beanName() 属性
l 当业务接口被多个 Bean 类使用时,则必须指定 beanName 。否则,在 @EJB 注入注释工作时,容器无法判断使用哪个 Bean 类名称作为注册项引用 EJB 名称。(相当于无法确定用哪个 Bean 名称作为 <ejb-link> )
l 如果引用的 EJB 不在 InjectionBean 所在的 JAR 文件中,而在 .ear 的其他 JAR ,则可以使用下面形式进行引用:
@EJB(beanName=”zhenxi.jar#HelloBean”) LocalHello helloworld;
3) 带 name() 属性
l 如果业务接口只被一个 Bean 类用作本地或者远程接口,则使用 @EJB(name=”ejb/HelloWorld”) HelloWorld hw 与 @EJB HelloWorld hw 效果一样;不同的是,前者在 ENC 的名称是 java:comp/env/ejb/HelloWorld ;而后者为 cn.gengv.ejb3ex.impl.InjectionTestBean/hw 。
l 如果业务接口被多个 Bean 类使用,要想单单只是用 name() 属性,则必须在 ejb-jar.xml 中配置注册项(在 EJB3.0 中, xml 部署描述文件的优先级高于注释,可以使用 xml 覆盖注释的配置)。如果不想配置 xml ,那就得一并使用 beanName 属性。
【个人观点】
实际上, name() 属性与是否有多个 Bean 类使用同一个业务接口,没多大关系。真正关系到是否一个业务接口被多个 Bean 类使用的,应该是 beanName() 属性。 要想使用注入,隐式地 为 ENC 添加注册项,不论用到或没用到 @EJB 的什么属性,真遇到一个业务接口被多个 Bean 类使用的时候,也照样还得 beanName() 出马来解决判断问题,否则容器不可能知道应该用哪个 Bean 类。
4) 带 mappedName() 属性
l 容器会根据 mappedName() 的值,直接从全局 JNDI 中查找 EJB 引用,容器无需执行像 beanName() 属性那样的搜索规则。
l mappedName() 属性值与厂商有关,可能会带来移植问题,应避免使用。
26. 补充要点
1) 可以将无状态会话 Bean 注入到有状态或无状态会话 Bean 中;也可以将有状态会话 Bean 注入到有状态会话 Bean 中;但不应该将有状态会话 Bean 注入到无状态会话 Bean 中。
2) 如果被注入的 jar 与使用者不在同一个 jar 中,例如使用者在 DependencyInjection.jar 中,而被注入的 EJB 在 HelloWorld.jar 。 JBoss 在启动时,会按默认发布顺序先发布 DependencyInjection.jar ,后发布 HelloWorld.jar 。然而,由于 DependencyInjection.jar 中用到了 HelloWorld.jar 中的 EJB ,所以会抛出找不到类的异常,导致 DependencyInjection.jar 发布失败。解决办法是:
修改 JBoss 的 /server/default/conf/jboss-service.xml 文件,找到
<attribute name="URLComparator">
org.jboss.deployment.DeploymentSorter
</attribute>
<!--
<attribute name="URLComparator">org.jboss.deployment.scanner.PrefixDeploymentSorter
</attribute>
-->
修改成:
<!--
<attribute name="URLComparator">
org.jboss.deployment.DeploymentSorter
</attribute>
-->
<attribute name="URLComparator">org.jboss.deployment.scanner.PrefixDeploymentSorter
</attribute>
然后给 jar 文件编号,格式为 01_XXX.jar ,如 01_HelloWorld.jar 、 02_DependencyInjection.jar , JBoss 会按照编号顺序发布 jar 文件。
27. 资源类型的注入
1) @javax.annotation.Resource 注释用来注入外部资源类型。其工作过程与 @EJB 相同,也是先往 JNDI ENC 添加一个指向资源引用的注册项,然后把资源引用注入到成员变量或者 setter 方法中。
2) @Resource 注释的属性
l name() :用于指定外部资源的 JNDI ENC 名,该名称是相对于 java:comp/env 的。
l type() :用于指定资源对应的 Java 类型。
l mappedName() :指定资源的 JNDI 名,该值与厂商有关。
l shareable() :指定资源是否共享,默认为 true 。
l authenticationType() :指定有谁来负责权限验证,其值可以是 CONTAINER 或 APPLICATION ,默认值为 CONTAINER ,即容器自动执行所需验证工作。如为 APPLICATION ,则 Bean 类需要在使用资源之前自行验证。
28. 注入与继承
1) 被注入到使用者中的 EJB ,可以被使用者的子类继承;
2) 在使用者的子类中,也可以通过重定义成员变量或者重写( override ) setter 方法来更改注入的资源。
3) 应当注意,注入的继承关系,是由类的继承关系产生出来的。
29. 自定义注入注释
可以通过自定义注入注释,并使用拦截器拦截生命周期事件的回调方法(例如 @PostConstruct ),利用 Java 反射机制,来实现注入。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gengv/archive/2009/07/17/4358264.aspx
当 EJB 需要使用其他 EJB 或资源的时候,必须通过 JNDI 查找或者注入注释来实现,而不能使用 new object 的方式,因为 EJB 实例的创建与销毁都是由容器进行管理的。
22. ENC 注册项
1) EJB 容器有一个自己的内部注册表( internal registry ),称为 Enterprise Naming Context ( ENC )。容器在 ENC 中维护某些外部环境的应用,可以认为 ENC 是容器的私人地址簿。先往 ENC 中登记资源,然后再从 ENC 中获取资源的引用。
2) 要往 ENC 添加引用的注册项,可以通过:
l ejb-jar.xml (用于 EJB 模块)
l web.xml (用于 WEB 模块)
l 注入注释
3) 使用 ejb-jar.xml 添加注册项:
< enterprise-beans >
< session >
< ejb-name > InjectionTestBean </ ejb-name >
< ejb-ref >
< ejb-ref-name > ejb/HelloWorldBean1 </ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< remote > cn.gengv.ejb3ex.HelloWorld </ remote >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-ref >
< ejb-local-ref >
< ejb-ref-name > ejb/HelloWorldBeanL </ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< local > cn.gengv.ejb3ex.HelloWorldLocal </ local >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-local-ref >
</ session >
< session >
< ejb-name > InjectionTestBean3 </ ejb-name >
< ejb-ref >
< ejb-ref-name > ejb/HelloWorldBean3 </ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< remote > cn.gengv.ejb3ex.HelloWorld </ remote >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-ref >
</ session >
</ enterprise-beans >
4) 使用 @EJB 注释添加注册项
@Stateless
@EJB (
name = "ejb/HelloWorldBean2" ,
beanName = "HelloWorldBean" ,
beanInterface = HelloWorldLocal. class
)
public class InjectionTestBean2 implements InjectionTestLocal {
}
5) 对应关系:
单独使用 ejb-jar.xml 和 @EJB 注释都可以在 ENC 中添加注册项。
l @EJB 的 name() ==> <ejb-ref-name>
l @EJB 的 beanName() ==> <ejb-link>
l @EJB 的 beanInstance() ==> <remote> 或 <local>
23. 获取 ENC 中的资源引用
1) 两种途径: JNDI 查找 和 @EJB 注入
2) JNDI 查找:
l 使用 InitialContext 的 lookup() 方法,根据注册项引用名称进行 JNDI 查找;
l <ejb-ref-name> 或 @EJB.name() ,注册的应用名称,都是相对于 java:comp/env 上下文的;例如:
HelloWorld hw = (HelloWorld)ctx.lookup("java:comp/env/ejb/HelloWorldBean1");
l 注意,在不同的 EJB 中调用 lookup , java:comp/env 会被解析为不同的上下文环境。例如在 EJB1 中调用 lookup(“java:comp/env”) 获取的就是 EJB1 的 ENC ;在 EJB2 中调用 lookup(“java:comp/env”) 获取的就是 EJB2 的 ENC 。
3) @EJB 注入:
l 只要给成员变量或者 setter 方法上标注 @EJB 注释, EJB 容器就会在 Bean 实例被创建时,自动往 Bean 的 ENC 中添加一个注册项,同时把 EJB 的引用直接注入成员变变量或 setter 方法中。
l 依赖注入只能用于本地命名服务中,因此不能注入远程服务器对象。
24. @EJB 依赖注入
1) @EJB 的属性:
l name() :表示被引用的 EJB 的 JNDI ENC 注册项名称,该名称相对于 java:comp/env 上下文。
l beanInterface() :指定使用的 Bean 接口。该属性通常用来区分所使用的 EJB 引用是远程还是本地的。如果 @EJB 用在 Bean 类级别,则必须指定该属性;如果用在成员变量或者 setter 方法上时,可以省略。(个人理解,如果用在类级别而不指定该属性,则 @EJB 没有任何依据,来指定何种接口,隐式添加到 ENC 注册项;而在成员变量或方法级别, @EJB 至少可以通过成员变量的类型或 setter 的参数类型,指定该接口类型,隐式添加到 ENC 注册项)
l beanName() :指定被引用的 EJB 的名称,其值与 @Stateless.name() 、 @Stateful.name() 或 ejb-jar.xml 中 <ejb-name> 元素的值相等。
l mappedName() :表示 EJB 的全局 JNDI 名称,而全局 JNDI 名称与容器厂商有关,不利于移植,因此不推荐使用,
2) @EJB 注释工作时,会自动引发容器在 JNDI ENC 中为被注入的元素创建一个注册项。
l 注册项名称:由 @EJB.name() 指定,如果没有指定名称,则容器会根据被注入的成员变量或 setter 方法的名称,设置 ENC 名。默认的 ENC 名称由被注入的类的全限定名和成员变量或方法的基础名组成。例如:
cn.gengv.ejb3ex.impl.InjectionTestBean/hw
l 注册项引用接口:由 @EJB.beanInterface() 指定,如果没有指定,容器会根据被注入成员变量类型或 setter 方法的参数类型来设定。
l 注册项引用的 EJB 名称:由 @EJB.beanName() 指定。
l 综上情况,使用 @EJB 注入而自动产生的注册项应该与如下 xml 设置的效果相同:
< enterprise-beans >
< session >
< ejb-name > InjectionTestBean </ ejb-name >
< ejb-ref >
< ejb-ref-name >
cn.gengv.ejb3ex.impl.InjectionTestBean/hw
</ ejb-ref-name >
< ejb-ref-type > Session </ ejb-ref-type >
< remote >
cn.gengv.ejb3ex.HelloWorld
</ remote >
< ejb-link > HelloWorldBean </ ejb-link >
</ ejb-ref >
</ session >
</ enterprise-beans >
25. @EJB 注释属性的应用场合
1) 不带任何属性
当接口在 EJB-JAR 内只被一个 Bean 类使用的时候,可以这样引用。
对于 @EJB HelloWorldLocal hw;
l 唯一标识符就是 HelloWorldLocal 接口,应用服务器首先在注入方的 EJB-JAR 中查找使用 HelloWorldLocal 作为本地或远程接口的 EJB 类。如果有多个 EJB 类使用了该接口,则容器会抛出一个部署异常。
l 如果 EJB-JAR 作为 EAR 的一个模块被部署,则容器也会在其他 EJB-JAR 中查找使用了该接口作为本地或远程接口的 EJB 类。同样,如果多于一个,也会引发异常。
l 如果容器没能在 EAR 中找到该 EJB 引用,则它会继续在其他全局 EJB-JAR 中继续寻找。
l 如果指定了 beanName() , JBoss 会遵循相同的过程,但它使用的唯一标示符是 beanName() 的值;如果指定了 mappedName() ,则 JBoss 不会进行任何搜索,而是根据 mappedName 值,直接从全局 JNDI 中获取 EJB 引用。
2) 带 beanName() 属性
l 当业务接口被多个 Bean 类使用时,则必须指定 beanName 。否则,在 @EJB 注入注释工作时,容器无法判断使用哪个 Bean 类名称作为注册项引用 EJB 名称。(相当于无法确定用哪个 Bean 名称作为 <ejb-link> )
l 如果引用的 EJB 不在 InjectionBean 所在的 JAR 文件中,而在 .ear 的其他 JAR ,则可以使用下面形式进行引用:
@EJB(beanName=”zhenxi.jar#HelloBean”) LocalHello helloworld;
3) 带 name() 属性
l 如果业务接口只被一个 Bean 类用作本地或者远程接口,则使用 @EJB(name=”ejb/HelloWorld”) HelloWorld hw 与 @EJB HelloWorld hw 效果一样;不同的是,前者在 ENC 的名称是 java:comp/env/ejb/HelloWorld ;而后者为 cn.gengv.ejb3ex.impl.InjectionTestBean/hw 。
l 如果业务接口被多个 Bean 类使用,要想单单只是用 name() 属性,则必须在 ejb-jar.xml 中配置注册项(在 EJB3.0 中, xml 部署描述文件的优先级高于注释,可以使用 xml 覆盖注释的配置)。如果不想配置 xml ,那就得一并使用 beanName 属性。
【个人观点】
实际上, name() 属性与是否有多个 Bean 类使用同一个业务接口,没多大关系。真正关系到是否一个业务接口被多个 Bean 类使用的,应该是 beanName() 属性。 要想使用注入,隐式地 为 ENC 添加注册项,不论用到或没用到 @EJB 的什么属性,真遇到一个业务接口被多个 Bean 类使用的时候,也照样还得 beanName() 出马来解决判断问题,否则容器不可能知道应该用哪个 Bean 类。
4) 带 mappedName() 属性
l 容器会根据 mappedName() 的值,直接从全局 JNDI 中查找 EJB 引用,容器无需执行像 beanName() 属性那样的搜索规则。
l mappedName() 属性值与厂商有关,可能会带来移植问题,应避免使用。
26. 补充要点
1) 可以将无状态会话 Bean 注入到有状态或无状态会话 Bean 中;也可以将有状态会话 Bean 注入到有状态会话 Bean 中;但不应该将有状态会话 Bean 注入到无状态会话 Bean 中。
2) 如果被注入的 jar 与使用者不在同一个 jar 中,例如使用者在 DependencyInjection.jar 中,而被注入的 EJB 在 HelloWorld.jar 。 JBoss 在启动时,会按默认发布顺序先发布 DependencyInjection.jar ,后发布 HelloWorld.jar 。然而,由于 DependencyInjection.jar 中用到了 HelloWorld.jar 中的 EJB ,所以会抛出找不到类的异常,导致 DependencyInjection.jar 发布失败。解决办法是:
修改 JBoss 的 /server/default/conf/jboss-service.xml 文件,找到
<attribute name="URLComparator">
org.jboss.deployment.DeploymentSorter
</attribute>
<!--
<attribute name="URLComparator">org.jboss.deployment.scanner.PrefixDeploymentSorter
</attribute>
-->
修改成:
<!--
<attribute name="URLComparator">
org.jboss.deployment.DeploymentSorter
</attribute>
-->
<attribute name="URLComparator">org.jboss.deployment.scanner.PrefixDeploymentSorter
</attribute>
然后给 jar 文件编号,格式为 01_XXX.jar ,如 01_HelloWorld.jar 、 02_DependencyInjection.jar , JBoss 会按照编号顺序发布 jar 文件。
27. 资源类型的注入
1) @javax.annotation.Resource 注释用来注入外部资源类型。其工作过程与 @EJB 相同,也是先往 JNDI ENC 添加一个指向资源引用的注册项,然后把资源引用注入到成员变量或者 setter 方法中。
2) @Resource 注释的属性
l name() :用于指定外部资源的 JNDI ENC 名,该名称是相对于 java:comp/env 的。
l type() :用于指定资源对应的 Java 类型。
l mappedName() :指定资源的 JNDI 名,该值与厂商有关。
l shareable() :指定资源是否共享,默认为 true 。
l authenticationType() :指定有谁来负责权限验证,其值可以是 CONTAINER 或 APPLICATION ,默认值为 CONTAINER ,即容器自动执行所需验证工作。如为 APPLICATION ,则 Bean 类需要在使用资源之前自行验证。
28. 注入与继承
1) 被注入到使用者中的 EJB ,可以被使用者的子类继承;
2) 在使用者的子类中,也可以通过重定义成员变量或者重写( override ) setter 方法来更改注入的资源。
3) 应当注意,注入的继承关系,是由类的继承关系产生出来的。
29. 自定义注入注释
可以通过自定义注入注释,并使用拦截器拦截生命周期事件的回调方法(例如 @PostConstruct ),利用 Java 反射机制,来实现注入。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gengv/archive/2009/07/17/4358264.aspx