Spring - Java/J2EE Application Framework 应用框架 第 8 章 源代码级的元数据支持
第?8?章?源代碼級的元數據支持
8.1.?源代碼級的元數據
源代碼級的元數據是對程序元素:通常為類和/或方法的?attribute?或者叫annotation的擴充。
舉例來說,我們可以象下面一樣給一個類添加元數據:
/*** Normal comments* @@org.springframework.transaction.interceptor.DefaultTransactionAttribute()*/ public class PetStoreImpl implements PetStoreFacade, OrderService {我們也可以添加元數據到一個方法上:
/*** Normal comments* @@org.springframework.transaction.interceptor.RuleBasedTransactionAttribute ()* @@org.springframework.transaction.interceptor.RollbackRuleAttribute (Exception.class)* @@org.springframework.transaction.interceptor.NoRollbackRuleAttribute ("ServletException")*/ public void echoException(Exception ex) throws Exception {.... }這兩個例子都使用了Jakarta Commons Attributes的格式。
源代碼級的元數據隨著Microsoft的.NET平臺的發布被介紹給大眾,它使用了源代碼級的attribute 來控制事務,緩沖池(pooling)和一些其他的行為。
這種方法的價值已經被J2EE社區的人們認識到了。舉例來說,跟EJB中清一色使用的傳統的XML部署描述文 件比起來它要簡單很多。XML描述文件適合于把一些東西從程序源代碼中提取出來,一些重要的企業級設 定——特別是事務特性——本來屬于程序代碼。并不像EJB規范中設想的那樣,調整一個方法的事務特性根本 沒有什么意義。
雖然元數據attribute主要用于框架的基礎架構來描述應用程序的類需要的業務, 但是也可以在運行時查詢元數據attribute。這是與XDoclet這樣的解決方案的關鍵區別,XDoclet 主要把元數據作為生成代碼的一種方式,比如生成EJB類。
這一段包括了幾個解決方案:
-
JSR-175:標準的Java元數據實現,在Java 1.5中提供。 但是我們現在就需要一個解決方案,通常情況下可能還需要一個外觀(facade)。
-
XDoclet:成熟的解決方案,主要用于代碼生成
-
其它不同的開源attribute實現,在JSR-175的發布 懸而未決的情況下,它們當中的Commons Attributes看來是最有前途的。所有的這些實現都需要一 個特定的前編譯或后編譯的步驟。
8.2.?Spring的元數據支持
為了與它提供的其他重要概念的抽象相一致,Spring提供了一個對元數據實現的外觀(facade), 以org.springframework.metadata.Attributes這個接口的形式來表示。
這樣一個外觀很有價值,因為下面幾個原因:
-
目前還沒有一個標準的元數據解決方案。 Java 1.5版本會提供一個,但是在Spring1.0版本的時候, Java 1.5仍是beta版本。而且,至少兩年內還是需要對1.3和1.4版本的應用程序提供元數據支持。?現在Spring打算提供一些可以工作的解決方案: 在一個重要的環境下等待1.5,并不是個好的選擇.
-
目前的元數據API,例如Commons Attributes(被Spring 1.0使用), 測試起來很困難。Spring提供了一個簡單的更容易模擬的元數據接口。
-
即使當Java 1.5在語言級別提供了對元數據的支持時,提供了一個如此的抽象仍然是有價值的:
-
JSR-175的元數據是靜態的。它是在編譯時與某一個類關聯,而在部署環境下是不可改變的。 這里會需要多層次的元數據,以支持在部署時重載某些attribute的值--舉例來說, 在一個XML文件中定義用于覆蓋的attribute。
-
JSR-175的元數據是通過Java反射API返回的。這使得在測試時無法模擬元數據。 Spring提供了一個簡單的接口來允許這種模擬。
-
雖然Spring在Java 1.5達到它的GA版本之前將支持JSR-175,但仍會繼續提供一個attribute抽象API。
Spring的Attributes接口看起來是這樣的:
public interface Attributes {Collection getAttributes(Class targetClass);Collection getAttributes(Class targetClass, Class filter);Collection getAttributes(Method targetMethod);Collection getAttributes(Method targetMethod, Class filter);Collection getAttributes(Field targetField);Collection getAttributes(Field targetField, Class filter); }這是個最普通不過的命名者接口。JSR-175能提供更多的功能,比如定義在方法參數上的attributes。 在1.0版本時,Spring目的在于提供元數據的一個子集,使得能象EJB或.NET一樣提供有效的聲明式企業級服務。 1.0版本以后,Spring將提供更多的元數據方法。
注意到該接口像.NET一樣提供了Object類型的attibute。 這使得它區別于一些僅提供String類的attribute的attribute系統, 比如Nanning Aspects和JBoss 4(在DR2版本時)。支持Object類型的attribute有一個顯著的優點。 它使attribute含有類層次,還可以使attribute能夠靈活的根據它們的配置參數起作用。
對于大多數attribute提供者來說,attribute類的配置是通過構造函數參數或JavaBean的屬性完成的。Commons Attributes同時支持這兩種方式。
同所有的Spring抽象API一樣,Attributes是一個接口。 這使得在單元測試中模擬attribute的實現變得容易起來。
8.3.?集成Jakarta Commons Attributes
雖然為其他元數據提供者來說,提供org.springframework.metadata.Attributes?接口的實現很簡單,但是目前Spring只是直接支持Jakarta Commons Attributes。
Commons Attributes 2.0?(http://jakarta.apache.org/commons/sandbox/attributes/) 是一個功能很強的attribute解決方案。它支持通過構造函數參數和JavaBean屬性來配置attribute, 也提供了更好的attribute定義的文檔。(對JavaBean屬性的支持是在Spring team的要求下添加的。)
我們已經看到了兩個Commons Attributes的attribute定義的例子。通常,我們需要解釋一下:
-
Attribute類的名稱。 這可能是一個FQN,就像上面的那樣。如果相關的attribute類已經被導入, 就不需要FQN了。你也可以在attibute編譯器的設置中指定attribute的包名。
-
任何必須的參數化,可以通過構造函數參數或者JavaBean屬性完成。
Bean的屬性如下:
/*** @@MyAttribute(myBooleanJavaBeanProperty=true)*/把構造函數參數和JavaBean屬性結合在一起也是可以的(就像在Spring IoC中一樣)。
因為,并不象Java 1.5中的attribute一樣,Common Attributes沒有和Java語言本身結合起來, 因此需要運行一個特定的attribute編譯的步驟作為整個構建過程的一部分。
為了在整個構建過程中運行Commmons Attributes,你需要做以下的事情。
1.復制一些必要的jar包到$ANT_HOME/lib目錄下。 有四個必須的jar包,它們包含在Spring的發行包里:
-
Commons Attributes編譯器的jar包和API的jar包。
-
來自于XDoclet的xjavadoc.jar
-
來自于Jakarta Commons的commons-collections.jar
2.把Commons Attributes的ant任務導入到你的項目構建腳本中去,如下:
<taskdef resource="org/apache/commons/attributes/anttasks.properties"/>3.接下來,定義一個attribute編譯任務,它將使用Commons Attributes的attribute-compiler任務 來“編譯”源代碼中的attribute。這個過程將生成額外的代碼至destdir屬性指定的位置。 在這里我們使用了一個臨時目錄:
<target name="compileAttributes" ><attribute-compiler destdir="${commons.attributes.tempdir}" ><fileset dir="${src.dir}" includes="**/*.java"/></attribute-compiler></target>運行javac命令編譯源代碼的編譯目標任務應該依賴于attribute編譯任務,還需要編譯attribute時生成至 目標臨時目錄的源代碼。如果在attribute定義中有語法錯誤,通常都會被attribute編譯器捕獲到。 但是,如果attribute定義在語法上似是而非,卻使用了一些非法的類型或類名, 編譯所生成的attribute類可能會失敗。在這種情況下,你可以看看所生成的類來確定錯誤的原因。
Commons Attributes也提供對Maven的支持。請參考Commons Attributes的文檔得到進一步的信息。雖然attribute編譯的過程可能看起來復雜,實際上是一次性的耗費。一旦被創建后,attribute的編譯是遞增式的, 所以通常它不會減慢整個構建過程。一旦編譯過程完成后, 你可能會發現本章中描述的attribute的使用將節省在其他方面的時間。
如果需要attribute的索引支持(目前只在Spring的以attribute為目標的web控制器中需要,下面會討論到), 你需要一個額外的步驟,執行在包含編譯后的類的jar文件上。在這步可選的步驟中, Commons Attributes將生成一個所有在你源代碼中定義的attribute的索引,以便在運行時進行有效的查找。 該步驟如下:
<attribute-indexer jarFile="myCompiledSources.jar"><classpath refid="master-classpath"/></attribute-indexer> 可以到Spring jPetStore例程下的attributes目錄下察看關于該構建過程的例子。 你可以使用它里面的構建腳本,并修改該腳本以適應你自己的項目。如果你的單元測試依賴于attribute,盡量使它依賴于Spring對于Attribute的抽象,而不是Commons Attributes。 這不僅僅為了更好的移植性——舉例來說,你的測試用例將來仍可以工作如果你轉換至Java 1.5的attributes—— 它也簡化了測試。Commons Attributes是靜態的API,而Spring提供的是一個容易模擬的元數據接口。
8.4.?元數據和Spring AOP自動代理
元數據attributes最重要的用處是和Spring AOP的聯合。 提供類似于.NET風格的編程模式,聲明式的服務會被自動提供給聲明了元數據attribute的應用對象。 這樣的元數據attribute可以像在聲明式事務管理一樣被框架直接支持,也可以是自定義的.
這就是AOP和元數據attribute配合使用的優勢所在。
8.4.1.?基礎
基于Spring AOP自動代理功能實現。配置可能象這樣:
<bean id="autoproxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> </bean><bean id="transactionAttributeSource"class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource"autowire="constructor"> </bean><bean id="transactionInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor"autowire="byType"> </bean><bean id="transactionAdvisor"class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"autowire="constructor" > </bean><bean id="attributes"class="org.springframework.metadata.commons.CommonsAttributes" />這里的基本概念和AOP章節中關于自動代理的討論一致。
最重要的bean的定義名稱為autoproxy和?transactionAdvisor。要注意bean的實際名稱并不重要; 關鍵是它們的類。
所定義的自動代理bean?org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator?將自動通知(“自動代理”)當前工廠類中的所有符合Advisor實現的bean實例, 這個類并不知道attribute,而是依賴符合的Advisors的切入點。這些切入點知道attribute的內容。
因而我們只是需要一個AOP的advisor來提供基于attribute的聲明式事務管理。
也可以添加任意的自定義Advisor實現,它們將被自動計算并應用。 (如果有必要的話,你可以使用切入點除了符合自動代理配置的attribute,而且滿足一定規則的Advisor。)
最后,attributes?bean使用的是Commons Attributes的實現。 也可以用org.springframework.metadata.Attributes的另一個實現代替。
8.4.2.?聲明式事務管理
源代碼級的attribute的最普通用法是提供了類似.NET的聲明式事務管理。一旦定義好上面的bean定義, 你就能定義任意數目的需要聲明式事務的應用對象了。只有那些擁有事務attribute的類或方法會被有事務的通知。 除了定義你需要的事務attribute以外,其他什么都不用做。
不象在.NET中那樣,你可以在類或方法級別指定事務attribute。 如果指定了類級別的attribute,它的所有方法都會“繼承”它。 方法中定義的attribute將完全覆蓋類上定義的attribute。
8.4.3.?緩沖池技術
再者,象.NET中一樣,你可以通過在類上指定attribute增加緩沖池行為。 Spring能把該行為應用到任何普通Java對象上。你只需在需要緩沖的業務對象上指定一個緩沖池的attribute,如下:
/** * @@org.springframework.aop.framework.autoproxy.target.PoolingAttribute (10)* * @author Rod Johnson*/ public class MyClass {你需要通常的自動代理的基本配置。 然后指定一個支持緩沖池的TargetSourceCreator,如下所示。 由于緩沖池會影響目標對象的創建,我們不能使用一個通常的advice。 注意如果一個類有緩沖池的attribute,即使沒有任何advisors應用到該類,緩沖池也會對該類起作用。
<bean id="poolingTargetSourceCreator"class="org.springframework.aop.framework.autoproxy.metadata.AttributesPoolingTargetSourceCreator"autowire="constructor" > </bean>對應的自動代理bean定義需要指定一系列的“自定義的目標源生成器”,包括支持緩沖池的目標源生成者。 我們可以修改上面的例子來引入這個屬性,如下:
<bean id="autoproxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> ><property name="customTargetSourceCreators"><list><ref local="poolingTargetSourceCreator" /></list></property> </bean>就像通常在Spring中使用元數據一樣,這是一次性的耗費:一旦創建完成后, 在其它業務對象上使用緩沖池是非常容易的。
對于很少需要緩沖池技術的地方,很少需要緩沖大量業務對象,這是就值得斟酌了。 因此這個功能好像也不是經常使用。詳細請參考org.springframework.aop.framework.autoproxy包的Javadoc。 除了以最少的代碼量使用Commons Pool以外,其它的緩沖池實現也是可以的。
8.4.4.?自定義的元數據
由于自動代理底層架構的靈活性,我們甚至還可以超越.NET元數據attribute的功能。
我們可以定義一些自定義的attribute來提供任何種類的行為。為了達到這個目的,你需要:
-
定義你的自定義attribute類
-
定義一個Spring AOP Advisor,自定義attributes的出現的時候觸發它的切入點。
-
把Advisor當作一個bean的定義添加到一個包含普通自動代理基礎架構的應用上下文中。
-
把attribute添加到普通Java對象上。
有幾個潛在的領域中你可能想這么做,比如自定義的聲明式安全管理,或者可能的緩存。
這是一個功能很強的機制,它能顯著減少某些工程中的配置工作。但是,要記住它在底層是依賴AOP的。 你在應用的使用的Advisors越多,運行時配置將會越復雜。(如果你想查看object上應用了哪些通知, 可以看一下關于org.springframework.aop.framework.Advised的參考。它使你能夠檢查相關的Advisors。)8.5.?使用attribute盡可能減少MVC web層配置
Spring 1.0中的元數據的另一個主要用法是為簡化Spring MVC web配置提供了一個選擇。
Spring MVC提供了靈活的處理器映射: 將外來的請求映射到控制器(或其它的處理器)實例上。 通常上處理器映射被配置在相應的Spring DispatcherServlet的xxx-servlet.xml文件中。
把這些配置定義在DispatcherServlet的配置文件中通常是個不錯的選擇。它提供了最大的靈活性。 特別的是:
-
Controller實例是顯式的被Spring IoC通過XML bean定義來管理。
-
該映射位于controller的外面, 所以相同controller的實例可以在同一個DispatcherServlet中被賦予不同的映射或者在不同的配置中重用。
-
Spring MVC能夠支持在任何規則上的映射,而不是大多數其它框架中僅有的將請求URL映射到控制器。
然而,對于每個controller來說,我們同時需要一個處理器映射(通常是一個處理器映射的XML bean定義), 和一個控制器自身的一個XML映射描述。
Spring提供了一個基于源代碼級attribute的更簡單的方法,對于一些簡單場景來說是一個吸引人的選擇。
這一段描述的方法很適合一些簡單的MVC場景。但它犧牲了一些Spring MVC的功能, 比如根據不同的映射使用相同的控制器,把映射基于除了請求的URL之外的規則。在這種方法下,控制器上標記了一個或多個類級的元數據attribute, 每一個attribute指定了它們應該映射的一個URL。
下面的例子展示了這種方法。在每一種情況下,我們都有一個依賴于Cruncher類的業務對象控制器。 和往常一樣,這種依賴性將通過依賴注射來解決。 Cruncher必須作為一個bean定義出現在相關的DispatcherServlet的XML文件中,或在一個父上下文中。
我們把一個attribute設置到控制器類上,并指定應該映射的URL。 我們可以通過JavaBean屬性或構造函數參數來表達依賴關系。這種映射必須通過自動裝配來解決: 那就是說,在上下文中必須只能有一個Crucher類型的業務對象。
/*** Normal comments here* @author Rod Johnson* @@org.springframework.web.servlet.handler.metadata.PathMap("/bar.cgi")*/ public class BarController extends AbstractController {private Cruncher cruncher;public void setCruncher(Cruncher cruncher) {this.cruncher = cruncher;}protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {System.out.println("Bar Crunching c and d =" + cruncher.concatenate("c", "d"));return new ModelAndView("test");}}為了使這個自動映射起作用,我們需要添加下面的配置到相關的xxxx-servlet.xml文件中, 這些配置是用來指定相關attribute的處理器映射的。 這個特殊的處理器映射可以處理任意數量的象上面一樣帶有attribute的控制器。 Bean的id(“commonsAttributesHandlerMapping”)并不重要。關鍵是它的類型:
<bean id="commonsAttributesHandlerMapping" class="org.springframework.web.servlet.handler.metadata.CommonsPathMapHandlerMapping" />現在我們并不需要一個象上面例子中的Attributes bean的定義, 因為這個類直接利用Commons Attributes API,而不是通過Spring的元數據抽象。
現在我們不再需要對每一個控制器提供XML配置。控制器被自動映射到指定的URL上。 控制器得益于IoC,使用了Spring的自動裝配功能。舉例來說, 上述的簡單控制器中的“cruncher”屬性上的依賴性會自動在當前的web應用中解決. Setter方法和構造函數依賴注射都是可以的,每一個都是零配置。
構造函數注冊的一個例子,同時演示了多個URL路徑:
/** * Normal comments here * @author Rod Johnson * * @@org.springframework.web.servlet.handler.metadata.PathMap("/foo.cgi") * @@org.springframework.web.servlet.handler.metadata.PathMap("/baz.cgi") */ public class FooController extends AbstractController {private Cruncher cruncher;public FooController(Cruncher cruncher) {this.cruncher = cruncher;}protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {return new ModelAndView("test");}}這種方法有著下列的好處:
-
顯著的減少配置工作量。每一次我們增加一個控制器時,我們不需要XML的配置。 就像attribute驅動的事務管理一樣,一旦有了基礎架構后,添加更多的應用類將會很簡單。
-
我們保留了Spring IoC配置控制器的能力。
不過該方法有如下的限制:
-
這個方法使構建過程更加復雜,不過開銷是一次性的。 我們需要一個attribute編譯步驟和一個建立attribute的索引步驟。 但是,一旦這些步驟就位后,這將不再是一個問題。
-
雖然將來會增加對其他attribute提供者的支持,但目前只支持Commons Attributes。
-
只有“根據類型的自動裝配”的依賴注射才支持這樣的控制器。但是, 這妨礙這些控制器對Structs Actions(在框架中并沒有IoC的支持), 和值得斟酌的WebWork Actions(只有基本的IoC支持,并且IoC逐步受到關注)的領先。
-
依賴自動魔法的IoC解決方案可能是令人困惑的。
由于根據類型的自動裝配意味著必須依賴一種指定類型,如果我們使用AOP就需要小心了。 在通常使用TransactionProxyFactoryBean的情況下,舉例來說, 我們對一個業務接口例如Cruncher有兩個實現: 一個為原始的普通Java對象的定義,另一個是帶有事務的AOP代理。這將無法工作, 因為所有者應用上下文無法確切的解析這種類型依賴。解決方法是使用AOP的自動代理, 建立起自動代理的基本架構,這樣就只有一個Crucher的實現, 同時該實現是自動被通知的。 因此這種方法和上面的以attribute為目標聲明式服務能很好的一起工作。 因為attribute編譯過程必須恰當處理web控制器的定位,所有這就很容易被創建
不象其它的元數據的功能,當前只有Commons Attributes的一種實現: org.springframework.web.servlet.handler.metadata.CommonsPathMapHandlerMapping。 這個限制是因為我們不僅需要attribute編譯,還需要attribute索引: 根據attribute API來查詢所有擁有PathMap attribute的類。雖然將來可能會提供, 但目前在org.springframework.metadata.Attributes的抽象接口上還沒有提供索引。 (如果你需要增加另一個attribute實現的支持,并且必須支持索引, 你可以簡單的繼承AbstractPathMapHandlerMapping,CommonsPathMapHandlerMapping的父類, 用你選擇的attribute API來實現其中兩個protected的虛方法。)
因此我們在構建過程中需要兩個額外的步驟:attribute編譯和建立attribute索引。 上面已經展示了attributer索引建立器的使用方法。要注意到目前Commons Attributes仍需要一個Jar文件來建立索引。
如果你開始使用控制器元數據映射方法,你可以在任何一個地方轉換至經典的Spring XML映射方法。 所以你不需要拒絕這種選擇。正因為這個原因,我發現我經常在開始一個web應用時使用元數據映射.8.6.?元數據attribute的其它使用
對元數據attribute的其它應用看來正在流行。到2004年3月時, 一個為Spring建立的基于attribute的驗證包正在開發中。 當考慮到潛在的多次使用時,attribute解析的一次性開銷看起來更加吸引人了.
8.7.?增加對其它的元數據API的支持
如果你希望提供對另一種元數據API的支持,這是容易做到的。
簡單的實現org.springframework.metadata.Attributes?接口作為你選擇的元數據API的 外觀(facade)。然后你就可以象上面那樣把這個對象包括到你的bean定義中去。
所有使用元數據的框架服務,例如AOP元數據驅動的自動代理,將自動使用你的新元數據提供者。
我們期待著增加對Java 1.5的attribute的支持--可能作為Spring核心的附件--在2004年第二季度。from:?http://docs.huihoo.com/spring/zh-cn/metadata.html
總結
以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 8 章 源代码级的元数据支持的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - Java/J2EE A
- 下一篇: Spring - Java/J2EE A