?
Ce document utilise Manuel du site Web PHP chinois Libérer
到目前為止本章討論的一直是純Spring AOP。在這一節(jié)里面我們將介紹如何使用AspectJ compiler/weaver 來代替Spring AOP或者作為它的補充,因為有些時候Spring AOP單獨提供的功能也許并不能滿足你的需要。
Spring提供了一個小巧的AspectJ aspect library,你可以在程序發(fā)行版本中單獨使用
spring-aspects.jar
文件,并將其加入到classpath下以使用其中的切面。
第?6.8.1?節(jié) “在Spring中使用AspectJ進行domain object的依賴注入”和第?6.8.2?節(jié) “Spring中其他的AspectJ切面” 討論了該庫以及如何使用該庫。
第?6.8.3?節(jié) “使用Spring IoC來配置AspectJ的切面”討論了如何對通過AspectJ compiler織入的AspectJ切面進行依賴注入。
最后第?6.8.4?節(jié) “在Spring應用中使用AspectJ加載時織入(LTW)”介紹了使用AspectJ的Spring應用程序如何進行加載期織入(load-time weaving)。
Spring容器對application context中定義的bean進行實例化和配置。同樣也可以通過bean factory
來為一個已經存在且已經定義為spring bean的對象應用所包含的配置信息。
spring-aspects.jar
中包含了一個annotation-driven的切面,
提供了能為任何對象進行依賴注入的能力。這樣的支持旨在為
脫離容器管理而創(chuàng)建的對象進行依賴注入。領域對象經常處于這樣的情形:
它們可能是通過new
操作符創(chuàng)建的對象,也可能是由ORM工具查詢數據庫所返回的結果。
@Configurable
注解標記了一個類可以通過Spring-driven方式來配置。
在最簡單的情況下,我們只把它當作標記注解:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
// ...
}
當只是簡單地作為一個標記接口來使用的時候,Spring將采用和該已注解的類型
(比如Account
類)全名(com.xyz.myapp.domain.Account
)
一致的bean原型定義來配置一個新實例。由于一個bean默認的名字就是它的全名,
所以一個比較方便的辦法就是省略定義中的id
屬性:
<bean class="com.xyz.myapp.domain.Account" scope="prototype"> <property name="fundsTransferService" ref="fundsTransferService"/> </bean>
如果你希望明確的指定bean原型定義的名字,你可以在注解中直接定義:
package com.xyz.myapp.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("account")
public class Account {
// ...
}
Spring會查找名字為"account"的bean定義,并使用它作定義來配置一個新的
Account
實例。
你也可以使用自動裝配來避免手工指定原型定義的名字。只要設置@Configurable
注解中的autowire
屬性就可以讓Spring進行自動裝配:
指定@Configurable(autowire=Autowire.BY_TYPE)
或者
@Configurable(autowire=Autowire.BY_NAME
可以讓自動裝配分別按照類型或名字進行。
作為另外一種選擇,在Spring2.5中最好是在域或方法級使用@Autowired
和
@Resource
為你的@Configurable
beans指定
明確的、注解驅動的依賴注入。(詳情請參看第?3.11?節(jié) “基于注解(Annotation-based)的配置”)
最后,你可以通過使用dependencyCheck
屬性,讓Spring對新創(chuàng)建和配置的對象的對象引用進行
依賴檢查(例如:@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)
)。
如果這個屬性設置為true,Spring會在配置結束后校驗(除了primitives和collections類型)
所有的屬性是否都被設置。
僅僅使用注解并沒有做任何事情。但是spring-aspects.jar
中的AnnotationBeanConfigurerAspect
會在注解存在時起作用。實質上切面指明:
“在初始化一個由@Configurable
注解的新對象時,
Spring按照注解中的屬性來配置這個新創(chuàng)建的對象”。這種情況下,initialization
指新初始化的(比如用new
初始化)的對象以及能進行反序列化的
Serializable
對象(例如通過
readResolve()方法)。
在上一段中一個關鍵的階段就是“inessence”。多數情況下,“
當從一個新對象初始化返回之后”的精確語義很不錯...這種語境下,
“初始化之后”的意思是依賴將在對象被構造之后注入 -
這意味著在類的構造器塊中依賴將不可用。如果你希望它能在構造器代碼塊執(zhí)行
之前被注入,并從而在構造器中使用它,
那么你需要在@Configurable
接口聲明上做類似的定義:
@Configurable(preConstruction=true)
你可以在 AspectJ Programming Guide一書的附錄中 找到更多有關在AspectJ中各種切面類型的語義信息。
要實現(xiàn)上述的操作,已注解的類型必須由AspectJ weaver來織入 - 你可以使用一個構建時的ant/maven任務來完成
(參見AspectJ Development Environment Guide)或者使用加載時織入(參見 第?6.8.4?節(jié) “在Spring應用中使用AspectJ加載時織入(LTW)”)。
類AnnotationBeanConfigurerAspect
本身也需要Spring來配置(獲得bean factory的引用,使用bean factory配置新的對象)。為此Spring的
context
命名空間
定義了一個非常方便的標簽。只要簡單的在application context配置中包含下面的內容。
<context:spring-configured/>
如果你使用DTD代替Schema,對應的定義如下:
<bean class="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect" factory-method="aspectOf"/>
在切面配置完成之前創(chuàng)建的@Configurable
對象實例會導致在log中留下一個warning,并且任何對于該對象的配置都不會生效。
舉一個例子,一個Spring管理配置的bean在被Spring初始化的時候創(chuàng)建了一個domain object。
對于這樣的情況,你需要定義bean屬性中的"depends-on"屬性來手動指定該bean依賴于configuration切面。
<bean id="myService"
class="com.xzy.myapp.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
提供@Configurable
支持的一個目的就是使得domain object的單元測試
可以獨立進行,不需要通過硬編碼查找各種倚賴關系。如果@Configurable
類型沒有通過AspectJ織入,則在單元測試過程中注解不會起到任何作用,
測試中你可以簡單的為對象的mock或者stub屬性賦值,并且和正常情況一樣去使用該對象。
如果@Configurable
類型通過AspectJ織入,
我們依然可以脫離容器進行單元測試,不過每次創(chuàng)建一個新的@Configurable
對象時都會看到一個warning,標示該對象沒有被Spring配置。
AnnotationBeanConfigurerAspect
通過一個AspectJ singleton切面來實現(xiàn)對
@Configurable
的支持。一個singleton切面的作用域和一個
靜態(tài)變量的作用域是一樣的,那就是說,對于每一個classloader有一個切面來定義類型。
這就意味著如果你在一個classloader層次結構中定義了多個application context的時候就需要考慮
在哪里定義<aop:spring-configured/>
bean和在哪個classpath下
放置spring-aspects.jar
。
考慮一下典型的Spring web項目,一般都是由一個父application context定義大部分business service和
所需要的其他資源,然后每一個servlet擁有一個子application context定義。所有這些context共存于
同一個classloader體系下,因此AnnotationBeanConfigurerAspect
僅保持
一個對context的引用。在這樣的情況下,我們推薦在父application context中定義
<aop:spring-configured/>
bean:這里所定義的service可能是
你希望注入domain object的。這樣做的結果是你不能為子application context中
使用@Configurable的domain object配置bean引用(可能你也根本就不希望那么做?。?。
當在一個容器中部署多個web-app的時候,請確保每一個web-application使用自己的classloader 來加載spring-aspects.jar中的類(例如將spring-aspects.jar放在WEB-INF/lib目錄下)。 如果spring-aspects.jar被放在了容器的classpath下(因此也被父classloader加載),則所有的 web application將共享一個aspect實例,這可能并不是你所想要的。
除了@Configurable
切面,
spring-aspects.jar
包含了一個AspectJ切面可以用來為
那些使用了@Transactional
注解的類型和方法驅動Spring事務管理。
提供這個的主要目的是有些用戶希望脫離Spring容器使用Spring的事務管理。
解析@Transactional
注解的切面是
AnnotationTransactionAspect
。當使用這個切面時,
你必須注解這個實現(xiàn)類(和/或這個類中的方法),而不是
這個類實現(xiàn)的接口(如果有)。AspectJ允許在接口上注解的Java規(guī)則 不被繼承。
類之上的一個@Transactional
注解為該類中任何
public操作的執(zhí)行指定了默認的事務語義。
類內部方法上的一個@Transactional
注解會覆蓋類注解(如果存在)
所給定的默認的事務語義。具有public、protected和default修飾符的方法都可以被注解。
直接注解protected和default方法是讓這個操作的執(zhí)行獲得事務劃分的唯一途徑。
對于AspectJ程序員,希望使用Spring管理配置和事務管理支持,不過他們不想(或者不能)使用注解,
spring-aspects.jar
也包含了一些抽象
切面供你繼承來提供你自己的切入點定義。參見AbstractBeanConfigurerAspect
和AbstractTransactionAspect
的Javadoc獲取更多信息。
作為一個例子,下面的代碼片斷展示了如何編寫一個切面,然后通過和類全名匹配的bean原型定義來
配置domian object中定義的所有實例:
public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {
public DomainObjectConfiguration() {
setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
}
// the creation of a new bean (any object in the domain model)
protected pointcut beanCreation(Object beanInstance) :
initialization(new(..)) &&
SystemArchitecture.inDomainModel() &&
this(beanInstance);
}
當在Spring application中使用AspectJ的時候,很自然的會想到用Spring來管理這些切面。 AspectJ runtime自身負責切面的創(chuàng)建,這意味著通過Spring來管理AspectJ 創(chuàng)建切面依賴于切面所使用的AspectJ instantiation model(per-clause)。
大多數AspectJ切面都是singleton切面。管理這些切面非常容易,
和通常一樣創(chuàng)建一個bean定義引用該切面類型就可以了,并且在bean定義中包含
'factory-method="aspectOf"'
這個屬性。
這確保Spring從AspectJ獲取切面實例而不是嘗試自己去創(chuàng)建該實例。示例如下:
<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf">
<property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>
non-singleton切面的配置稍難一點,然而它可以通過定義一個bean原型定義并且使用
spring-aspects.jar
中的@Configurable支持,
當切面實例由AspectJ runtime創(chuàng)建后進行配置。
如果你希望一些@AspectJ切面使用AspectJ來織入(例如使用load-time織入domain object)
而另一些@AspectJ切面使用Spring AOP,并且這些切面都由Spring來管理,那你就需要告訴Spring AOP
@AspectJ自動代理支持那些切面需要被自動代理。你可以通過在
<aop:aspectj-autoproxy>
聲明中使用一個或多個
<include/>
元素。每個元素指定了一種命名格式,
只有bean命名至少符合其中一種情況下才會使用Spring AOP自動代理配置:
<aop:aspectj-autoproxy> <aop:include name="thisBean"/> <aop:include name="thatBean"/> </aop:aspectj-autoproxy>
不要被<aop:aspectj-autoproxy/>
元素的名字所誤導:
用它會導致Spring AOP 代理的創(chuàng)建。在這中只是使用@AspectJ
類型的切面聲明,但并不會涉及AspectJ運行時。
加載時織入(Load-time weaving(LTW))指的是在虛擬機載入字節(jié)碼文件時動態(tài)織入AspectJ切面。 本 節(jié)關注于在Spring Framework中特的定context下配置和使用LTW:并沒有LTW的介紹。 關于LTW和僅使用AspectJ配置LTW的詳細信息(根本不涉及Spring),請查看 LTW section of the AspectJ Development Environment Guide。
Spring框架的值添加為AspectJ LTW在動態(tài)織入過程中提供了更細粒度的控制。使用Java(5+)的代理
能使用一個叫‘Vanilla’的AspectJ LTW,這需要在啟動JVM的時候將某個VM參數設置為開。
這種JVM范圍的設置在一些情況下或許不錯,但通常情況下顯得有些粗顆粒。而用Spring的LTW能讓你在
per-ClassLoader
的基礎上打開LTW,
這顯然更加細粒度并且對“單JVM多應用”的環(huán)境更具意義(例如在一個典型應用服務器環(huán)境中一樣)。
另外,在某些環(huán)境下,這能讓你使用LTW而 不對應用服務器的啟動腳本做任何改動,不然則需要添加 -javaagent:path/to/aspectjweaver.jar或者(以下將會提及的)-javaagent:path/to/spring-agent.jar。 開發(fā)人員只需簡單修改應用上下文的一個或幾個文件就能使用LTW,而不需依靠那些管理著部署配置 比如啟動腳本的系統(tǒng)管理員。
經過以上講解之后,先讓我們來過一遍一個使用Spring的AspectJ LTW的快速示例,接著是一個 有對元素詳細講解的示例。如果想要一個完整的示例,請參看Petclinic(寵物診所)的應用實例。
假設你是一個應用開人員,被指派診斷一個系統(tǒng)的若干性能問題。與其拿出性能分析工具, 我們不如開啟一個簡單的分析切面,使我們能很快地得到一些性能指標,這樣我們就能馬上 針對特定區(qū)域使用一些較細粒度的分析工具。
這就是一個分析切面。沒什么特別的,只是一個快餐式的基于時間的模擬分析器, 使用類@AspectJ風格的切面聲明。
package foo; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Pointcut; import org.springframework.util.StopWatch; import org.springframework.core.annotation.Order; @Aspect public class ProfilingAspect { @Around("methodsToBeProfiled()") public Object profile(ProceedingJoinPoint pjp) throws Throwable { StopWatch sw = new StopWatch(getClass().getSimpleName()); try { sw.start(pjp.getSignature().getName()); return pjp.proceed(); } finally { sw.stop(); System.out.println(sw.prettyPrint()); } } @Pointcut("execution(public * foo..*.*(..))") public void methodsToBeProfiled(){} }
我們還需要創(chuàng)建一個“META-INF/aop.xml
”文件,以告知AspectJ weaver
我們要把ProfilingAspect
織入到類中。這個文件慣例,即在Java classpath中
出現(xiàn)一個文件稱作“META-INF/aop.xml
”是標準的AspectJ。
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"> <aspectj> <weaver> <!-- only weave classes in our application-specific packages --> <include within="foo.*"/> </weaver> <aspects> <!-- weave in just this aspect --> <aspect name="foo.ProfilingAspect"/> </aspects> </aspectj>
現(xiàn)在來看Spring特定的配置部分。我們需要配置一個LoadTimeWeaver
(稍后會有解釋,暫時不多深究)。當將一個或多個“META-INF/aop.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <!-- a service object; we will be profiling it's methods --> <bean id="entitlementCalculationService" class="foo.StubEntitlementCalculationService"/> <!-- this switches on the load-time weaving --> <context:load-time-weaver/> </beans>
現(xiàn)在萬事俱備 - 切面,META-INF/aop.xml
文件,以及Spring的配置 -
讓我們創(chuàng)建一個帶有main(..)
方法的簡單驅動類來演示LTW的作用吧。
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);
EntitlementCalculationService entitlementCalculationService
= (EntitlementCalculationService) ctx.getBean("entitlementCalculationService");
// the profiling aspect is 'woven' around this method execution
entitlementCalculationService.calculateEntitlement();
}
}
最后還有一件事要做。此節(jié)之前的介紹說過可以有選擇性的基于Spring的
per-ClassLoader
來啟動LTW,而且的確如此。不過,對此例來說,
我們將使用Java代理(由Spring提供)來啟動LTW。這個就是用以運行上面Main
類的命令行語句:
java -javaagent:C:/projects/foo/lib/global/spring-agent.jar foo.Main
-javaagent
是一個Java 5+標記,用來指定和激活
使JVM上的程序運行的代理。Spring框架裝載了一個InstrumentationSavingAgent
代理,在上面的例子中被作為了-javaagent
參數的值打包在
spring-agent.jar
中。
Main
程序運行的輸出如下所示。(我已經在
calculateEntitlement()
的實現(xiàn)中插入了Thread.sleep(..)
語句,以免讓模擬分析器獲取0毫秒 - 這里的01234
毫秒并非是AOP引入的系統(tǒng)開銷。)
Calculating entitlement StopWatch 'ProfilingAspect': running time (millis) = 1234 ------ ----- ---------------------------- ms % Task name ------ ----- ---------------------------- 01234 100% calculateEntitlement
因為這個LTW使用成熟的AspectJ,我們并不局限于通知Spring beans的方法;接下來這個稍有變化的
Main
程序將生成同樣的結果。
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("beans.xml", Main.class);
EntitlementCalculationService entitlementCalculationService =
new StubEntitlementCalculationService();
// the profiling aspect will be 'woven' around this method execution
entitlementCalculationService.calculateEntitlement();
}
}
注意以上程序我們只是引導了Spring容器,然后完全在Spring上下文之外創(chuàng)建了一個
StubEntitlementCalculationService
的實例...分析通知仍然得到織入。
上面的例子雖然簡單了些,但Spring中基本的LTW支持都已介紹完了, 此節(jié)余下內容將對使用這些配置和用法背后的理由作詳細解釋。
類ProfilingAspect
在此例中雖然基本但是頗為有用。這是一個很好的開發(fā)時切面的例子,開發(fā)者可以在開發(fā)過程中使用它(廢話),
然后也能從已部署到UAT或者生產環(huán)境的應用中輕易的脫離。
你在LTW中使用的切面必須是AspectJ切面。你可以使用AspectJ語言或者類@AspectJ風格來編寫你的切面。 后一種方式當然只能在Java 5+中使用,但它說明了你的切面可以同時對AspectJ和Spring AOP切面有效。 此外,編譯后的切面類需要被注冊到classpath下。
AspectJ LTW的基礎設施是用一個或多個位于Java classpath上的(可以是直接的文件形式,
也可以是更典型的jar包形式)META-INF/aop.xml
文件配置起來的。
有關文件的結構和內容都在AspectJ的參考文檔中有詳細介紹,有興趣的讀者
請參考這些資源。(很慶幸這一節(jié)比較簡短,但aop.xml
文件
是100% AspectJ的 - 沒有任何使用Spring特定的信息或語義,因此我也沒有什么可貢獻的。
與其重寫這些已由AspectJ開發(fā)者提供的令人滿意的章節(jié),我不如領你到這里。)
你至少需要以下類庫來讓Spring框架支持AspectJ LTW:
spring.jar
(2.5或更高版本)
aspectjrt.jar
(1.5或更高版本)
aspectjweaver.jar
(1.5或更高版本)
如果你正在使用 由Spring提供的代理來激活檢測(instrumentation)功能,你會需要:
spring-agent.jar
Spring LTW功能的關鍵組件是LoadTimeWeaver
接口
(在org.springframework.instrument.classloading
包中),
以及Spring分發(fā)包中大量的實現(xiàn)。LoadTimeWeaver
的實現(xiàn)負責
在運行時把一個或多個java.lang.instrument.ClassFileTransformers
類添加到
ClassLoader
中,這能產生各種各樣有趣的應用,LTW切面恰好便是其中之一。
如果你對運行時類文件變換的思想還不熟悉,推薦你在繼續(xù)之前閱讀
java.lang.instrument
包的Javadoc API文檔。
這其實并不難-反而有些惱人-因為有用的文件并不多...關鍵的接口和類都將會在此節(jié)呈現(xiàn)給你。
用XML為ApplicationContext
配置一個
LoadTimeWeaver
簡單得只需要添加一行。
(請注意幾乎肯定你需要使用ApplicationContext
作為你的
Spring容器 - 一般來說只有BeanFactory
是不夠的,
因為LTW功能需要用到BeanFactoryPostProcessors
。)
當要使用Spring框架的LTW功能時,你需要配置一個LoadTimeWeaver
,
一般可以用<context:load-time-weaver/>
元素來完成。
下面為一個有效的使用默認設置的<context:load-time-weaver/>
定義。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:load-time-weaver/> </beans>
上面<context:load-time-weaver/>
bean的定義會自動為你定義和注冊若干
特定LTW的基礎設施beans,比如一個LoadTimeWeaver
和一個AspectJWeavingEnabler
。請注意
<context:load-time-weaver/>
是怎樣在context
命名空間下被定義的;還要注意被引用的XML Schema文件只在Spring 2.5或更高版本中才可用。
上面的配置為你定義并注冊了一個默認的LoadTimeWeaver
bean。
默認的LoadTimeWeaver
是一個
DefaultContextLoadTimeWeaver
類,它更傾向于去裝飾一個能自動檢測的LoadTimeWeaver
類:LoadTimeWeaver
的確切類型會根據你的運行時環(huán)境“自動檢測”出來(概述如下表)。
表?6.1.?DefaultContextLoadTimeWeaver
LoadTimeWeavers
DefaultContextLoadTimeWeaver
類和LoadTimeWeavers
接口
運行時環(huán)境 |
LoadTimeWeaver 的接口實現(xiàn) |
---|---|
BEA's Weblogic 10環(huán)境下 |
|
Oracle's OC4J環(huán)境下 |
|
GlassFish環(huán)境下 |
|
以Spring
|
|
不過,我們更希望這些類加載器能遵循共同的規(guī)范
(例如適用 |
|
請注意當使用DefaultContextLoadTimeWeaver
時只有
LoadTimeWeavers
實現(xiàn)類能進行自動檢測:
當然,你也可以通過指定將類的完全限定名作為<context:load-time-weaver/>
元素中weaver-class
屬性的值
來指定究竟想使用哪個LoadTimeWeaver
的實現(xiàn)。如下例:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
在<context:load-time-weaver/>
元素上定義和注冊的
LoadTimeWeaver
接口可以在Spring容器中以
loadTimeWeaver
名字找到。
記住LoadTimeWeaver
接口只是作為Spring LTW基礎設施的一個機制
用來添加一個或多個ClassFileTransformers
的。
ClassFileTransformer
類實際是利用
ClassPreProcessorAgentAdapter
類(包含在
org.aspectj.weaver.loadtime
中)來進行LTW的。
有關ClassPreProcessorAgentAdapter
的細節(jié)請參見
類級別的javadoc,織入實際怎樣生效的具體內容已經超出本節(jié)討論范圍。
讓我們來討論<context:load-time-weaver/>
的最后一個屬性:
aspectj-weaving
。 這是一個簡單的LTW開關,就這么簡單。
它可以接受如下所述的三種值,如果不顯示設置此屬性則其默認值為autodetect
表?6.2.?aspectj-weaving
屬性值
屬性值 | 注釋 |
---|---|
|
AspectJ織入功能開啟,切面將會在加載時適當時機被織入。 |
|
LTW功能關閉...不會在加載時織入切面。 |
|
如果Spring LTW基礎設施能找到至少一個 |
這最后一節(jié)包括所有你在諸如應用服務器和web容器中使用Spring的LTW功能時需要的額外設置和配置。
你可能在各種Java應用中通過使用由Spring提供的檢測代理啟用Spring的LTW功能
(獨立應用或者基于應用服務器的應用)。這樣的話,可以通過指定
-javaagent:path/to/spring-agent.jar
選項來啟動虛擬機。
請注意這需要修改虛擬機的啟動腳本,但在某些應用服務器環(huán)境下是禁止這么做的
(這取決于你的操作策略)。
對于部署在Apache Tomcat 5.0或更高版本上的web應用,Spring將一個
TomcatInstrumentableClassLoader
注冊成為web應用的類加載器。
必須的Tomcat設置如下所示,你可以把它放在Tomcat WAR包根目錄下的核心文件
server.xml
中或放到應用特定的META-INF/context.xml
文件中。
Spring的spring-tomcat-weaver.jar
需要被包含到Tomcat
的common lib路徑下以確保設置生效。
<Context path="/myWebApp" docBase="/my/webApp/location"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/> </Context>
注意:當使用LTW時,我們一般推薦使用Tomcat 5.5.20或更高版本。
先前的版本對定制的ClassLoader
設置會產生問題。
另外,請考慮使用在Tomcat啟動腳本中(見上面)指定由Spring提供的通用虛擬機代理。 這樣才能使檢測功能在所有已部署的web應用中可用,無論其上運行的是哪種類加載器。
有關更多基于Tomcat織入設置的詳細討論,請參考討論各種不同Tomcat版本內容的 第?12.6.1.3.1?節(jié) “Tomcat(5.0以上)加載時的織入配置”一節(jié)。雖然本節(jié)主要關注于 JPA persistence提供者的設置,但也談到了Tomcat各種特定設置適用于一般加載時織入的情況。
BEA WebLogic(版本10或更高),Oracle的JavaEE容器(OC4J 10.1.3.1或更高)以及
Resin(版本3.1或更高)提供具有本地檢測能力的類加載器。
Srping的原生LTW利用這些類加載器來激活AspectJ織入。你可以通過簡單地激活之前提到的
context:load-time-weaver
來啟動LTW功能。具體來說,即你
不需要通過修改啟動腳本來添加
-javaagent:path/to/spring-agent.jar
。
GlassFish同樣也提供了檢測能力的類加載器,不過只能在它的EAR環(huán)境下使用。 對于GlassFish的web應用,可以使用跟上面tomcat相同的設置。