java public aspect_ASPECT Java的使用

有时候调试一些程序会牵涉到第三方的类库,但由于它们都是以class或者jar形式发布的,不大可能改变其行为,所以不是特别方便。这个时候有两个选择,其一就是进行二进制class文件修改(参看链接Java Binary Translation),这个对不熟悉class格式文件的人来说门槛有点高。第二种方法相对较为简单,就是利用AOP的技术,常用的实现库就是AspectJ(如果集成spring框架的AOP,参看链接Spring

AOP)

首先是几个概念:

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 : true

Show all about me ...

(0, 0)

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

@Aspect

public 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,所以使用比较方便

注意一点是,目前iajc只支持最高1.5 level的文件编译,如果使用高于1.5的jdk的话,需要指定编译级别到1.5

sourceRoots, sourceRootsRef

(Path)

Directories containing source files

(ending with .java or .aj) to compile.

inpath, inpathRef (Path)

Read .class files for bytecode weaving

from directories or zip files (like classpath).

classpath, classpathRef (Path)

The classpath used by the sources being

compiled. When compiling aspects, include the same version of the

aspectjrt.jar.

destDir

The directory in which to place the

generated class files. Only one of destDir and

outJar may be set.

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值