weidagang2046的專欄

          物格而后知致
          隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
          數(shù)據(jù)加載中……

          EJB 3.0+Aspect實現(xiàn)聲明性編程初步

          提要 本文將與你一同探討怎樣把注解和方面的威力聯(lián)合起來,以與EJB 3.0兼容的方式為企業(yè)實現(xiàn)提供聲明性服務(wù),而在同時仍然提供容器的獨立性。

            一、 引言

            在我們共同尋求進一步提高軟件開發(fā)生產(chǎn)性能的方法的過程中,我們-作為Java社團成員-一般都轉(zhuǎn)向J2EE來提供針對企業(yè)開發(fā)中更具挑戰(zhàn)性的技術(shù)問題如分布式事務(wù)管理、并發(fā)性和對象分布等的解決方案。其背后的指導(dǎo)思想-這些復(fù)雜的企業(yè)服務(wù)能被應(yīng)用程序服務(wù)器供應(yīng)商所實現(xiàn)并能為商業(yè)開發(fā)者所平衡-的確是一種很好的思想。J2EE,具體地說是EJB,已成功地提供了一個平臺-在其上構(gòu)建企業(yè)Java應(yīng)用程序。

            這其中部分的成功是由于能夠進行聲明性編程-一種程序開發(fā)方式-用這種方式,你可以聲明基礎(chǔ)結(jié)構(gòu)服務(wù)而不是用商業(yè)邏輯明確地編碼從而使代碼散布于各處。EJB已經(jīng)證明了這種編程方式的價值-通過允許企業(yè)問題例如事務(wù)和安全被用一種發(fā)布描述符所聲明并為容器所處理。

            然而,在過去的歲月中,越來越多的開發(fā)者認識到EJB在團隊的生產(chǎn)效率方面給它自己帶來新的大量的挑戰(zhàn)-每個EJB必須伴隨多個接口,以一種發(fā)布描述符描述,經(jīng)由JNDI被存取,等等。而在容器外EJB上進行單元測試也帶來另外的困難,如今EJB已不再把重點放在單純的面向?qū)ο箝_發(fā)上。

            請注意,為閱讀本文您需具備如下工具:

            ·Java 2 SDK 1.5

            ·Maven 2.0 Beta 2

            EJB 3.0的目標在于從以下幾個方面使企業(yè)開發(fā)更為容易:

            ·通過引入元數(shù)據(jù)注解來實現(xiàn)聲明性請求企業(yè)服務(wù)

            ·經(jīng)由注解實現(xiàn)依賴性/資源注入

            ·實現(xiàn)企業(yè)beans與EJB特定接口的解耦

            ·經(jīng)由輕量級的對象關(guān)系映射實現(xiàn)持續(xù)性存儲的簡化

            這對于EJB開發(fā)者來說尤如一股春風(fēng)-一直以來,他們竭力地從事開發(fā)、測試和維護EJB。利用EJB 3.0寫一個企業(yè)bean現(xiàn)在變得很容易,就如用特定的注解創(chuàng)建一個POJO(傳統(tǒng)的Java對象)以把它標明為一個EJB并請求企業(yè)服務(wù)。下面是一個來自于EJB 3.0 Public Draft中EJB的例子:

          @Stateful
          public class CartBean implements ShoppingCart
          {
          private float total;
          private Vector productCodes;
          public int someShoppingMethod(){...};
          ...
          }

            EJB 3.0聲明中實質(zhì)上指明開發(fā)者需要的不是一重量級的、"一次發(fā)布滿足所有"的解決方案,而是一個輕量級的、容易使用的解決方案-為開發(fā)者提供一定范圍的企業(yè)服務(wù)。為此,EJB 3.0所提供的最重要的方法之一就是實現(xiàn)企業(yè)beans與EJB API的解耦。并且,此解決方案還帶來令人感興趣的衍生-EJB現(xiàn)在不僅能夠運行在不同的EJB容器上,而且還能運行于任何應(yīng)用程序框架內(nèi)部-這些框架必須能夠識別EJB 3.0(JSR 220)和用于聲明企業(yè)服務(wù)的普通注解(JSR 250)。

            本文沒有提供關(guān)于聲明性編程、EJBs、方面或注解的深度探索。相反,而只是分析一下這些技術(shù)之間的相互關(guān)系并討論如何把它們用一種新的方式結(jié)合起來以簡化應(yīng)用程序開發(fā)。

            在本文中,你將會學(xué)習(xí)到如何編寫一個EJB 3.0兼容的bean并且通過創(chuàng)建幾個簡單的方面使其具有聲明性事務(wù)管理、安全和資源注入等功能。我希望您能從這個練習(xí)中得到以下的受益:

            ·學(xué)習(xí)方面的三個實際應(yīng)用(依賴性注入、安全和事務(wù))。

            ·熟悉EJB 3.0及其背后的思想。

            ·認識到怎樣實現(xiàn)EJB與特定API的解耦以允許EJB 3.0兼容的服務(wù)能夠以輕量級實現(xiàn)而不是僅由EJB來提供。

            二、 實例應(yīng)用程序-航班訂購

            在整個后面的討論中,你將學(xué)習(xí)到一個航班訂購系統(tǒng)的實現(xiàn)-它使用方面和注解來實現(xiàn)依賴性注入、安全和事務(wù)管理。該應(yīng)用程序僅執(zhí)行兩項功能:它允許用戶搜索航班(圖1),然后訂購一次旅行(圖2)。這兩個操作都將被進行安全處理以僅允許能被識別的用戶來執(zhí)行它們。另外,既然"訂購旅行"操作包含訂購兩個航班(外出和返回航班),那么需要把該操作創(chuàng)建為事務(wù)性的-如,兩個訂購將作為一個工作單元要么都成功要么都失敗。


          圖1.航班查詢:首先,用戶查找滿足他們的指定標準的航班。

          圖2.航班訂購:接下來,用戶訂購一個外出航班和一個返回航班。兩個訂購要么都成功要么都失敗。

            這個簡單的Web應(yīng)用程序包含幾個servlet、一個服務(wù)外觀和一個DAO層(見圖3)。

            資源配置、安全性和事務(wù)管理等橫切關(guān)注點將由方面(用AspectJ 1.5 M3實現(xiàn))所提供以實現(xiàn)在Java 5注解中所聲明的注入行為。


          圖3.航班訂購系統(tǒng)架構(gòu):這個航班訂購系統(tǒng)包括三個主要組成組件-它們聯(lián)合起來共同完成用戶請求。

          三、 資源注入

            EJB 3.0草案聲明中允許資源經(jīng)由@Resource注解來聲明(這一決定定義在草案普通注解聲明中)并且被容器注入進你的EJB。依賴性注入是一項技術(shù)-使用這種技術(shù),一個對象外部的實體而不是顯式地為該對象所創(chuàng)建的實體能夠提供(注入)一個對象的依賴性。它有時被描述為好萊塢原則-這開玩笑似地意味著"不要給我們打電話,我們會給你打電話的"。

            以TravelAgencyServiceImpl類為例-這個類為了持續(xù)性存儲一些數(shù)據(jù)需要找到一個IFlightDAO接口的實現(xiàn)。傳統(tǒng)地,這是經(jīng)由一個工廠、singleton、服務(wù)定位器或一些另外的定制解決方案來實現(xiàn)的。其中,一個可能的解決方案看上去如下所示:

          public class TravelAgencyServiceImpl implements ITravelAgencyService
          {
           public IFlightDAO flightDAO;
           public TravelAgencyServiceImpl()
           { flightDAO = FlightDAOFactory.getInstance().getFlightDAO(); }
           public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
           throws InsufficientSeatsException
           {
            reserveSeats(outboundFlightID, seats);
            reserveSeats(returnFlightID, seats);
           }
          }

            你已看到,這個實現(xiàn)包含創(chuàng)建一個特定的工廠類-它很可能讀取存儲在某處的配置信息以了解要創(chuàng)建IFlightDAO的實現(xiàn)方式。如果不是讓服務(wù)顯式地創(chuàng)建它的由容器所注入的依賴性,那么配置細節(jié)和對象創(chuàng)建將被代理到容器上。這允許一個應(yīng)用程序中的組件能夠被容易地連接到一起-用不同的配置并且消除大量老式的singleton和工廠代碼。

            該類的一個實現(xiàn)-它依賴于一個用JSR 250資源注解所聲明的IFlightDAO的實現(xiàn)-可能看上去如下所示:

          public class TravelAgencyServiceImpl implements ITravelAgencyService
          {
           @Resource(name = "flightDAO")
           public IFlightDAO flightDAO;
           public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
           throws InsufficientSeatsException
           {
            reserveSeats(outboundFlightID, seats);
            reserveSeats(returnFlightID, seats);
           }
          }

            在這種情況下,容器將把一個命名為"flightDAO"的資源的正確實現(xiàn)提供給服務(wù)類。但是,如果你現(xiàn)在就想利用資源注入,而不是等待EJB 3.0發(fā)行版,又該如何呢?好,你可以采用一種輕量級的容器-它能夠提供例如Spring或Pico Container的依賴性注入。然而,當前我還不了解存在一個輕量級的容器-它能夠使用JSR 250資源注解以指定注入要求(盡管我非常盼望在這一方面出現(xiàn)一些)。

            一種解決方案是使用方面來實現(xiàn)依賴性注入。如果你為此使用@Resource注解,那么你的實現(xiàn)將與EJB 3.0方式一致并且向前兼容EJB 3.0實現(xiàn)-而實現(xiàn)這并不是很困難的事情。下列列表顯示用AspectJ創(chuàng)建的一個方面-它注入用@Resource注解所注解的字段:

          @Aspect
          public class InjectionAspect
          {
           private DependencyManager manager = new DependencyManager();
           @Before("get(@Resource * *.*)")
           public void beforeFieldAccesses(JoinPoint thisJoinPoint)
           throws IllegalArgumentException, IllegalAccessException
           {
            FieldSignature signature = (FieldSignature) thisJoinPoint.getSignature();
            Resource injectAnnotation = signature.getField().getAnnotation(Resource.class);
            Object dependency = manager.resolveDependency(signature.getFieldType(),injectAnnotation.name());
            signature.getField().set(thisJoinPoint.getThis(), dependency);
           }
          }

            這個簡單方面所做的全部是,從一個屬性文件(這個邏輯被封裝在DependencyManager對象中)查詢實現(xiàn)類并且在存取字段之前把它注入到用@Resource注解所注解的字段中。顯然,這種實現(xiàn)不是完整的,但是它確實說明了你可以怎樣以一種JSR 250兼容方式且不需采用EJB來提供資源注入。
          四、 安全性

            除了資源注入外,JSR 250和EJB 3.0還提供經(jīng)由注解的元數(shù)據(jù)安全表示。javax.annotation.security包定義了五個注解-RunAs,RolesAllowed,PermitAll,DenyAll和RolesReferenced-所有這些都能應(yīng)用到方法上來定義安全要求。例如,如果你想要聲明上面列出的bookFlight方法僅能為具有"user"角色的調(diào)用者所執(zhí)行,那么你可以用如下的安全約束來注解這個方法:

          public class TravelAgencyServiceImpl implements ITravelAgencyService
          {
           @Resource(name = "flightDAO")
           public IFlightDAO flightDAO;
           @RolesAllowed("user")
           public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
           throws InsufficientSeatsException
           {
            reserveSeats(outboundFlightID, seats);
            reserveSeats(returnFlightID, seats);
           }
          }

            這個注解將指出由容器來負責(zé)保證只有指定角色的調(diào)用者才能執(zhí)行這個方法。因此現(xiàn)在我將展示另一個簡單的方面-它將進一步加強在該應(yīng)用程序上的安全約束:

          @Aspect
          public class SecurityAspect
          {
           @Around("execution(@javax.annotation.security.RolesAllowed * *.*(..))")
           public Object aroundSecuredMethods(ProceedingJoinPoint thisJoinPoint)
           throws Throwable
           {
            boolean callerAuthorized = false;
            RolesAllowed rolesAllowed = rolesAllowedForJoinPoint(thisJoinPoint);
            for (String role : rolesAllowed.value())
            {
             if (callerInRole(role))
             { callerAuthorized = true; }
            }
            if (callerAuthorized)
            { return thisJoinPoint.proceed(); }
            else
            {
             throw new RuntimeException("Caller not authorized to perform specified function");
            }
           }
           private RolesAllowed rolesAllowedForJoinPoint(ProceedingJoinPoint thisJoinPoint)
           {
            MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
            Method targetMethod = methodSignature.getMethod();
            return targetMethod.getAnnotation(RolesAllowed.class);
           }
           private boolean callerInRole(String role)
           { ... }
          }

            這個方面包含了所有方法的執(zhí)行-通過核實該調(diào)用者是在注解中所指定的角色之一,用@RolesAllowed注解來注解并且保證調(diào)用者被授權(quán)調(diào)用該方法。當然你還能代之以任何你喜歡的算法來授權(quán)用戶并且檢索他/她的角色,例如JAAS或一個定制的解決方案。在本示例程序中,為方便起見,我選擇代理到servlet容器。
          五、 事務(wù)

            事務(wù)成為企業(yè)開發(fā)的一個重要部分-因為它們有助于在一個并發(fā)的環(huán)境中的數(shù)據(jù)集成。從一個高層次上看,事務(wù)可以通過多種或者是完整的或者是都不完整的操作來保證這一點。

            不象針對資源注入和安全的注解,針對事務(wù)的注解是特定于EJB 3.0的并且沒有在JSR 250普通注解中定義。EJB 3.0定義了兩個與事務(wù)相聯(lián)系的注解:TransactionManagement和TransactionAttribute。該TransactionManager注解指定事務(wù)是由容器所管理還是為bean所管理的。在EJB 3中,如果這個注解沒被指定,那么將使用容器所管理的事務(wù)。TransactionAttribute注解用于指定方法的事務(wù)傳播級別。有效值-包括強制的、要求的、要求新的、支持的、不支持的和從不支持的-用來定義是否要求一個已有事務(wù)或啟動一個新的事務(wù),等等。

            因為bookFlight操作包含兩步-訂購一個外出航班和一個返回航班,所以,通過把它包裝成一個事務(wù),你能保證這項操作的一致性。通過使用EJB 3.0事務(wù)注解,這將看上去如下所示:

          public class TravelAgencyServiceImpl implements ITravelAgencyService
          {
           @Resource(name = "flightDAO")
           public IFlightDAO flightDAO;
           @RolesAllowed("user")
           @TransactionAttribute(TransactionAttributeType.REQUIRED)
           public void bookTrip(long outboundFlightID, long returnFlightID, int seats)
           throws InsufficientSeatsException
           {
            reserveSeats(outboundFlightID, seats);
            reserveSeats(returnFlightID, seats);
           }
          }

            并且你可以應(yīng)用一個簡單的方面來自動地界定事務(wù)邊界:

          @Aspect
          public class TransactionAspect
          {
           @Pointcut("execution(@javax.ejb.TransactionAttribute * *.*(..))")
           public void transactionalMethods(){}
           @Before("transactionalMethods()")
           public void beforeTransactionalMethods()
           { HibernateUtil.beginTransaction(); }
           @AfterReturning("transactionalMethods()")
           public void afterReturningTransactionalMethods()
           { HibernateUtil.commitTransaction(); }
           @AfterThrowing("transactionalMethods()")
           public void afterThrowingTransactionalMethods()
           { HibernateUtil.rollbackTransaction(); }
          }

            這個實現(xiàn)基于這樣的假設(shè)-Hibernate和無所不在的線程本地模式被用于管理Hibernate會話和事務(wù)對象;但是,任何其它適當?shù)膶崿F(xiàn),例如基于JTA的實現(xiàn),都能被代替使用。

            六、 小結(jié)

            通過使用EJB 3.0和JSR 250注解集,本文已經(jīng)展示了例如資源管理、安全和事務(wù)等橫切關(guān)注點是怎樣被實現(xiàn)為方面的。當然,還有許多內(nèi)容我們需進一步學(xué)習(xí)。首先要學(xué)的就是通過使用AspectJ的實現(xiàn)這些示例方面為模塊化橫切關(guān)注點所提供的藍圖。其次,我們已經(jīng)看到了在如今正浮出水面的EJB 3.0聲明背后的一些新思想和新概念。最后,我們還以戲劇性的方式看到了從EJB API中解耦我們的商業(yè)對象所必須要提供的自由。在這一點上,所有你想使TravelAgencyServiceImpl成為一個無狀態(tài)會話要做的僅是添加一條最后的注解:

          @Stateful
          public class TravelAgencyServiceImpl implements ITravelAgencyService
          { ... }

            最后,我非常希望這種自由地提供企業(yè)服務(wù)的方式會帶來框架/容器工業(yè)界的競爭和革新。

          from: http://www.7dspace.com/doc/21/2005-11-21/2005112107135764791_1.htm

          posted on 2005-12-24 21:15 weidagang2046 閱讀(177) 評論(0)  編輯  收藏 所屬分類: Java

          主站蜘蛛池模板: 甘南县| 黔南| 二连浩特市| 乐都县| 平湖市| 获嘉县| 连江县| 康保县| 克东县| 邵阳市| 聂拉木县| 盘山县| 苏州市| 宁国市| 洪江市| 于田县| 托里县| 高陵县| 延寿县| 长治县| 青龙| 苗栗县| 中宁县| 牙克石市| 黄大仙区| 大理市| 邵东县| 介休市| 金阳县| 额济纳旗| 连云港市| 广河县| 宣城市| 岑溪市| 龙井市| 申扎县| 前郭尔| 衡水市| 大庆市| 黄山市| 登封市|