EJB 3.0+Aspect實現宣告式程式設計初步

來源:互聯網
上載者:User
編程   提要 本文將與你一同探討怎樣把註解和方面的威力聯合起來,以與EJB 3.0相容的方式為企業實現提供聲明性服務,而在同時仍然提供容器的獨立性。

   一、 引言

  在我們共同尋求進一步提高軟體開發生產效能的方法的過程中,我們-作為Java社團成員-一般都轉向J2EE來提供針對企業開發中更具挑戰性的技術問題如分散式交易管理、並發性和對象分布等的解決方案。其背後的指導思想-這些複雜的企業服務能被應用程式伺服器供應商所實現並能為商業開發人員所平衡-的確是一種很好的思想。J2EE,具體地說是EJB,已成功地提供了一個平台-在其上構建企業Java應用程式。

  這其中部分的成功是由於能夠進行宣告式程式設計-一種程式開發方式-用這種方式,你可以聲明基礎結構服務而不是用商業邏輯明確地編碼從而使代碼散佈於各處。EJB已經證明了這種編程方式的價值-通過允許企業問題例如事務和安全被用一種發布描述符所聲明並為容器所處理。

  然而,在過去的歲月中,越來越多的開發人員認識到EJB在團隊的生產效率方面給它自己帶來新的大量的挑戰-每個EJB必須伴隨多個介面,以一種發布描述符描述,經由JNDI被存取,等等。而在容器外EJB上進行單元測試也帶來另外的困難,如今EJB已不再把重點放在單純的物件導向開發上。

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

  ·Java 2 SDK 1.5

  ·Maven 2.0 Beta 2

  EJB 3.0的目標在於從以下幾個方面使企業開發更為容易:

  ·通過引入中繼資料註解來實現聲明性請求企業服務

  ·經由註解實現依賴性/資源注入

  ·實現企業beans與EJB特定介面的解耦

  ·經由輕量級的對象關係映射實現持久性儲存的簡化

  這對於EJB開發人員來說尤如一股春風-一直以來,他們竭力地從事開發、測試和維護EJB。利用EJB 3.0寫一個企業bean現在變得很容易,就如用特定的註解建立一個POJO(傳統的Java對象)以把它標明為一個EJB並請求企業服務。下面是一個來自於EJB 3.0 Public Draft中EJB的例子:

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


  EJB 3.0聲明中實質上指明開發人員需要的不是一重量級的、"一次發布滿足所有"的解決方案,而是一個輕量級的、容易使用的解決方案-為開發人員提供一定範圍的企業服務。為此,EJB 3.0所提供的最重要的方法之一就是實現企業beans與EJB API的解耦。並且,此解決方案還帶來令人感興趣的衍生-EJB現在不僅能夠運行在不同的EJB容器上,而且還能運行於任何應用程式架構內部-這些架構必須能夠識別EJB 3.0(JSR 220)和用於聲明企業服務的普通註解(JSR 250)。

  本文沒有提供關於宣告式程式設計、EJBs、方面或註解的深度探索。相反,而只是分析一下這些技術之間的相互關係並討論如何把它們用一種新的方式結合起來以簡化應用程式開發。

  在本文中,你將會學習到如何編寫一個EJB 3.0相容的bean並且通過建立幾個簡單的方面使其具有聲明性交易管理、安全和資源注入等功能。我希望您能從這個練習中得到以下的受益:

  ·學習方面的三個實際應用(依賴性注入、安全和事務)。

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

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

   二、 執行個體應用程式-航班訂購

  在整個後面的討論中,你將學習到一個航班訂購系統的實現-它使用方面和註解來實現依賴性注入、安全和交易管理。該應用程式僅執行兩項功能:它允許使用者搜尋航班(圖1),然後訂購一次旅行(圖2)。這兩個操作都將被進行安全處理以僅允許能被識別的使用者來執行它們。另外,既然"訂購旅行"操作包含訂購兩個航班(外出和返回航班),那麼需要把該操作建立為事務性的-如,兩個訂購將作為一個工作單元要麼都成功要麼都失敗。


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

