Spring AOP 底层原理_001----AspectJ与CGLIB介绍
2017-09-02 14:23
525 查看
http://www.ibm.com/developerworks/cn/java/l-aspectJ/index.html中介绍了What
is AspectJ 。
AspectJ是一个代码生成工具(Code Generator)。
AspectJ语法就是用来定义代码生成规则的语法。您如果使用过Java Compiler Compiler (JavaCC),您会发现,两者的代码生成规则的理念惊人相似。
AspectJ有自己的语法编译工具,编译的结果是Java Class文件,运行的时候,classpath需要包含AspectJ的一个jar文件(Runtime lib)。
....
看了上面几点,我就想看看它怎么把代码生成了。现在做一个试验。
一个类(包括main函数):Speaker.Java
[java] view
plain copy
package test.aspectj;
public class Speaker
{
public void speak()
{
System.out.println("[Speaker] bla bla ");
}
public static void main(String[] args)
{
Speaker speaker = new Speaker();
speaker.speak();
}
}
一个aspect:AspectObserver.java
[java] view
plain copy
package test.aspectj;
public aspect AspectObserver
{
pointcut speakerSpeak():
call(void *Speaker.speak());
before() : speakerSpeak() {
System.out.println("[AspectObserver] speaker is about to speak!");
}
after() returning() : speakerSpeak() {
System.out.println("[AspectObserver] speaker has completed his speech!");
}
}
以上都是源码部分哦。
运行结果:
[java] view
plain copy
[AspectObserver] speaker is about to speak!
[Speaker] bla bla
[AspectObserver] speaker has completed his speech!
说明程序是正常运作的哦。
好了,下面,做三个操作:
1、将以上的编译成的class文件打包成apectjtest.jar文件。
说明:可以使用ajdt的eclipse插件带的导出功能,Export --> JAR file with ApectJ support
2、新建一个AspectJ工程,将apectjtest.jar加入类路径,使用jad来反编译Speaker.class和AspectObserver.class
得到反编译后的源码:
Speaker.class
[java] view
plain copy
/*jadclipse*/// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) radix(10) lradix(10)
// Source File Name: Speaker.java
package test.aspectj;
import java.io.PrintStream;
// Referenced classes of package test.aspectj:
// AspectObserver
public class Speaker
{
public Speaker()
{
}
public void speak()
{
System.out.println("[Speaker] bla bla ");
}
public static void main(String args[])
{
Speaker speaker = new Speaker();
AspectObserver.aspectOf().ajc$before$test_aspectj_AspectObserver$1$b2b6354();
speaker.speak();
AspectObserver.aspectOf().ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354();
}
}
AspectObserver.class
[java] view
plain copy
/*jadclipse*/// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) radix(10) lradix(10)
// Source File Name: AspectObserver.aj
package test.aspectj;
import java.io.PrintStream;
import org.aspectj.lang.NoAspectBoundException;
public class AspectObserver
{
public AspectObserver()
{
}
void ajc$pointcut$$speakerSpeak$44()
{
}
public void ajc$before$test_aspectj_AspectObserver$1$b2b6354()
{
System.out.println("[AspectObserver] speaker is about to speak!");
}
public void ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354()
{
System.out.println("[AspectObserver] speaker has completed his speech!");
}
public static AspectObserver aspectOf()
{
if(ajc$perSingletonInstance == null)
throw new NoAspectBoundException("test_aspectj_AspectObserver", ajc$initFailureCause);
else
return ajc$perSingletonInstance;
}
public static boolean hasAspect()
{
return ajc$perSingletonInstance != null;
}
private static void ajc$postClinit()
{
ajc$perSingletonInstance = new AspectObserver();
}
private static Throwable ajc$initFailureCause;
public static final AspectObserver ajc$perSingletonInstance;
static
{
try
{
ajc$postClinit();
}
catch(Throwable throwable)
{
ajc$initFailureCause = throwable;
}
}
}
3、运行一下Speaker.class
得到结果:
[java] view
plain copy
[AspectObserver] speaker is about to speak!
[Speaker] bla bla
[AspectObserver] speaker has completed his speech!
结果跟源码运行是一样的哦(不一样就是你的人品问题咯!!)
分析一下,先理解AspectJ编译器为我们做了什么事情:
首先、AspectJ从文件列表里取出所有的文件名,然后读取这些文件,进行分析。
二、AspectJ发现一些文件含有aspect的定义,在这个例子里,就是AspectObserver的定义;这些aspect就是代码生成规则。
三、AspectJ根据这些aspect代码生成规则,修改添加你的源代码。在这个例子里,源码是修改成怎样了?比较一下反编译后的代码和源码便知。
四、AspectJ读取AspectObserver的定义,发现了一个pointcut--speakerSpeak();这个pointcut的定义是call(void *Speaker.speak()),表示所有对Speaker类的speak方法的执行点。
五、AspectJ继续读取AspectObserver的定义,发现了一个before(),这在AspectJ中叫做Advice。Advice允许你在某个类的方法的调用之前或调用之后,加入另外的代码。加入的代码是什么?比较一下反编译后的代码和源码吧。 AspectJ继续读取AspectObserver的定义
.
好了,回头看看反编译后的Speaker.class,与源码Speaker.java差别在哪呢?主要在main函数中,方法被调用的前后
[java] view
plain copy
public static void main(String args[])
{
Speaker speaker = new Speaker();
AspectObserver.aspectOf().ajc$before$test_aspectj_AspectObserver$1$b2b6354();//多了这行
speaker.speak();
AspectObserver.aspectOf().ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354();//多了这行
}
不同的地方就是多出了两行,虽然有$和数字,但是很容易看出,这同我们平常写的java代码差不了多少。
[java] view
plain copy
AspectObserver.aspectOf().ajc$before$test_aspectj_AspectObserver$1$b2b6354();
这行代码中,看起来不就是AspectObserver类调用了静态方法aspectOf()吗,接着aspectOf()的返回对象又调用ajc$before$test_aspectj_AspectObserver$1$b2b6354()方法吗?
回来看看AspectObserver.class的反编译代码,哈哈,这就对了,aspectOf返回的是AspectObserver的一个实例,返回实例在调用实例方法ajc$before$test_aspectj_AspectObserver$1$b2b6354()。ajc$before$test_aspectj_AspectObserver$1$b2b6354()是干什么的呢?呵呵,不正是我们要的拦截方法所要做的操作吗?
[java] view
plain copy
System.out.println("[AspectObserver] speaker is about to speak!");
同样,可知
[java] view
plain copy
AspectObserver.aspectOf().ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354();
是怎么一回事啦。
这正是应了上面所提的两点:
AspectJ是一个代码生成工具(Code Generator)。
AspectJ语法就是用来定义代码生成规则的语法。
用这两点去感受apectj在程序里该如何去运用使用,就清晰多啦!
is AspectJ 。
AspectJ是一个代码生成工具(Code Generator)。
AspectJ语法就是用来定义代码生成规则的语法。您如果使用过Java Compiler Compiler (JavaCC),您会发现,两者的代码生成规则的理念惊人相似。
AspectJ有自己的语法编译工具,编译的结果是Java Class文件,运行的时候,classpath需要包含AspectJ的一个jar文件(Runtime lib)。
....
看了上面几点,我就想看看它怎么把代码生成了。现在做一个试验。
一个类(包括main函数):Speaker.Java
[java] view
plain copy
package test.aspectj;
public class Speaker
{
public void speak()
{
System.out.println("[Speaker] bla bla ");
}
public static void main(String[] args)
{
Speaker speaker = new Speaker();
speaker.speak();
}
}
一个aspect:AspectObserver.java
[java] view
plain copy
package test.aspectj;
public aspect AspectObserver
{
pointcut speakerSpeak():
call(void *Speaker.speak());
before() : speakerSpeak() {
System.out.println("[AspectObserver] speaker is about to speak!");
}
after() returning() : speakerSpeak() {
System.out.println("[AspectObserver] speaker has completed his speech!");
}
}
以上都是源码部分哦。
运行结果:
[java] view
plain copy
[AspectObserver] speaker is about to speak!
[Speaker] bla bla
[AspectObserver] speaker has completed his speech!
说明程序是正常运作的哦。
好了,下面,做三个操作:
1、将以上的编译成的class文件打包成apectjtest.jar文件。
说明:可以使用ajdt的eclipse插件带的导出功能,Export --> JAR file with ApectJ support
2、新建一个AspectJ工程,将apectjtest.jar加入类路径,使用jad来反编译Speaker.class和AspectObserver.class
得到反编译后的源码:
Speaker.class
[java] view
plain copy
/*jadclipse*/// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) radix(10) lradix(10)
// Source File Name: Speaker.java
package test.aspectj;
import java.io.PrintStream;
// Referenced classes of package test.aspectj:
// AspectObserver
public class Speaker
{
public Speaker()
{
}
public void speak()
{
System.out.println("[Speaker] bla bla ");
}
public static void main(String args[])
{
Speaker speaker = new Speaker();
AspectObserver.aspectOf().ajc$before$test_aspectj_AspectObserver$1$b2b6354();
speaker.speak();
AspectObserver.aspectOf().ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354();
}
}
AspectObserver.class
[java] view
plain copy
/*jadclipse*/// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) radix(10) lradix(10)
// Source File Name: AspectObserver.aj
package test.aspectj;
import java.io.PrintStream;
import org.aspectj.lang.NoAspectBoundException;
public class AspectObserver
{
public AspectObserver()
{
}
void ajc$pointcut$$speakerSpeak$44()
{
}
public void ajc$before$test_aspectj_AspectObserver$1$b2b6354()
{
System.out.println("[AspectObserver] speaker is about to speak!");
}
public void ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354()
{
System.out.println("[AspectObserver] speaker has completed his speech!");
}
public static AspectObserver aspectOf()
{
if(ajc$perSingletonInstance == null)
throw new NoAspectBoundException("test_aspectj_AspectObserver", ajc$initFailureCause);
else
return ajc$perSingletonInstance;
}
public static boolean hasAspect()
{
return ajc$perSingletonInstance != null;
}
private static void ajc$postClinit()
{
ajc$perSingletonInstance = new AspectObserver();
}
private static Throwable ajc$initFailureCause;
public static final AspectObserver ajc$perSingletonInstance;
static
{
try
{
ajc$postClinit();
}
catch(Throwable throwable)
{
ajc$initFailureCause = throwable;
}
}
}
3、运行一下Speaker.class
得到结果:
[java] view
plain copy
[AspectObserver] speaker is about to speak!
[Speaker] bla bla
[AspectObserver] speaker has completed his speech!
结果跟源码运行是一样的哦(不一样就是你的人品问题咯!!)
分析一下,先理解AspectJ编译器为我们做了什么事情:
首先、AspectJ从文件列表里取出所有的文件名,然后读取这些文件,进行分析。
二、AspectJ发现一些文件含有aspect的定义,在这个例子里,就是AspectObserver的定义;这些aspect就是代码生成规则。
三、AspectJ根据这些aspect代码生成规则,修改添加你的源代码。在这个例子里,源码是修改成怎样了?比较一下反编译后的代码和源码便知。
四、AspectJ读取AspectObserver的定义,发现了一个pointcut--speakerSpeak();这个pointcut的定义是call(void *Speaker.speak()),表示所有对Speaker类的speak方法的执行点。
五、AspectJ继续读取AspectObserver的定义,发现了一个before(),这在AspectJ中叫做Advice。Advice允许你在某个类的方法的调用之前或调用之后,加入另外的代码。加入的代码是什么?比较一下反编译后的代码和源码吧。 AspectJ继续读取AspectObserver的定义
.
好了,回头看看反编译后的Speaker.class,与源码Speaker.java差别在哪呢?主要在main函数中,方法被调用的前后
[java] view
plain copy
public static void main(String args[])
{
Speaker speaker = new Speaker();
AspectObserver.aspectOf().ajc$before$test_aspectj_AspectObserver$1$b2b6354();//多了这行
speaker.speak();
AspectObserver.aspectOf().ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354();//多了这行
}
不同的地方就是多出了两行,虽然有$和数字,但是很容易看出,这同我们平常写的java代码差不了多少。
[java] view
plain copy
AspectObserver.aspectOf().ajc$before$test_aspectj_AspectObserver$1$b2b6354();
这行代码中,看起来不就是AspectObserver类调用了静态方法aspectOf()吗,接着aspectOf()的返回对象又调用ajc$before$test_aspectj_AspectObserver$1$b2b6354()方法吗?
回来看看AspectObserver.class的反编译代码,哈哈,这就对了,aspectOf返回的是AspectObserver的一个实例,返回实例在调用实例方法ajc$before$test_aspectj_AspectObserver$1$b2b6354()。ajc$before$test_aspectj_AspectObserver$1$b2b6354()是干什么的呢?呵呵,不正是我们要的拦截方法所要做的操作吗?
[java] view
plain copy
System.out.println("[AspectObserver] speaker is about to speak!");
同样,可知
[java] view
plain copy
AspectObserver.aspectOf().ajc$afterReturning$test_aspectj_AspectObserver$2$b2b6354();
是怎么一回事啦。
这正是应了上面所提的两点:
AspectJ是一个代码生成工具(Code Generator)。
AspectJ语法就是用来定义代码生成规则的语法。
用这两点去感受apectj在程序里该如何去运用使用,就清晰多啦!
相关文章推荐
- Spring AOP 实现原理----AspectJ与CGLIB介绍
- SpringAOP的CGLIB动态代理的底层原理实现
- JAVAWEB开发之Spring详解之——AOP底层剖析(基于JDK和cglib)、Spring中的AOP以及基于AspectJ的AOP实现、Spring的JDBCTemplate详解
- Spring AOP 实现原理与 CGLIB 应用
- 【Spring】——AOP实现原理(基于JDK和CGLIB)
- 框架 day37 Spring3,AOP,代理模式(动态/CGLIB/工厂bean),传统AOP,AspectJ框架(基于xml/注解),切入点表达式,jdbcTemplate
- Spring--Spring AOP 实现原理与 CGLIB 应用
- Spring学习-17:AOP底层原理
- Spring AOP & AspectJ之原理探析
- 探析Spring AOP(三):Spring AOP的底层实现原理
- Spring AOP 实现原理与CGLIB应用
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
- Spring AOP 实现原理与 CGLIB 应用
- Spring AOP 实现原理与 CGLIB 应用--转
- (十九)Core Java 加载器及Spring底层AOP原理实现 (117)
- Spring AOP底层实现原理
- SpringAOP原理介绍
- Spring AOP,AspectJ, CGLIB 有点晕
- Spring AOP 实现原理与 CGLIB 应用
- Spring AOP 实现原理与 CGLIB 应用