Spring的LoadTimeWeaver(代碼織入)


在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>

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM