AspectJ AOP 简单示例2

来源:互联网 发布:德军总部新巨人 知乎 编辑:程序博客网 时间:2024/06/04 19:52

转自 http://blog.sina.com.cn/s/blog_46d0362d0100mmzy.html
有时候调试一些程序会牵涉到第三方的类库,但由于它们都是以class或者jar形式发布的,不大可能改变其行为,所以不是特别方便。这个时候唯一可以利用的就是AOP的技术,常用的就是AspectJ
首先是几个概念:
1.aspect(层面) 2.pointcut(切入点) 3.advice(建议)4.weave(织入)5.LTW(加载期织入 load time weave)
按照aspectj的语法规则,一个aspect就是很多pointcut和advice的集合,也就是一个*.aj的文件
一个pointcut就是对target class的切入点定义,类似Java class定义中的field
一个advice就是对target class的行为改变,类似Java class中的method
weave就是aspectj runtime库把aspect织入到target class的行为。
LTW就是指运行期间动态织入aspect的行为,它是相对静态织入行为(包括对源文件、二进制文件的修改)。
一般来讲,从运行速度上来说,静态织入比动态织入要快些。因为LTW需要使用aspectj本身的classloader,
它的效率要低于jdk的classloader,因此当需要load的class非常多时,就会很慢的。

举个例子来说明aspectj的使用:
scenario: Example工程需要使用一个类Line存在于第三方库Line.jar中,但是Line本身没有实现Serializable接口,并且其toString方法输出也不完善。因此这两点都需要修改。
Line的实现:

package bean;public class Line {    protected int x1 = 0;    protected int x2 = 0;    public int getX1(){        return x1;    }    public int getX2(){        return x2;    }    public void setLength(int newX, int newY){        setX1(newX);        setX2(newY);    }    public void setX1(int newX) {        x1 = newX;    }    public void setX2(int newY) {        x2 = newY;    }    public String toString(){        return "(" + getX1() + ", " + getX2() + ")" ;    }}

Main entry :

public class MyExample { private Line line = null; public MyExample() {  line = new Line();  System.err.println("Line implement serializable interface : "    + (line instanceof Serializable)); } public void showMe() {  System.out.println("Show all about me ...");  System.out.println(line.toString()); } public static void main(String[] args) {  MyExample demo = new MyExample();  // i want to change the action of show me, but i cannot get line source.  // so i will trying load-time weaving  demo.showMe(); }}

output :

Line implement serializable interface : trueShow all about me ...(0, 0)

定义一个aspect :由于1.5之后可以直接支持annotation,所以对于不复杂的aspect定义可以直接使用标签表示。但是目前aspectj支持的标签相对其语法来讲功能要弱些,因此可以根据实际情况选择

@Aspectpublic class DynamicPrinter { @Pointcut("call(void example.aop.weaver.MyExample.showMe())") void printTitle() {}; //此处定义一个切入点--发生调用MyExample的showMe()方法的时刻 @Before("printTitle()")//此处定义了一个advice,其直接引用到前面定义的切入点 public void printStartMessage() {  System.out.println("***************************************"); } @After("printTitle()")//此处定义了一个advice,其直接引用到前面定义的切入点 public void printOverMessage() {  printStartMessage(); } @Around("call(String bean.Line.toString())") //此处定义了一个替换方法的advice,由于没有已定义的切入点可以引用,因此写出切入点原始信息call(String bean.line.toString()) public String redirectMessage() {  return "This is a hack action!!!"; } @DeclareParents("bean.Line") private Serializable i;}

编译aj和源文件

由于aspectj提供了ant的task,所以使用比较方便

<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">  <classpath>   <path refid="My Aop Example.classpath">   </path>  </classpath> </taskdef> <target name="iajc">  <iajc sourceroots="${basedir}/src" inpath="${basedir}/lib/Line.jar" destdir="${basedir}/bin" classpathref="My Aop Example.classpath" source="1.5" /> </target>

new output :

Line implement serializable interface : true***************************************Show all about me ...This is a hack action!!!***************************************

后记 :

把iajc编译后的源文件反编译之后,就可以参考静态织入的源码道理了

public class MyExample{    private Line line;    private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0;    public MyExample()    {        line = null;        line = new Line();        System.err.println((new StringBuilder("Line implement serializable interface : ")).append(line instanceof Serializable).toString());    }    public void showMe()    {        System.out.println("Show all about me ...");        Line line1;        System.out.println(toString_aroundBody1$advice(this, line1 = line, DynamicPrinter.aspectOf()));    }    public static void main(String args[])    {        MyExample demo = new MyExample();        demo;        DynamicPrinter.aspectOf().printStartMessage();        showMe();        break MISSING_BLOCK_LABEL_30;        Throwable throwable;        throwable;        DynamicPrinter.aspectOf().printOverMessage();        throw throwable;        DynamicPrinter.aspectOf().printOverMessage();        return;    }    private static final String toString_aroundBody0(MyExample myexample, Line line1)    {        return line1.toString();    }    private static final String toString_aroundBody1$advice(DynamicPrinter this, Line line1, DynamicPrinter dynamicprinter)    {        return "This is a hack action!!!";    }    static    {        Factory factory = new Factory("MyExample.java", Class.forName("example.aop.weaver.MyExample"));        ajc$tjp_0 = factory.makeSJP("method-call", factory.makeMethodSig("1", "toString", "bean.Line", "", "", "", "java.lang.String"), 26);    }}
public class DynamicPrinter{    private Serializable i;    private static Throwable ajc$initFailureCause;    public static final DynamicPrinter ajc$perSingletonInstance;    public DynamicPrinter()    {    }    void printTitle()    {    }    public void printStartMessage()    {        System.out.println("***************************************");    }    public void printOverMessage()    {        printStartMessage();    }    public String redirectMessage()    {        return "This is a hack action!!!";    }    public static DynamicPrinter aspectOf()    {        if(ajc$perSingletonInstance == null)            throw new NoAspectBoundException("example.aop.hook.DynamicPrinter", ajc$initFailureCause);        else            return ajc$perSingletonInstance;    }    public static boolean hasAspect()    {        return ajc$perSingletonInstance != null;    }    private static void ajc$postClinit()    {        ajc$perSingletonInstance = new DynamicPrinter();    }    static    {        try        {            ajc$postClinit();        }        catch(Throwable throwable)        {            ajc$initFailureCause = throwable;        }    }}

但是反编译jar中Line.class的时候,发现其结构没有变化,不知为何能够在运行时刻将serializable接口和toString方发织入进去的?

0 0
原创粉丝点击