posts - 195, comments - 34, trackbacks - 0, articles - 1

          EJB3.0中的依賴注入,截獲器及其在WebLogic Server 10中的擴(kuò)展2007-09-05 來(lái)自:lizhe1985  [收藏到我的網(wǎng)摘]



          1 前言

            與EJB2.1相比,EJB3.0規(guī)范引入了兩個(gè)重要概念:依賴注入(DI:Dependency Injection)和截獲器(Interceptor),本文首先介紹了這兩個(gè)概念并給出相關(guān)示例,然后分析了EJB3.0規(guī)范在這兩方面的不足之處,最終深入探討了WebLogic Server 10對(duì)它們的支持和擴(kuò)展。

          2 依賴注入

            2.1 基本概念

            依賴注入是從開(kāi)源社區(qū)的一些著名輕量級(jí)容器(如Spring、Pico container)中所發(fā)展出來(lái)的概念,其主要思想就是由容器而不是對(duì)象本身來(lái)負(fù)責(zé)處理對(duì)象之間的依賴關(guān)系。與傳統(tǒng)的服務(wù)定位器相比,依賴注入具有易測(cè)試、弱侵入性等優(yōu)點(diǎn),這也就是為什么在最新的Java EE 5規(guī)范中引入它的原因。

            對(duì)于EJB3.0來(lái)說(shuō),依賴注入就是由容器負(fù)責(zé)查找被依賴的對(duì)象并注入到依賴bean中,而bean本身不再需要進(jìn)行JNDI或者context查詢。此外,依賴注入發(fā)生在任何業(yè)務(wù)方法被調(diào)用之前,而且支持setter方法注入和域注入兩種方式。

            通過(guò)與標(biāo)注結(jié)合使用,在bean類(lèi)中聲明依賴注入是非常簡(jiǎn)單的(當(dāng)然,也可以在部署描述符文件中聲明依賴注入):

              @EJB用于注入EJB業(yè)務(wù)對(duì)象
              @PersistenceUnit 用于注入EntityManagerFactory
              @PersistenceContext 用于注入EntityManager
              @Resource 用于注入其它資源對(duì)象,如連接工廠、消息目標(biāo)等

            2.2 示例

          @Stateless
          public class ServiceBean implements Service {
          private javax.sql.DataSource myDS;
          @Resource(mappedName=“LocalDataSource")
          public void setMyDS(javax.sql.DataSource ds) {this.myDS = ds; }
          @EJB(beanName=“AccountBean")
          private Account account;
          }

            在無(wú)狀態(tài)會(huì)話bean ServiceBean中,聲明了兩個(gè)依賴:一個(gè)是數(shù)據(jù)源,一個(gè)是業(yè)務(wù)接口。在運(yùn)行期間,EJB3.0容器一旦創(chuàng)建了ServiceBean的實(shí)例,就會(huì)分別通過(guò)方法注入和域注入將數(shù)據(jù)源對(duì)象和業(yè)務(wù)對(duì)象注入到ServiceBean中。

          3 截獲器

            3.1 基本概念

            作為EJB3.0中提出的新概念,截獲器是可以對(duì)bean的業(yè)務(wù)方法和生命周期事件進(jìn)行攔截的組件。截獲器需要由@Interceptors 或發(fā)布描述符文件中相關(guān)的標(biāo)簽指定。截獲器可以帶有狀態(tài)而且可以進(jìn)行依賴注入,其生命周期與其所綁定的EJB bean實(shí)例的生命周期一致。

            定義在截獲器中用于攔截目的的方法被稱為截獲器方法,其中,針對(duì)業(yè)務(wù)方法的截獲器方法通過(guò)@AroundInvoke標(biāo)注或發(fā)布描述符文件中相關(guān)的標(biāo)簽指定;針對(duì)生命周期回調(diào)的截獲器方法通過(guò)@PostConstruct, @PreDestroy等標(biāo)注或發(fā)布描述符文件中對(duì)應(yīng)的標(biāo)簽指定。

            截獲器分為四類(lèi):

              缺省截獲器:可作用于ejb-jar中定義的所有EJB bean。缺省截獲器只能定義在DD中,不存在相應(yīng)的標(biāo)注
              類(lèi)級(jí)截獲器:只能作用于所指定的EJB bean
              方法級(jí)截獲器:只能作用于所指定的某個(gè)EJB bean業(yè)務(wù)方法,方法級(jí)截獲器不能用于攔截bean的生命周期事件
              bean 級(jí)截獲器:又被稱為自我截獲器,因?yàn)榻孬@器同時(shí)就是EJB bean本身,此時(shí)相關(guān)的截獲器方法只能作用于該EJB

            3.2 截獲器鏈的調(diào)用順序

            因?yàn)榭梢詾镋JB定義多個(gè)截獲器,所以存在截獲器鏈的調(diào)用順序問(wèn)題,缺省情況下,以下原則被遵循:

              調(diào)用順序依次是缺省截獲器、類(lèi)級(jí)截獲器、方法級(jí)截獲器以及bean級(jí)截獲器
              對(duì)于同類(lèi)別的截獲器,按照聲明的順序調(diào)用
              總是優(yōu)先調(diào)用父類(lèi)的截獲器方法。

            此外,EJB3.0規(guī)范還提供了更靈活的選項(xiàng),詳細(xì)信息請(qǐng)參考EJB3.0規(guī)范中“截獲器”一節(jié):

              在發(fā)布描述符文件中設(shè)置“exclude-default-interceptors” 可以取消對(duì)缺省截獲器的調(diào)用,而應(yīng)用“exclude-class-interceptors”則取消對(duì)類(lèi)級(jí)截獲器的調(diào)用
              為了替換缺省的截獲器鏈調(diào)用順序,可以設(shè)置發(fā)布描述符文件的“interceptor-order”。

            3.3 示例

          @Stateless(name="Trader")
          @Interceptors(ClassInterceptor.class)
          public class TraderBean implements Trader {
          @Interceptors(MethodInterceptor.class)
          public String sell(String stockSymbol) {

          }
          @AroundInvoke
          public Object selfInterceptor(InvocationContext) throws Exception {

          }
          }

            在無(wú)狀態(tài)會(huì)話bean TraderBean中,聲明了兩個(gè)截獲器:一個(gè)類(lèi)級(jí)截獲器,一個(gè)方法級(jí)截獲器,此外,通過(guò)使用@AroundInvoke標(biāo)注,TraderBean的方法selfInterceptor被聲明為截獲器方法,所以TraderBean就是bean級(jí)截獲器。

          4 EJB3.0規(guī)范在依賴注入和截獲器方面的不足

            從依賴注入方面來(lái)說(shuō),EJB3.0目前支持的注入類(lèi)型是非常有限的,諸如List、Map之類(lèi)的集合類(lèi)型都不能被注入。

            另一方面,EJB3.0的截獲器可被看作是一種AOP模型,截獲器類(lèi)相當(dāng)于方面(Aspect),而截獲器方法相當(dāng)于通知(Advice),但這種AOP是非常簡(jiǎn)單的:

              不支持引入(Introduction);
              不支持切入點(diǎn)(Pointcut)模式
              通過(guò)標(biāo)注告訴容器某個(gè)EJB需要使用截獲器,從而使得這種AOP模型具有一定的侵入性

          5 WebLogic Server 10 EJB3.0容器簡(jiǎn)介

             5.1 Pitchfork 簡(jiǎn)介

            Pitchfork是由Interface21與BEA合作開(kāi)發(fā)的一個(gè)具有Apache License的開(kāi)源項(xiàng)目,作為Spring的“增值”項(xiàng)目,開(kāi)發(fā)Pitchfork有兩個(gè)目的:

              當(dāng)前應(yīng)用服務(wù)器可使用Pitchfork實(shí)現(xiàn)Java EE 5中的依賴注入,標(biāo)注處理以及EJB3.0中的截獲器。
              在Spring容器中支持Java EE 5的標(biāo)注

            5.2 WebLogic Server 10 EJB3.0容器的架構(gòu)

            WebLogic Server 10完全支持Java EE 5和EJB3.0,其EJB3.0 容器是基于Pitchfork來(lái)實(shí)現(xiàn)依賴注入與截獲器的,而Pitchfork又利用了Spring的依賴注入和AOP。此時(shí),WebLogic Server 10的用戶有兩種選擇:

              使用EJB3.0標(biāo)準(zhǔn)模型: EJB bean實(shí)例由EJB容器創(chuàng)建,然后由Pitchfork實(shí)施Java EE 5 的依賴注入和截獲器,這是WebLogic Server 10的缺省配置。
              使用Spring擴(kuò)展模型:EJB bean實(shí)例由Spring容器創(chuàng)建并實(shí)施Spring的依賴注入,然后由Pitchfork實(shí)施Java EE 5的依賴注入,EJB3.0截獲器和Spring AOP。

          6 Spring擴(kuò)展模型

            6.1 使用Spring擴(kuò)展模型的主要開(kāi)發(fā)步驟

              下載某個(gè)版本的Spring類(lèi)庫(kù)(最好是由BEA指定的版本),至少需要包括以下jar文件:spring.jar, aspectjweaver.jar, commons-logging.jar, log4j-1.2.14.jar,如果需要,還可以增加其它jar文件。
              將這些jar文件加入到WebLogic Server 10的類(lèi)路徑中。
              創(chuàng)建文本文件Weblogic.application.ComponentFacotry以標(biāo)識(shí)當(dāng)前應(yīng)用將使用Spring擴(kuò)展模型,其內(nèi)容只有一行:    Weblogic.application.SpringComponentFactory。
              創(chuàng)建/META-INF/services子目錄,然后放入前面創(chuàng)建的文件Weblogic.application.ComponentFacotry
              建立Spring 配置文件,并放入到/META-INF目錄下,如果是EJB應(yīng)用程序,Spring配置文件的名字應(yīng)該是spring-ejb-jar.xml,如果是Web應(yīng)用程序,Spring配置文件的名字則是spring-web.xml.
              為了使EJB bean能利用Spring的依賴注入與AOP,需要同時(shí)將其定義在spring-ejb-jar.xml(或spring-web.xml)中,從Spring角度來(lái)看,這就是一個(gè)普通的Spring bean,只不過(guò)它的id或name必須與對(duì)應(yīng)的EJB name相同。
              將上述目錄文件、相關(guān)的Java類(lèi)、部署描述符等打包成標(biāo)準(zhǔn)的jar文件、war文件或ear文件。

            需要注意的是,在Spring配置文件中定義的bean,如果它也是一個(gè)EJB bean,則該Spring bean的class屬性會(huì)被忽略以避免潛在的沖突。

            6.2 示例

            示例一:利用Spring的依賴注入。本例將演示兩點(diǎn),(1) 注入一個(gè)集合類(lèi)型,(2) 注入一個(gè)Spring POJO對(duì)象

            (1) 開(kāi)發(fā)一個(gè)bean類(lèi)

          @Stateless(name="AccountManagementBean")
          @Remote({examples.AccountManagement.class})
          public class AccountManagementBean
          {
          private List accountList;
          private BusinessObject businessObject;
          public boolean doBusiness(String account) {
          if (accountList.contains(account)) {
          businessObject.doBusiness();
          return true;
          }
          return false;
          }
          public BusinessObject getBusinessObject() {
          return businessObject;
          }
          public void setBusinessObject(BusinessObject businessObject) {
          this.businessObject = businessObject;
          }

          public List getAccountList() {
          return accountList;
          }
          public void setAccountList(List accountList) {
          this.accountList = accountList;
          }
          }
            (2) 一個(gè)POJO

          public class BusinessObjectImpl implements BusinessObject
          {
          private String result;

          public void doBusiness() {
          System.out.println("I am doing business, the result is: " + result);
          }

          public String getResult() {
          return result;
          }

          public void setResult(String result) {
          this.result = result;
          }
          }
            (3) Spring-ejb-jar.xml


          <beans>
          <bean name="BusinessObject" class="examples.BusinessObjectImpl">
          <property name="result" value="success" />
          </bean>
          <bean id="AccountManagementBean">
          <property name="businessObject" ref="BusinessObject" />
          <property name="accountList">
          <list>
          <value>Safin</value>
          <value>Amy</value>
          <value>Albert</value>
          <value>Yang</value>
          </list>
          </property>
          </bean>
          </beans>

            示例二:利用Spring 的AOP。本例演示了混合使用JEE interceptor、基于代理的Spring AOP以及AspectJ的Aspect

            (1) 開(kāi)發(fā)一個(gè)bean類(lèi)

          @Stateless(name="AccountManagementBean")
          @Remote({examples.AccountManagement.class})
          public class AccountManagementBean
          {
          public AccountManagementBean() {}
          @Interceptors({examples.JEEInterceptor.class})
          public boolean doBusiness(String account) {
          System.out.println("I am doing business " + account);
          return true;
          }
          }

            (2) 開(kāi)發(fā)一個(gè)JEE interceptor

          public class JEEInterceptor {
          @AroundInvoke
          private Object intercept(InvocationContext inv) throws Exception{
          System.out.println("I am in JEEInterceptor.intercept()");
          return inv.proceed();
          }
          }

            (3) 開(kāi)發(fā)一個(gè)Advice

          public class SimpleAdvice implements MethodInterceptor{
          public Object invoke(MethodInvocation invocation) throws Throwable {
          System.out.println("I am in Spring AOP interceptor");
          return invocation.proceed();
          }
          }

            (4) 開(kāi)發(fā)一個(gè)Pointcut

          public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
          public boolean matches(Method method, Class cls) {
          return "doBusiness".equals(method.getName());
          }
          public ClassFilter getClassFilter() {
          return new ClassFilter() {
          public boolean matches(Class cls) {
          return AccountManagementBean.class.isAssignableFrom(cls);
          }
          };
          }
          }

            (5) 開(kāi)發(fā)一個(gè)AspectJ Aspect

          @Aspect
          public class Aspects {
          @Around("execution(* examples.AccountManagementBean.doBusiness(..))")
          public Object around(ProceedingJoinPoint pjp) {
          System.out.println(“I am in AspectJ aspect);
          Object retVal = null;
          try {
          retVal = pjp.proceed();
          } catch (Throwable e) {
          e.printStackTrace();
          }
          return retVal;
          }
          }

            (5) Spring-ejb-jar.xml

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
          <bean id="AccountManagementBean" />
          <bean id="myAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
          <property name="advice">
          <bean class=”examples.SimpleAdvice” />
          </property>
          <property name="pointcut">
          <bean class="examples.SimpleStaticPointcut" />
          </property>
          </bean>
          <bean id="MyAspect" class="examples.Aspects" />
          </beans>

            6.3 優(yōu)點(diǎn)

            提供Spring擴(kuò)展模型具有如下好處:

              可以在同一個(gè)發(fā)布單元中混合使用EJB bean和Spring POJO,而且一個(gè)EJB bean可以同時(shí)是一個(gè)Spring POJO,因此被EJB容器和Spring容器同時(shí)管理
              有效地克服了EJB3.0 規(guī)范在依賴注入和截獲器方面的一些缺陷
              為用戶提供更靈活的選擇:
                如果標(biāo)準(zhǔn)的EJB 依賴注入以及截獲器可以滿足應(yīng)用需求,或者可移植性是優(yōu)先考慮的因素,則選用標(biāo)準(zhǔn)模型
                如果標(biāo)準(zhǔn)的EJB依賴注入以及截獲器不能滿足應(yīng)用需求,或者不用考慮可移植性,或者需要集成一些Spring應(yīng)用,則應(yīng)選用Spring擴(kuò)展模型

          7 Spring AOP在WebLogic Server 10 Spring擴(kuò)展模型中的使用限制及說(shuō)明

            7.1 代理生成方式

            Spring AOP采用的是一種基于代理的框架,目前有兩種生成代理的方式:JDK動(dòng)態(tài)代理和CGLIB。在WebLogic Server 10的Spring擴(kuò)展模型中,目前不支持CGLIB方式,這主要是因?yàn)樯傻腅JB類(lèi)都是final的

            7.2 代理創(chuàng)建

            在Spring中,代理或者是通過(guò)諸如ProxyFactoryBean之類(lèi)的工廠bean明確創(chuàng)建,或者是通過(guò)“自動(dòng)代理”創(chuàng)建器隱含創(chuàng)建。

            在WebLogic Server 10中,Pitchfork已經(jīng)隱含提供了類(lèi)似的“自動(dòng)代理”機(jī)制,所以原有的Spring代理創(chuàng)建方式是無(wú)效的,換句話說(shuō),用戶只需要在Spring配置文件中定義目標(biāo)bean以及對(duì)應(yīng)的Spring Advisor和AspectJ Aspect, 不需要再定義ProxyFactoryBean和“自動(dòng)代理”創(chuàng)建器,Pitchfork會(huì)自動(dòng)為我們創(chuàng)建AOP代理

            7.3 截獲的順序問(wèn)題

            在Spring擴(kuò)展模型中,可以混合使用JEE截獲器、Spring Advisor以及AspectJ Aspect,此時(shí)的調(diào)用順序?yàn)镴EE截獲器、Spring Advisor以及AspectJ Aspect。

            7.4 bean的實(shí)例化模式

            缺省方式下,Spring bean是Singleton的,即Spring容器總是返回同一個(gè)bean實(shí)例,在Spring擴(kuò)展模型中,這意味著同一個(gè)Spring Advisor或AspectJ Aspect實(shí)例將被應(yīng)用到不同的EJB bean實(shí)例上,如果Advisor或Aspect實(shí)例是有狀態(tài)的,這可能會(huì)產(chǎn)生問(wèn)題,為此我們推薦如下的使用方式:

             SLSB & MDB
              帶有狀態(tài)的Advisor或Aspect
               不推薦用帶有狀態(tài)的Advisor或Aspect,除非用戶意識(shí)到相關(guān)的后果并認(rèn)為這種后果是可以接收的,此時(shí)的實(shí)例化模式取決于具體應(yīng)用
              沒(méi)有狀態(tài)的Advisor或Aspect
               采用Singleton實(shí)例化模式
             SFSB
              帶有狀態(tài)的Advisor或Aspect
               正常情況下應(yīng)該采用的實(shí)例化模式是Prototype,除非用戶意識(shí)到采用Singletong的后果并認(rèn)為這種后果是可以接收的
              沒(méi)有狀態(tài)的Advisor或Aspect
               采用Singleton實(shí)例化模式

          8 結(jié)束語(yǔ)

            依賴注入和截獲器的引入,使得EJB3.0克服了很多EJB2.1的缺點(diǎn),但與開(kāi)源社區(qū)中流行的輕量級(jí)容器相比,EJB3.0無(wú)論是在依賴注入的范圍還是在AOP的易用性方面都還存在一定的不足。而最近BEA所推出的WebLogic Server 10不但完全支持EJB3.0,而且對(duì)其進(jìn)行了有效擴(kuò)展,這使得一方面可以充分利用在開(kāi)源社區(qū)中出于領(lǐng)導(dǎo)地位的Spring在依賴注入和AOP方面的強(qiáng)大能力,另一方面又緊密依托WebLogic Server 10在事務(wù)、安全、集群方面領(lǐng)先的技術(shù),從而為EJB3.0用戶開(kāi)發(fā)出易維護(hù)、可擴(kuò)展、高效率的企業(yè)應(yīng)用程序打下堅(jiān)實(shí)的基礎(chǔ)。

          作者簡(jiǎn)介

            李鋼 dev2devid: cc_masterli ,BEA研發(fā)中心WebLogic Server 研發(fā)團(tuán)隊(duì)成員,西安交通大學(xué)計(jì)算機(jī)專(zhuān)業(yè)博士,一直從事java中間件領(lǐng)域的研發(fā),2005年加入BEA,現(xiàn)為EJB3 中國(guó)研發(fā)小組的負(fù)責(zé)人。



          主站蜘蛛池模板: 曲阳县| 宜兰市| 大田县| 定结县| 鹰潭市| 南雄市| 武宣县| 合江县| 独山县| 湘阴县| 东乡县| 龙海市| 秦皇岛市| 辰溪县| 朝阳市| 海晏县| 潢川县| 平舆县| 连南| 胶南市| 嘉义市| 泰来县| 谢通门县| 拜泉县| 丹江口市| 林西县| 洞头县| 通州市| 廉江市| 盐源县| 盐津县| 望谟县| 鄯善县| 嵩明县| 眉山市| 寻乌县| 察哈| 泸定县| 安庆市| 山东| 柳江县|