圖2.航班訂購:接下來,使用者訂購一個外出航班和一個返回航班。兩個訂購要麼都成功要麼都失敗。
  這個簡單的Web應用程式套件組合含幾個servlet、一個服務外觀和一個DAO層(見圖3)。

  資源配置、安全性和交易管理等橫切關注點將由方面(用AspectJ 1.5 M3實現)所提供以實現在Java 5註解中所聲明的注入行為。


圖3.航班訂購系統架構:這個航班訂購系統包括三個主要組成組件-它們聯合起來共同完成使用者請求。   三、 資源注入

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

  以TravelAgencyServiceImpl類為例-這個類為了持久性儲存一些資料需要找到一個IFlightDAO介面的實現。傳統地,這是經由一個工廠、singleton、服務定位器或一些另外的定製解決方案來實現的。其中,一個可能的解決方案看上去如下所示:

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);
 }
}


  你已看到,這個實現包含建立一個特定的工廠類-它很可能讀取儲存在某處的配置資訊以瞭解要建立IFlightDAO的實現方式。如果不是讓服務顯式地建立它的由容器所注入的依賴性,那麼配置細節和對象建立將被代理到容器上。這允許一個應用程式中的組件能夠被容易地串連到一起-用不同的配置並且消除大量老式的singleton和工廠代碼。

  該類的一個實現-它依賴於一個用JSR 250資源註解所聲明的IFlightDAO的實現-可能看上去如下所示:

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"的資源的正確實現提供給服務類。但是,如果你現在就想利用資源注入,而不是等待EJB 3.0發行版,又該如何呢?好,你可以採用一種輕量級的容器-它能夠提供例如Spring或Pico Container的依賴性注入。然而,當前我還不瞭解存在一個輕量級的容器-它能夠使用JSR 250資源註解以指定注入要求(儘管我非常盼望在這一方面出現一些)。

  一種解決方案是使用方面來實現依賴性注入。如果你為此使用@Resource註解,那麼你的實現將與EJB 3.0方式一致並且向前相容EJB 3.0實現-而實現這並不是很困難的事情。下列列表顯示用AspectJ建立的一個方面-它注入用@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對象中)查詢實作類別並且在存取欄位之前把它注入到用@Resource註解所註解的欄位中。顯然,這種實現不是完整的,但是它確實說明了你可以怎樣以一種JSR 250相容方式且不需採用EJB來提供資源注入。

  四、 安全性

  除了資源注入外,JSR 250和EJB 3.0還提供經由註解的中繼資料安全表示。javax.annotation.security包定義了五個註解-RunAs,RolesAllowed,PermitAll,DenyAll和RolesReferenced-所有這些都能應用到方法上來定義安全要求。例如,如果你想要聲明上面列出的bookFlight方法僅能為具有"user"角色的調用者所執行,那麼你可以用如下的安全約束來註解這個方法:

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);
 }
}


  這個註解將指出由容器來負責保證只有指定角色的調用者才能執行這個方法。因此現在我將展示另一個簡單的方面-它將進一步加強在該應用程式上的安全約束:

@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)
 { ... }
}


  這個方麵包含了所有方法的執行-通過核實該調用者是在註解中所指定的角色之一,用@RolesAllowed註解來註解並且保證調用者被授權調用該方法。當然你還能代之以任何你喜歡的演算法來授權使用者並且檢索他/她的角色,例如JAAS或一個定製的解決方案。在本樣本程式中,為方便起見,我選擇代理到servlet容器。

  五、 事務

  事務成為企業開發的一個重要部分-因為它們有助於在一個並發的環境中的Data Integration。從一個高層次上看,事務可以通過多種或者是完整的或者是都不完整的操作來保證這一點。

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

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

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);
 }
}


  並且你可以應用一個簡單的方面來自動地界定事務邊界:

@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(); }
}


  這個實現基於這樣的假設-Hibernate和無所不在的執行緒區域模式被用於管理Hibernate會話和事務對象;但是,任何其它適當的實現,例如基於JTA的實現,都能被代替使用。

   六、 小結

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

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


  最後,我非常希望這種自由地提供企業服務的方式會帶來架構/容器工業界的競爭和革新。



相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

Starter Package

SSD Cloud server and data transfer for only $2.50 a month

Get Started >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。