技术开发 频道

AspectJ学习:理解方法签名中的类型声明模式



   它的运行结果是这样的:

    B.foo()

    Call A

    和你的想法一致吗?对于( 1 )处 b.foo() 的调用应用了面向对象中的覆盖( override ),它是动态的,是在运行时进行解析。而( 2 )处的 callFoo (b) 则是重载( overload ),它是静态的,是在编译时解析的。因此,对于变量 b ,虽然它是 B 的一个实例,但 b 的静态类型(也就是变量声明的类型)是 A ;由于重载方法的选择是静态的,所以 main 中调用的是 callFoo(A a) ,而不是 callFoo(B b)

    终于说到了类型声明模式。类型声明模式是基于静态类型信息进行匹配的,而不是动态(或者运行时。下面根据几个典型的例子说明类型声明模式的特性。

    还是上面的两个类 A B ,现在我们定义一个方面如下:


public  aspect TypeAspect {

        pointcut callA():
            call(
*  A. *
(..));
        
        before():callA(){
            System.out.println(
" call A "
);
        }    
}

 

    main 函数内容如下:


public   static   void  main(String[] args) {
        A b1 
=   new
 B();
        b1.foo();
        
        B b2 
=   new
 B();
        b2.foo();
    }

 

运行结果如下:

call A

B.foo()

call A

B.foo()

    可以看到,尽管切入点 callA() 声明的类型为 A ,但实际上,切入点 callA() 可以捕获 A 中的方法及其子类中继承于 A 的方法或重载 A 的方法,而声明的静态类型既可以是 A 也可以是其子类。

    但如果在 B 中增加一个新的方法:

public   void  doAnotherThing(){

System.out.println(
" B.doAnotherThing "
);

}

        main 函数改为:

public   static   void  main(String[] args) {

B b2 
=   new
 B();

b2.doAnotherThing();

}

    输出结果为: B.doAnotherThing ,如果想对 A 的子类 B 中扩展的方法进行通知,可采用的方法是将切入点 callA() 改为 pointcut callA(): call (* A+.*(..));

    让我们再来看另一种情景:如果定义一个切入点如下:


 pointcut callB():call( *  B. * (..));

before():callB(){

System.out.println(
" call B "
);

}

        main 函数内容如下:

public   static   void  main(String[] args) {

A b 
=   new
 B();

b.foo();

}

    运行结果为: B.foo() b.foo() 没有匹配切入点 callB() 的原因在于, b 的静态类型是 A ,从静态类型的角度来看,这是对 A 的调用,而不是对 B 的调用。在使用 AspectJ 的类型声明时,很容易在这个地方犯错。

    好了,如上便是有关类型声明模式的东西,说得有些凌乱,希望对 AspectJ 初学者有些帮助(我本身也是个初学者)。该文参考了《 Eclipse AspectJ 》和《 AspectJ cookbook 》。

0
相关文章