JNDI ENC与依赖注入

JNDI ENC与依赖注入
请搞清楚:通过注解还是XML注册资源,通过注入还查找得到资源,注解与XML等价方式。
以下说的是EJB引用资源

每个部署于应用服务器中的EJB容器都拥有一个属于它自己的内部注册表(internal registry),该内部注册表被称为Enterprise Naming Context(ENC),ENC是由JNDI实现的。开发人员可以通过注解或XML为某个EJB 的JNDI ENC(这是一个局部JNDI)定义资源、EJB或外部环境的别名,而后在编写EJB业务逻辑时,通过别名在JNDI中查找这些引用,在EJB3.0中,还可以把这些引用直接注入到bean数据成员中。

JNDI ENC中可以注册哪些内容
指向EJB接口的引用、JMS队列或主题的目标地址、JMS连接工厂、数据源、任何JCA资源、基本的数据类型常量,还有一些java EE服务(javax.ejb.TimerService、javax.transaction.UserTransaction、org.omg.CORBA.ORB)

如何配置JNDI ENC
ENC的JNDI名字空间可以通过XML或通过注解来定义。

如何从ENC中获取引用
通过InitialContext查找
@Stateful
@EJB(name=”ejb/ProcessPayment”,   //类级的注解
    beanInterface=ProcessPaymentLocal.class,
    beanName=”ProcessPaymentBean”)
public class TravelAgentBean implements TravelAgentRemote{
        public TicketDO bookPassage(CreditCarDO card,double amount){
            ProcessPaymentLocal payment=null;
            try{
                //EJB厂商不同,初始化方式也许不同
                javax.naming.InitialContext ctx=new InitialContext();
                //如果是本地接口,对于其他资源(如EntityManagerFactory等)也是类似的查找方法
                payment=(ProcessPaymentLocal)ctx.lookup(
                        “java:comp/env/ejb/ ProcessPayment”);//使用java:comp/env上下文
                //如果是远程接口,要用narrow方法
                Object ref= ctx.lookup(“java:comp/env/ejb/ ProcessPayment”);
                (ProcessPaymentRemote)javax.rmi.PortableRemoteObject.narrow(ref,
                     ProcessPaymentRemote.class);
            }catch(javax.naming.NamingException ne){…..}……..
        }
}

使用EJBContext查找
javax.ejb.SessionContext和javax.ejb.MessageDrivenContext接口都继承自javax.ejb.EJBContext,并且都可以用来查找ENC注册项。
@Stateful
@EJB(name=”ejb/ProcessPayment”,
    beanInterface=ProcessPaymentLocal.class,
    beanName=”ProcessPaymentBean”)
public class TravelAgentBean implements TravelAgentRemote{
        @Resource private javax.ejb.SessionContext ejbContext;
        public TicketDO bookPassage(CreditCarDO card,double amount){
            ProcessPaymentLocal payment=(ProcessPaymentLocal)ejbContext.lookup(
            “ejb/ ProcessPayment”);
            ……….
        }
}

注解注入
import com.titan.travelagent;//包名
@Stateful
public class TravelAgentBean implements TravelAgentRemote{
        @EJB private ProcessPaymentLocal payment;//通过成员变量注解直接注入
        //默认ENC名称为:com.titan.travelagent.TravelAgentBean/payment;
        //或者通过setter方法注解直接注入
        @EJB public void setProcessPayment(ProcessPaymentLocal payment){
        //默认ENC名称为:com.titan.travelagent.TravelAgentBean/processPayment;
            this.payment=payment;
        }
}

XML注入
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <ejb-local-ref>
            <ejb-ref-name> com.titan.travelagent.TravelAgentBean/payment </ejb-ref-name>注册名
            <ejb-ref-type>Session</ejb-ref-type>EJB类型有Session、Entity
            <local>com.tian.Processpayment.ProcessPaymentLocal</local>
            实现接口的EJB,由于ENC名与注解的一致,这个配置会覆盖注解
            <ejb-link>ProcessPaymentBean</ejb-link>
            <injection-target>     要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                下面的payment也可以使用processPayment,以通过setter注入
                <injection-target-name>payment</injection-target-name>
            </injection-target>
        </ejb-local-ref>
    </session>
