AspectJ ITD添加的新构造函数的行为

时间:2013-07-15 05:52:12

标签: aop aspectj

我目前正在将AspectJ应用于我们的项目,我发现了一种对我来说有点奇怪的行为。

Q1: 我使用inter-type声明向我当前的类添加了一个新的构造函数,并发现如果使用新的构造函数来实例化我的类,则不会初始化类的成员变量。

例如:

我将添加一个新构造函数的类:

public class Child {

    public String name = "John";

    public Child(String desc) {
        // TODO Auto-generated constructor stub
    }
} 

aspectJ代码:

public aspect MyTest {
    public Child.new(String desc, int num) {
        System.out.println("Child Name:" + this.name);
    }
}

如果我使用新的构造函数实例化Child:

new Child("A child", 5)

成员变量 this.name 未初始化,原始构造函数将完成。

但是,如果我调用原始构造函数:

new Child("A child") 

成员变量 this.name 将像往常一样初始化为“John”

结果:

  

子名称:null

这是AspectJ的限制吗?无论如何都有解决这个问题的方法吗?

我真的不想将成员变量初始化的代码添加到新的构造函数中。

Q2: 似乎在新添加的构造函数中 super.method()无法正确解析。

我将添加一个新构造函数的类:

public class Child extends Parent{

    public String name = "John";

    public Child(String desc) {

    }
} 

儿童扩展的方法是 init()

public class Parent {

    public void init() {
        //....
    }

}

我在我的方面为 Child 添加了一个新的构造函数。

public aspect MyTest {
    public Child.new(String desc, int num) {
        super.init();
    }
}

上述方面代码将触发异常。

Exception in thread "main" java.lang.NoSuchMethodError: com.test2.Child.ajc$superDispatch$com_test2_Child$init()V
    at MyTest.ajc$postInterConstructor$MyTest$com_test2_Child(MyTest.aj:19)
    at com.test2.Child.<init>(Child.java:1)
    at MainProgram.main(MainProgram.java:11)

我的解决方法是为我的班级孩子定义另一种方法,并在该方法中间接调用super.method()

例如,为儿童

添加一个名为 super.init()的新方法
public void Child.initState()
{
    super.init();
}

现在,我可以在新添加的构造函数中调用initState(),如下所示:

public aspect MyTest {
    public Child.new(String desc, int num) {
        this.initState();
    }
}

这是AspectJ的限制吗?这是解决此问题的唯一方法吗?

谢谢大家的时间:)

3 个答案:

答案 0 :(得分:2)

对于第一个问题,似乎在编译时会出现lint警告: (除非你关闭lint警告)

“类型间构造函数不包含显式构造函数调用:目标类型中的字段初始值设定项不会被执行[Xlint:noExplicitConstructorCall]”

因此,我认为这是 AspectJ的限制

执行此操作的最佳方法可能是在AspectJ添加的构造函数中调用 Child 的其他构造函数

例如:

public aspect MyTest {
    public Child.new(String desc, int num) {
        this("Hello"); // -> This will call the constructor of Child, and trigger fields initialization
        System.out.println("Child Name:" + this.name);
    }
}

答案 1 :(得分:1)

对于第二个问题,我认为这是aspectJ的一个错误。 反编译编织目标字节代码将发现将插入方法“com.test2.Child.ajc $ superDispatch $ com_test2_Child $ init()V”。这意味着这个方法应该由aspectJ生成,但字节代码中没有这样的方法。

答案 2 :(得分:0)

ITD介绍的代码与您直接添加到类中的代码没有什么不同。因此,如果在引入的构造函数中没有成员初始化代码,成员当然将保持未初始化状态。因此,您需要在Q1中更改代码,如下所示。

public Child.new(String name, int age) {
    this.name = name;
    this.age = age;
    System.out.println("Child Name:" + this.name);
}

对于Q2,它对我来说很好。

class Parent {
    public void init() {
    System.out.println("P.init");
}
}

class Child extends Parent {
}

aspect Intro {
    public void Child.init(){
        super.init();
        System.out.println("C.init");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.init();
    }
}

打印:

P.init
C.init

将引入的方法更改为init以外的其他方法也适用(以匹配您的代码)。

关于你的评论:我没看到你在Q1中有什么不同。对不起,我收不到。

对于评论的第2部分,构造函数的安排对我有用:

class Parent {
    protected String name;

    public Parent(String name) {
        this.name = name;
    }
}

class Child extends Parent {
    int age;

    public Child(String name) {
        super(name);
    }
}

aspect Intro {
    public Child.new(String name, int age){
        super(name);
        this.age = age;
        System.out.println("this.name: " + this.name + " this.age: " + this.age);
    }
}

打印this.name: myname this.age: 2