(翻譯)JavaEE6規范 CDI教程第一部分

          (譯)JavaEE6規范 CDI教程第一部分

          kuuyee  |  2011-06-11  |  JEE   CDI  

          引言

          此教程講述 DI (依賴注入),并且涵蓋了 CDI (上下文依賴注入)的一些特性,比如類型安全注解配置、替換選擇等內容。

          CDI 是依賴注入 (DI) 和攔截 (AOP) 的Java標準規范。DI 和AOP有著很高的知名度,Java需要處理DIAOP以便在此之上構建其它的標準。DIAOP是很多Java框架的基礎。

          CDIJavaEE 6的基礎。它很快就得到了 Caucho’s Resin、 IBM’s WebSphere、 Oracle’s Glassfish、 Red Hat’s JBoss和眾多應用服務器的支持。CDISpringGuice框架非常相似,就像JPA很像ORMCDI簡化了對于DIAOPAPI。如果你使用過Spring或者Guice,你會發現CDI更容易學習和使用。如果你是依賴注入(DI)的新手,那么CDI能讓你迅速理解DICDI更容易學習和使用。

          CDI能夠獨立使用也能嵌入的任何應用中。

          這個教程在發布三年之久的Spring 2.5 DI 教程 (使用Spring “new” DI 注解)之后出現并不奇怪。它將有趣的對比三年前縮寫的Spring DI注解。

          本教程設計目標

          本教程的目標是描述和解讀不包含復雜的EJB3.1和JSF的DI和CDI。

          CDI的優勢是能夠在EJB和JSF之外。本教程只關注CDI。再次聲明在本教程中沒有JSF2和EJB3.1的內容。很多文章和教程都涵蓋如何使用CDI(JEE6規范)。本教程并不是,這里只是CDI。

          本教程有完整的代碼示例,你可以下載試用。

          我們將放緩速度,逐步的從基礎開始。一旦你理解了基本原理,我們會適當的加快腳步。

          所有的示例代碼都以確保能夠運行。我們不會鍵入臨時代碼,如果代碼不能運行,那它就不屬于本教。

          示例代碼都有清晰的標題,所以你可以把教程看做一個菜單,將來你如果想使用CDI DI的某些特性,可以方便的在菜單目錄中查找示例。

          裝飾器、擴展、攔截器、范圍都不在本教程的范圍之內。

          如果這個教程通過google討論組收到足夠的反饋和評論,我將加入CDI AOP(裝飾器和攔截器)綜合教程還有擴展。

          更多的建議和反饋會鼓舞我做的更好。

          依賴注入

          依賴注入(DI)是為軟件組件提供擴展依賴的過程。DI能夠讓你的代碼架構很簡潔。

          它幫助你用測試驅動開發的方式設計接口,提供統一的方式注入依賴。例如,一個數據訪問對象(DAO)可能依賴一個數據庫連接。

          取而代之,使用JNDI查找數據庫連接,你不需要注入它。

          考慮到JNDI是徹底的翻查,DI框架取代對象查找其它準備好的對象(依賴的),一個DI容器能注入這些依賴的對象。這被成為“好萊塢原則”,“不要給我打電話(查找對象),我會打給你(注入對象)”。

          如果你接觸過CRC卡,你能想象出一個依賴就像一個合作者。一個合作者是一個對象,另一個對象需要執行它的角色。例如,就像DAO(數據訪問對象)需要一個JDBC連接對象。

          依賴注入-自動柜員機 不用CDI或Spring或Guice版

          比如說你有一個自動柜員機(ATM,在其它國家也叫自動銀行機)并需要能夠和銀行通話。它需要調用一個傳輸對象來做此事。在這個例子中,傳輸對象掌控對銀行的底層通訊。

          這個例子可以用下面兩個接口來描述:

          // AutomatedTellerMachine接口 
          package org.cdi.advocacy;

          import java.math.BigDecimal;
          public interface AutomatedTellerMachine {
          public abstract void deposit(BigDecimal bd); //存錢
          public abstract void withdraw(BigDecimal bd); //取錢
          }
          // ATMTransport接口 
          package org.cdi.advocacy;

          public interface ATMTransport {
          public void communicateWithBank(byte[] datapacket);
          }

          現在 AutomatedTellerMachine 需要一個傳輸器來執行它的意圖,也就是存錢和取錢。要執行這個任務,AutomatedTellerMachine 可能會依賴很多對象和與這些依賴合作才能完成工作。

          一個 AutomatedTellerMachine 的實現可能看起來像這樣:

          // AutomatedTellerMachineImpl類 
          package org.cdi.advocacy;
          ...
          public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
          private ATMTransport transport;
          ...
          public void deposit(BigDecimal bd) {
          System.out.println("deposit called");
          transport.communicateWithBank(...);
          }
          public void withdraw(BigDecimal bd) {
          System.out.println("withdraw called");
          transport.communicateWithBank(...);
          }
          }

          AutomatedTellerMachineImpl 不需要關系傳輸器如何從銀行進行存款和取款操作。這是一個中間層允許我們用不同的傳輸器實現來替換,例如下面的例子:

          三個傳輸器例子:SoapAtmTransport、StandardAtmTransport和JsonAtmTransport

          //StandardAtmTransport 
          package org.cdi.advocacy;

          public class StandardAtmTransport implements ATMTransport {
          public void communicateWithBank(byte[] datapacket) {
          System.out.println("communicating with bank via Standard transport");
          ...
          }
          }
          //SoapAtmTransport 
          package org.cdi.advocacy;

          public class SoapAtmTransport implements ATMTransport {
          public void communicateWithBank(byte[] datapacket) {
          System.out.println("communicating with bank via Soap transport");
          ...
          }
          }
          //JsonRestAtmTransport 
          package org.cdi.advocacy;
          public class JsonRestAtmTransport implements ATMTransport {
          public void communicateWithBank(byte[] datapacket) {
          System.out.println("communicating with bank via JSON REST transport");
          }
          }

          注意 ATMTransport 接口的可能實現。AutomatedTellerMachineImpl 不需要關心使用的是那個傳輸器。并且,對于測試和開發,需要替換通話的真實銀行,你可以容易的通過 Mockito和EasyMock 實現,甚至你能夠編寫一個SimulationAtmTransport模擬實現用來測試。 DI的概念超越 CDI、Guice 和 Spring 。因此,你不用 CDI、Guice 或 Spring就能夠實現 DI,比如下面的例子:

          // AtmMain: 不使用CDI, Spring或Guice的DI實現 
          package org.cdi.advocacy;

          public class AtmMain {
          public void main (String[] args) {
          AutomatedTellerMachine atm = new AutomatedTellerMachineImpl();
          ATMTransport transport = new SoapAtmTransport();
          /* Inject the transport. */
          ((AutomatedTellerMachineImpl)atm).setTransport(transport);
          atm.withdraw(new BigDecimal("10.00"));
          atm.deposit(new BigDecimal("100.00"));
          }
          }

          注入不同的傳輸器只不過是調用了不同的setter方法,如下所示:

          //不使用CDI, Spring或Guice的DI實現 : setTransport 
          ATMTransport transport = new SimulationAtmTransport();
          ((AutomatedTellerMachineImpl)atm).setTransport(transport);

          假定在前面我們為 AutomateTellerMachineImpl 添加了一個 setTransport 方法。注意,你只要使用構造器參數就能替換setter方法。因此,保持 AutomateTellerMachineImpl 的接口簡潔。

          運行例子

          為了馬上能運行例子,我們為你準備了一些pom.xml文件。這里是運行例子的指令說明。

          依賴注入-自動柜員機 使用CDI版

          要使用CDI管理依賴,需要做如下工作:

          • 在META-INF資源目錄下創建一個空的bean.xml

          • 在 AutomatedTellerMachineImpl 內的 setTransport 方法上使用 @Inject 注解

          • 在 StandardAtmTransport 上標注 @Default 注解

          • 在 SoapAtmTransport 和 JsonRestAtmTransport 上標注 @Alternative 注解

          • 在 AutomatedTellerMachineImpl 上標注 @Named 注解一遍其容易被查找;給它一個命名“atm”

          • 使用CDI beanContainer查找atm,執行存款和取款

          在META-INF資源目錄下創建一個空的bean.xml

          CDI 需要有一個 bean.xml 文件放置在 META-INF 內,META-INF 可以是jar文件或classpath或web應用 WEB-INF 下的。這個文件完全可以是空的(大小為0 bytes)。如果你的war或jar內的 META-INF 目錄下沒有這個beans.xml,那么CDI將不會處理它。另外CDI將會檢索jar和war文件內的beans.xml,甚至它為0 bytes。

          META-INF/beans.xml,可能是空文件

          <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

          </beans>

          注意,我們在 beans.xml 文件中使用<beans>作為根元素并帶有命名空間。盡管 beans.xml 可以完全是空的,但是加入這個起始元素是個好的習慣。這同樣可以避免IDE對0 byte的 beans.xml 發出警告。(我憎恨IDE警告,它使我分散精力)

          在AutomatedTellerMachineImpl內的setTransport方法上使用@Inject注解

          @Inject 注解用來標記要注入的位置。你可以對構造器參數、實例變量和setter方法的屬性使用此注解。在這個例子中,我們注解在setTransport 方法上(transport屬性的setter方法)。

          // AutomatedTellerMachineImpl使用@Inject注入一個transport 
          package org.cdi.advocacy;
          ...
          import javax.inject.Inject;
          public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
          private ATMTransport transport;
          @Inject
          public void setTransport(ATMTransport transport) {
          this.transport = transport;
          }
          ...
          }

          默認情況下, CDI 將會尋找 ATMTransport 接口的實現類,一旦找到就會創建一個實例并用setter方法setTransport注入這個實例到 ATMTransport 中。如果我們只有一個 ATMTransport 實例在classpath中,那么我們就不需要注解其它的 ATMTransport 實現。現在我們有三個實現,分別命名為 StandardAtmTransport , SoapAtmTransport 和 JsonAtmTransport ,這就需要我們把其中兩個注解為 @Alternatives ,還有一個注解為 @Default 。

          在StandardAtmTransport上標注@Default注解

          當前的例子里, StandardAtmTransport 是transport的默認實現,所以我們給他加上 @Default 注解,如下:

          //StandardAtmTransport使用注解@Default 
          package org.cdi.advocacy;

          import javax.enterprise.inject.Default;
          @Default
          public class StandardAtmTransport implements ATMTransport {
          ...

          在SoapAtmTransport和JsonRestAtmTransport上標注@Alternative注解

          如果我們沒有給他們使用 @Alternative 注解,那只有等到給他們注解為 @DefaultCDI 才會關注它們。讓我們來給JsonRestAtmTransport 和 SoapRestAtmTransport 加上 @Alternative 注解以便 CDI 不會感到迷惑。

          //JsonRestAtmTransport使用注解@Alternative 
          package org.cdi.advocacy;

          import javax.enterprise.inject.Alternative;
          @Alternative
          public class JsonRestAtmTransport implements ATMTransport { ... }
          // SoapAtmTransport使用注解@Alternative 
          package org.cdi.advocacy;
          import javax.enterprise.inject.Alternative;
          @Alternative
          public class SoapAtmTransport implements ATMTransport { ... }

          在AutomatedTellerMachineImpl上標注@Named注解以便其容易被查找;給它一個命名“atm”

          我們不在 Java EE6 應用中使用 AutomatedTellerMachineImpl ,而只是通過 beanContainer 來查找它。讓我們給它一個容易理解的名字,比如"atm"。使用 @Name 注解來給他命名。在 JavaEE 6 應用中同樣可以使用 @Name 注解來讓bean可以通過統一EL語言(表達式語言標準,用來在JSP和JSF組件中使用)。

          下面是使用 @Named 給 AutomatedTellerMachineImpl 起名為"atm"的代碼:

          //AutomatedTellerMachineImpl使用注解@Name [source,java] package org.cdi.advocacy;  import java.math.BigDecimal;  import javax.inject.Inject; import javax.inject.Named;  @Named("atm") public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {        ...  }

          注意,如果你沒有在 @Name 注解中提供名字,那么默認名字就是類名把第一個字母小寫,如下:

          //名字默認值 @Named public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {        ...  }

          這時候名字默認就是automatedTellerMachineImpl。

          使用CDI beanContainer查找atm,執行存款和取款

          最后我們使用beanContainer查找atm并執行一些存款操作。

          // AtmMain通過名字查找atm 
          package org.cdi.advocacy;
          ...
          public class AtmMain {
          ...
          ...
          public static void main(String[] args) throws Exception {
          AutomatedTellerMachine atm = (AutomatedTellerMachine) beanContainer
          .getBeanByName("atm");
          atm.deposit(new BigDecimal("1.00"));
          }
          }

          如果你在命令行運行它,你將得到如下輸出

          Output

          deposit called             
          communicating with bank via Standard transport

          你同樣可以通過類型查找AtmMain。

          //AtmMain通過類型查找atm 
          package org.cdi.advocacy;
          ...
          public class AtmMain {
          ...
          ...
          public static void main(String[] args) throws Exception {
          AutomatedTellerMachine atm = beanContainer.getBeanByType(AutomatedTellerMachine.class);
          atm.deposit(new BigDecimal("1.00"));
          }
          }

          自從CDI注入是類型安全的,通過名字查找就可能失效。注意我們有一個向下轉型在應用 Java泛型 的時候。

          如果你去掉 StandardATMTransport 上的 @Default 注解,你將會得到同樣的輸出。但是如果你去掉其它兩個 transport JsonATMTransport 和 SoapATMTransport 上的 @Alternative,CDI 將會報出如下錯誤信息:

          Output

          Exception in thread "main" java.lang.ExceptionInInitializerError 
          Caused by: javax.enterprise.inject.AmbiguousResolutionException: org.cdi.advocacy.AutomatedTellerMachineImpl.setTransport:
          Too many beans match, because they all have equal precedence.
          See the @Stereotype and <enable> tags to choose a precedence. Beans:
          ManagedBeanImpl[JsonRestAtmTransport, {@Default(), @Any()}]
          ManagedBeanImpl[SoapAtmTransport, {@Default(), @Any()}]
          ManagedBeanImpl[StandardAtmTransport, {@javax.enterprise.inject.Default(), @Any()}]
          ...

          CDI期望找到一個并且只有一個適合的注入。后面我們將講述如何使用替換選擇(alternative)。

          待續..

          2011-06-11

          posted on 2011-06-16 14:03 kuuyee 閱讀(3782) 評論(1)  編輯  收藏 所屬分類: CDIJEE

          評論

          # re: (翻譯)JavaEE6規范 CDI教程第一部分[未登錄] 2013-03-13 17:39 rainbow

          博主,你好,我在網上看到了您的這篇翻譯的部分內容,找到您的博客后,但發現這幾篇介紹cdi的文章已經不可看了.如果方便的話,希望可以再重新貼出來,謝謝了  回復  更多評論   

          導航

          <2011年6月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          統計

          隨筆分類(139)

          Linux內核

          搜索

          •  

          積分與排名

          • 積分 - 320251
          • 排名 - 178

          最新評論

          閱讀排行榜

          主站蜘蛛池模板: 呼图壁县| 望谟县| 准格尔旗| 莱芜市| 连城县| 上杭县| 合山市| 哈巴河县| 盱眙县| 顺昌县| 崇左市| 娄底市| 达州市| 沙坪坝区| 浏阳市| 社旗县| 遂平县| 原阳县| 扬中市| 沙洋县| 辽阳市| 吴忠市| 浦北县| 江西省| 安远县| 迁安市| 铜梁县| 闻喜县| 巫溪县| 新平| 洛阳市| 宣威市| 维西| 舟山市| 荣昌县| 谢通门县| 伊金霍洛旗| 普兰县| 剑川县| 中西区| 民和|