手动实现AspectJ的编译前、编译后、加载时、加载后的实现方案。

前提思想

研究AspectJ的实现,首先必须要明白一个大前提,那就是是其实对于jvm虚拟机来说,它只认符合class文件结构的文件,无论这个文件是从java语言还是AspectJ语言、或者其它什么语言编译的,最终,只要生成的文件符合class文件结构,就能被jvm所识别并加载。

同时,我们还要了解一个前提,那就是Aspectj织入功能,其实可以分别在四个阶段进行织入:

第一个阶段:编译前。所谓的编译前,就是我们可以使用ajc指令直接对.java文件进行织入,这一个阶段会生成新的class文件结构,并且会回写到磁盘上,替换原class文件。

第二个阶段:编译后。所谓的编译后,就是指的,我们可以使用ajc指令,直接对.class文件进行织入。这一个阶段会生成新的class文件结构,并且会回写到磁盘上,替换原class文件。

第三个阶段:加载时。所谓的加载时,就是在class文件被classloader加载时,我们可以通过特殊的技术手段,比如自己实现classloader或者使用Java SE 6 新特性新特性提供的Instrumentation 新功能来实现,这一个阶段会生成新的class文件结构,但是并不需要将新生成的文件写回到磁盘上,主要此时新生成的class文件结构也完全不同于上面的两种,内容太深,如果想细究,可在后面将二进制流打印出来,并使用javap反编译看一下(只有用javaP反编译才能看出来)。

第四个阶段:加载后。所谓的加载后,就是指class文件已经被jvm加载,我们通过jdk6新特性提供的Instrumentation 新功能来实现对内存中的class文件强制替换的功能来实现,其实AspectJ官方并没有介绍这个阶段,因为这个阶段,aspectj主要提供的功能,还是像第三阶段一样,重构原class文件结构,生成新的class文件结构二进制流,而替换的工作主要是由Java SE 6 新特性新特性提供的Instrumentation 新功能来实现。

 

编译前、编译后的代码实现

该功能实现可以使用自定义的classloader来实现,也可以使用Java SE 6 新特性新特性提供的Instrumentation 新功能来实现,至于新特性实现我就不讲了,推荐看这篇文章(https://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html),我只讲如何将 切面织入编译前和编译后的代码中。

两种方式:

第一种,使用ajc指令,ajc指令其实就相当于java指令,我们可以使用

Runtime.getRuntime().exec(ajc指令)的方式来实现。

这里主要注意两点:

第一:使用Runtime.getRuntime().exec执行ajc指令,需要在ajc指令前加上cmd /c;

第二点:ajc指令的核心几个要素,

-classpath:要在路径下指定aspectjrt的jar包
-aspectpath:在该要素下指定切面所在路径。
-inpath:在该要素下,指定想要被切面修饰的类。

第二种,调用ajc的代码方式实现:

Main main = new Main();
MessageHandler messageHandler=new MessageHandler();
main.run(args,messageHandler);

这里的args是一个数组,其实就是将前面所讲的ajc指令的核心要素,一个个分成数组的元素,另外该方法执行需要指定一个指令要素

-cp:该要素指定java运行环境所需的jar包路径。

 

加载时和加载后的代码实现

同样推荐先看推荐看这篇文章(https://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html)这篇文章,了解如何在加载前和加载后实现java的class对象拦截。

加载时和加载后的代码后的代码织入则要求有如下格式:

 

第一个,在Meta-INF里建一个Aop的xml文件(文件怎么建‘以及文件作用请参考https://www.eclipse.org/aspectj/doc/released/devguide/ltw-configuration.html

第二步,使用Aj对象,对代码进行重新织入,核心代码如下三行:

Aj aj=new Aj();
aj.initialize();
byte[] bytes1=aj.preProcess(className,bytes,this.getClass().getClassLoader(),null);

Aj对象是AspectJ提供的,preProcess的几个参数分别如下:

className:被修饰对象的类名,该类名必须是译\或/来修饰的,比如com/alibaba/test/Test这样的

bytes:被修饰对象的字节流。

this.getClass().getClassLoader():很容易看明白。

bytes1:就是使用Aspectj织入代码后的二进制流了。

 

整个工作的核心代码,至此讲完。

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AspectJ是一种基于Java语言的面向切面编程(AOP)的扩展。它通过在编译期间将切面代码织入到目标代码中,实现了对横切关注点的模块化和重用。 AspectJ实现原理主要包括以下几个步骤: 1. 编译器扩展:AspectJ通过扩展Java编译器,添加了对切面语法的支持。在编译Java源代码AspectJ编译器会解析切面文件中的切点和通知,并将其转换为字节码文件。 2. 切点匹配:AspectJ使用切点表达式来定义切点,切点表达式可以根据方法的签名、注解、访问修饰符等条件来匹配目标代码中的连接点。AspectJ编译器会根据切点表达式在目标代码中找到匹配的连接点。 3. 通知织入:一旦找到匹配的连接点,AspectJ会将切面中定义的通知代码织入到目标代码中。通知可以分为置通知、后置通知、环绕通知等,它们会在目标代码执行、后或者替代目标代码执行。 4. 字节码增强:AspectJ通过修改目标代码的字节码来实现通知的织入。它会在目标代码中插入切面代码,并调整字节码中的跳转指令,以确保通知的正确执行顺序。 5. 运行支持:AspectJ还提供了运行的支持,包括切面实例的创建和管理、连接点的动态绑定、通知的执行等。在程序运行AspectJ会根据切面的定义和连接点的匹配情况来决定是否执行通知。 总的来说,AspectJ通过编译器扩展和字节码增强的方式,将切面代码织入到目标代码中,实现了对横切关注点的模块化和重用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值