在Java 語言中,從織入切面的方式上來看,存在三種織入方式:編譯期織入、類加載期織入和運行期織入。編譯期織入是指在Java編譯期,采用特殊的編譯器,將切面織入到Java類中;而類加載期織入則指通過特殊的類加載器,在類字節碼加載到JVM時,織入切面;運行期織入則是采用CGLib工具或JDK動態代理進行切面的織入。
AspectJ采用編譯期織入和類加載期織入的方式織入切面,是語言級的AOP實現,提供了完備的AOP支持。它用AspectJ語言定義切面,在編譯期或類加載期將切面織入到Java類中。
AspectJ提供了兩種切面織入方式,第一種通過特殊編譯器,在編譯期,將AspectJ語言編寫的切面類織入到Java類中,可以通過一個Ant或Maven任務來完成這個操作;第二種方式是類加載期織入,也簡稱為LTW(Load Time Weaving)。 (只講解第二種)
如何使用Load Time Weaving?首先,需要通過JVM的-javaagent參數設置LTW的織入器類包,以代理JVM默認的類加載器;第二,LTW織入器需要一個 aop.xml文件,在該文件中指定切面類和需要進行切面織入的目標類。
下面將通過一個簡單的例子在描述如何使用LTW:
例子所作的是記錄被調用方法的執行時間和CPU使用率。其實這在實際生產中很有用,與其拉一堆性能測試工具,不如動手做個簡單的分析切面,使我們能很快得到一些性能指標。我指的是沒有硬性的性能測試需求下。
首先我們編寫一個被織入的受體類,也就是被攔截的對象。
public class DemoBean { public void run1() { System.out.println("run1..."); } public void run2() throws Exception { TimeUnit.SECONDS.sleep(2); System.out.println("run2..."); } }
接着,我們編寫分析方法執行效率的切面。
@Aspect public class ProfileAspect { @Around("profileMethod()") public Object profile(ProceedingJoinPoint pjp) throws Throwable { StopWatch sw = new StopWatch(getClass().getName()); try { sw.start(pjp.getSignature().getName()); return pjp.proceed(); } finally { sw.stop(); System.err.println(sw.prettyPrint()); } } @Pointcut("execution(public * org.codetree.core.spring.loadtimeweaver.*.*(..))") public void profileMethod() { System.out.println("profileMethod.."); } }
aop.xml。這個文件要求放在META-INF/aop.xml路徑下,以告知AspectJ Weaver我們需要把ProfilingAspect織入到應用的哪些類中。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"> <aspectj> <weaver> <!-- only weave classes in your application-specific packages --> <include within="org.codetree.core.spring.loadtimeweaver.*" /> </weaver> <aspects> <!-- weave in just these aspects --> <aspect name="org.codetree.core.spring.loadtimeweaver.ProfileAspect" /> </aspects> </aspectj>
spring的配置文件
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:load-time-weaver aspectj-weaving="autodetect" /> <context:component-scan base-package="org.codetree.core.spring.loadtimeweaver" use-default-filters="false"> <context:include-filter type="regex" expression="org.codetree.core.spring.loadtimeweaver.*" /> <context:exclude-filter type="regex" expression="org.codetree.core.spring.loadtimeweaver.Main" /> </context:component-scan> </beans>
或者
@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.AUTODETECT)
@EnableAspectJAutoProxy
@ComponentScan
通過 <context:load-time-weaver aspectj-weaving="on" /> 使 spring開啟 loadtimeweaver, 注意 aspectj-weaving 有三個選項 : on, off, auto-detect, 如果設置為 auto-detect, spring 將會在 classpath 中查找aspejct 需要的 META-INF/aop.xml, 如果找到則開啟 aspectj weaving,這個邏輯在LoadTimeWeaverBeanDefinitionParser#isAspectJWeavingEnabled方法中。
測試類
public class Main { public static void main(String[] args) throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring/loadtimeweaver/loadtimeweaver.xml"); DemoBean demoBean = context.getBean(DemoBean.class); demoBean.run1(); demoBean.run2(); } }
結果
輸出結果如下: run1... StopWatch 'ProfilingAspect': running time (millis) = 0 ----------------------------------------- ms % Task name ----------------------------------------- 0001 100% run1
如果想不加JVM的-javaagent參數可以進行如下替換:
public class ExtInstrumentationLoadTimeWeaver extends InstrumentationLoadTimeWeaver(默認的weaver) { @Override public void addTransformer(ClassFileTransformer transformer) { try { super.addTransformer(transformer); } catch (Exception e) {吃掉無關異常不拋出} } }
spring的配置文件
<?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:load-time-weaver weaver-class="com.shansun.multidemo.spring.ExtInstrumentationLoadTimeWeaver" aspectj-weaving="autodetect" /> <context:component-scan base-package="com.shansun.multidemo"></context:component-scan> </beans>