</enterprise-beans>

XML覆盖
XML可以覆盖注解,它比注解具有一更高的优先级。覆盖方式见上一节,通过XML可以覆盖注解中要引入的实现类。

注入与继承
如果父类的数据成员或成员方法标有注入用注解,则注入父类的引用也会在子类中被引入,子类也可以覆盖父类的注入注解,但是如果父类的方法是private的,那么子类是无法覆盖它的注入注解的,这时父类和子类可能存在两个不同的引用。

不同类型的引用与注入
EJB类型的引用
@java.ejb.EJB用于引入一个EJB,@java.ejb.EJBs用于引入多个EJB
@EJB(name=”ejb/ProcessPayment”, //ENC注册名
    beanInterface=ProcessPaymentLocal.class,//接口
    beanName=”processPaymentEJB”,//实现这个接口的类,如果有多个类实现这个接口,必须指定这个
    mappedName=”….”)//根据EJB容器厂商的不同而有所不同,可能是一个键值,指向厂商的全局注册表
以上注解标注在bean class类之上,表示注册一个引用,要通过lookup查找,如果用于bean class的setter方法或数据成员(类型是EJB的成员),却表示直接注入

通过XML指定远程EJB引用
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>//EJB名
        <ejb-ref>
            <ejb-ref-name> com.titan.travelagent.TravelAgentBean/payment </ejb-ref-name>注册名
            <ejb-ref-type>Session</ejb-ref-type>    EJB类型有Session、Entity
            <remote>com.tian.Processpayment.ProcessPaymentRemote</ remote>    远程接口
            <ejb-link>ProcessPaymentBean</ejb-link>实现接口的EJB,如果接口有多个实现类必须配置
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>payment</injection-target-name>也可以使用processPayment
            </injection-target>
        </ejb-local-ref>
    </session>
</enterprise-beans>

通过XML指定本地EJB引用
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <ejb-local-ref>
            <ejb-ref-name> com.titan.travelagent.TravelAgentBean/payment</ejb-ref-name>注册名
            <ejb-ref-type>Session</ejb-ref-type>     EJB类型有Session、Entity
            <local>com.tian.Processpayment.ProcessPaymentLocal</local>    本地接口
            <ejb-link>ProcessPaymentBean</ejb-link>     与注解beanName等价,表示引用的EJB名。
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>payment</injection-target-name>也可以使用processPayment
            </injection-target>
        </ejb-local-ref>
    </session>
</enterprise-beans>

EJB名称的二义性和重载
对于某个特定的EJB JAR部署包而言,部署描述文件中的<ejb-name>元素及任何@Stateless或@Stateful注解的name属性必须是全局唯一的。但是对于同一个.ear文件的不同EJB-JAR部署包内,EJB名称是可以重复的,这了区分这些EJB,可以使用扩展语法:@EJB(beanName=”inventory-ejb.jar#InventoryEJB”) InventoryLocal inventory;

EntityManagerFactory类型的引用
在应用服务器对ENC进行配置或将EntityManagerFactory注入到EJB中之后,应用服务器会为你管理EntityManagerFactory实例的生命周期。定义引用的方式:
@javax.persistence.PersistenceUnit(name=”persistence/TitanDB”,//JNDI ENC名
    unitName=”TitanDB”)//persistence.xml文件中声明的persistence unit名称
该注解标注bean class时,表示不直接注入,如果标注在bean class的setter方法或数据成员上,表示直接注入,如:
@ PersistenceUnit(unitName=”crmDB”)private EntityManagerFactory crm;
如果要引用多个persistenceunit,要使用@PersistenceUnits注解。

通过XML指定EntityManagerFactory类型的引用
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <persistence-unit-ref>
            < persistence-unit-ref-name>persistence/TitanDB</persistence-unit-ref-name>注册名
            persistence.xml文件声明的persistence unit名
            < persistence-unit-ame>TitanDB</ persistence-unit-ame>
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>ships<injection-target-name>
            </injection-target>
        </ persistence-unit-ref >
    </session>
</enterprise-beans>

Persistence Unit名称的作用域及重载问题
可以在不同的地方声明persistence unit,它可以定义在EJB-JAR、EAR/lib目录下的JAR或WAR文件中。如果定义的persistence unit有重名,可以通过这个扩展语法访问:@ PersistenceUnit(unitName=”inventory.jar#InventoryDB”)

EntityManager类型的引用
当将EntityManager注册到JNDI ENC或注入到EJB时,EJB容器会对EntityManager所依赖的persistence contect具有完全的控制权(控制它的生命周期),注册方式与EntityManagerFactory很相似,如:
@PersistenceContext(name”persistence/TitanDB”//JNDI ENC名
    unitName=”TitanDB”//与peristence.xml文件中声明的peristence unit名称相同
    type=PersistenceContextType.EXTENDED)//表示extended persistence context(方法间事务,只用于        //stateful session),还有一个取值TRANSACTION。
可以用于标注bean class,当标注在bean class的setter方法或数据成员上时,表示直接注入,如:
@ PersistenceContext(unitName=”crmDB”)private EntityManager crm;

通过XML指定EntityManager类型的引用
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <persistence-context-ref>
            <persistence-context-ref-name>persistence/TitanDB</persistence-context-ref-name>名
            persistence.xml文件声明的persistence unit名
            <persistence-context-ame>TitanDB </persistence-context-ame>
            <persistence-context-type>EXTENDED</persistence-context-type>
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>ships<injection-target-name>
            </injection-target>
        </ persistence- context -ref >
    </session>
</enterprise-beans>

资源类型的引用
外部资源同样会被映射到JNDI ENC名字空间中的名称上,并且可以有选择地注入到bean实例的数据成员或setter方法中,被引用的外部资源类型可以是:javax.sql.DataSource、javax.jms.ConnectionFactory、javax.jms.QueueConnectionFactory、javax.jms.TopicConnectonFactory、javax.mail.Session、java.net.URL、javax.resource.cci.ConnectionFactory以及由JCA资源适配器定义的其他类型。下面以javax.sql.DataSource为例。

@javax.annotation.Resource(annotation:注解)
@ Resource注解除了用来引用外部资源,还可以用来引用JMS消息的目标地址、JNDI的环境注册项、EJBContext,以及其他Java EE的核心服务。

@ Resource(name=”jdbc/OracleDB”,//JNDI ENC名
    type=javax.sql.DataSource,//引入的类别
    authenticationType=AuthenticationType.APPLICATION,
    //认证方式,有容器认证(取值CONTAINER,容器根据部署的用户名和密码进行认证)和程序自行认证
    //(getConnection时要提供用户名和密码)
    shareable=true,//多个EJB在同一事务中是否可以使用相同的资源,使用相同的资源连接,以提高性能
    mappedName=”java:/DefaultDS”)//外部资源的厂商专用标识符,大多数情况,它的取值等价于一个                                    //全局JNDI名。
如果要引用多个资源,可以使用@javax.annotation.Resources注解

通过XML指定资源类型的引用
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <resource-ref>
            <res-ref-name>jdbc/OracleDB</res-ref-name>ENC注册名
            <res-type>javax.sql.DataSource</res-type>类名
            <res-auth>Container</res-auth>
            <mapped-name>java:/DefaultDS</mapped-name>
            java:/DefaultDS是一个全局JNDI中定义的一个数据源,数据源的配置请查询相关书籍
          
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>oracle<injection-target-name>
            </injection-target>
        </resource-ref >
    </session>
</enterprise-beans>

资源环境和受管对象
资源环境注册项可以引用包含受管对象的资源,受管对象是一种在部署期间被配置到容器中,并在运行期间由容器负责管理的资源,它的配置书中没有给出例子。资源环境注册项也可可以用于诸如:javax.transaction.Usertransaction和javax.transaction.TransactionSynchronizationRegistry服务,这时定义authenticationType和shareable 是非法的。
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <resource-env-ref>
            <resource-env-ref-name>jdbc/OracleDB</resource-env-ref-name>//ENC注册名
            <resource-env-ref-type>javax.transaction.Usertransaction</resource-env-ref-type>类
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>utx<injection-target-name>
            </injection-target>
        </resource-env-ref >
    </session>
</enterprise-beans>

环境注册项还可以用来配置String、Integer、Long、Double、Float、Byte、Boolean和Short这些包装类型的常量。
<enterprise-beans>
    <session>
        <ejb-name>ProcessPaymentBean</ejb-name>
        <env-entry>
            <env-entry-name>minCheckNumber</env-entry-name>ENC注册名
            <env-entry-type>java.lang.Integer</env-entry-type>类名
            <env-entry-value>2000</env-entry-value>常量值,这种引用几乎都是用XML来配置的
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.processpayment.ProcessPaymentBean
                </injection-target-class>
                <injection-target-name>minCheckNumber<injection-target-name>
            </injection-target>
        </env-entry>
    </session>
</enterprise-beans>

消息目标地址类型的引用
在JNDI ENC中注册的消息目标地址引用指向JMS的主题或队列。
@Resource(name=”jms/TicketTopic”,//ENC名
    type=javax.jms.Topic,//类型,还可以是队列(javax.jms.Queue)
    mappedName=”topic/TicketTopic”)//全局的厂商专用标识符
注意,使用注解无法设置目标地址的连接(message-destincton-link)
等价XML
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <message-destination-ref>
            <message-destination-ref-name>jms/TicketTopic</message-destination-ref-name>注册名
            <message-destination-type>javax.jms.Topic</message-destination-type>消息类型
            从目标地址收消息还是向其发消息
            <message-destination-usage>Produces</message-destination-usage>
            <message-destination-link>Distributor </message-destination-link>消息分发地址
            <mapped-name> topic/TicketTopic </mapped-name>
            topic/TicketTopic表示主题,它的配置请找相关书籍,原书练习中只讲到配置队列
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>ticketTopic <injection-target-name>
            </injection-target>
        </ message-destination-ref >
    </session>
</enterprise-beans>

Web Service类型的引用
在JNDI ENC中注册的web service引用指向服务的接口或服务的终端接口。19章有详细介绍
<enterprise-beans>
    <session>
        <ejb-name>TravelAgentBean</ejb-name>
        <service-ref>
            <service-ref-name>service/ChargeItProcessorService</service-ref-name>ENC注册名
          <service-interface>com.charge_it.ProcessorService</service-interface>JAX-RPC服务接口
            <wsdl-file>META-INF/wsdl/ChargeItProcesor.wsdl</wsdl-file> WSDL文件位置
            <jaxrpc-mapping-file>META-INF/mapping.xml </jaxrpc-mapping-file> JAX-RPC映射文件
            <service-qname>chargeIt:ProcessorService</service-qname>与WSDL文件定义的服务名一致
            <mapped-name>webservice/ChargeItProcessorService</mapped-name>
            webservice/ChargeItProcessorService映射应用服务器全局注册项
            <injection-target>        要直接注入时才用这个配置
                <injection-target-class>
                    com.titan.travelagent.TravelAgentBean
                </injection-target-class>
                <injection-target-name>chargeService <injection-target-name>
            </injection-target>
        </ message-destination-ref >
    </session>
</enterprise-beans>

猜你喜欢

转载自haywing.iteye.com/blog/1158022