在前面《[AspectJ] 明明白白AspectJ (1) 》中用例子说明了AspectJ的确是代码生成器。
现在,同样的方式,将官方提供的例子“Bean Example”(File -->New -->Project -->AspectJ -->AspectJ Examples --> Bean Example)简单剖析一下。Bean Example主要是利用AOP来实现属性改变通知,效果像观察者模式来关注bean的属性,但它是用java.beans.PropertyChangeSupport来实现的。如果你对java.beans.PropertyChangeSupport用法不了解,可以参考我以前的一篇随笔《[java 拾遗篇] JavaBean实现约束属性简单例 》。
Bean Example的源码有三个文件,分别是Point.java , BoundPoint.aj , Demo.java
Point.java
package bean;


class Point
{

protected int x = 0;
protected int y = 0;


/** *//**
* Return the X coordinate
*/

public int getX()
{
return x;
}


/** *//**
* Return the y coordinate
*/

public int getY()
{
return y;
}


/** *//**
* Set the x and y coordinates
*/

public void setRectangular(int newX, int newY)
{
setX(newX);
setY(newY);
}


/** *//**
* Set the X coordinate
*/

public void setX(int newX)
{
x = newX;
}


/** *//**
* set the y coordinate
*/

public void setY(int newY)
{
y = newY;
}


/** *//**
* Move the point by the specified x and y offset
*/

public void offset(int deltaX, int deltaY)
{
setRectangular(x + deltaX, y + deltaY);
}


/** *//**
* Make a string of this
*/

public String toString()
{
return "(" + getX() + ", " + getY() + ")" ;
}
}
BoundPoint.aj
package bean;

import java.beans.*;
import java.io.Serializable;


/**//*
* Add bound properties and serialization to point objects
*/


aspect BoundPoint
{

/**//*
* privately introduce a field into Point to hold the property
* change support object. `this' is a reference to a Point object.
*/
private PropertyChangeSupport Point.support = new PropertyChangeSupport(this);


/**//*
* Introduce the property change registration methods into Point.
* also introduce implementation of the Serializable interface.
*/

public void Point.addPropertyChangeListener(PropertyChangeListener listener)
{
support.addPropertyChangeListener(listener);
}

public void Point.addPropertyChangeListener(String propertyName,

PropertyChangeListener listener)
{

support.addPropertyChangeListener(propertyName, listener);
}

public void Point.removePropertyChangeListener(String propertyName,

PropertyChangeListener listener)
{
support.removePropertyChangeListener(propertyName, listener);
}


public void Point.removePropertyChangeListener(PropertyChangeListener listener)
{
support.removePropertyChangeListener(listener);
}


public void Point.hasListeners(String propertyName)
{
support.hasListeners(propertyName);
}

declare parents: Point implements Serializable;


/** *//**
* Pointcut describing the set<property> methods on Point.
* (uses a wildcard in the method name)
*/
pointcut setter(Point p): call(void Point.set*(*)) && target(p);


/** *//**
* Advice to get the property change event fired when the
* setters are called. It's around advice because you need
* the old value of the property.
*/

void around(Point p): setter(p)
{
String propertyName =
thisJoinPointStaticPart.getSignature().getName().substring("set".length());
int oldX = p.getX();
int oldY = p.getY();
proceed(p);

if (propertyName.equals("X"))
{
firePropertyChange(p, propertyName, oldX, p.getX());

} else
{
firePropertyChange(p, propertyName, oldY, p.getY());
}
}


/**//*
* Utility to fire the property change event.
*/
void firePropertyChange(Point p,
String property,
double oldval,

double newval)
{
p.support.firePropertyChange(property,
new Double(oldval),
new Double(newval));
}
}
Demo.java
package bean;

import java.beans.*;
import java.io.*;


public class Demo implements PropertyChangeListener
{

static final String fileName = "test.tmp";


/** *//**
* when Demo is playing the listener role,
* this method reports that a propery has changed
*/

public void propertyChange(PropertyChangeEvent e)
{
System.out.println("Property " + e.getPropertyName() + " changed from " +
e.getOldValue() + " to " + e.getNewValue() );
}


/** *//**
* main: test the program
*/

public static void main(String[] args)
{
Point p1 = new Point();
p1.addPropertyChangeListener(new Demo());
System.out.println("p1 =" + p1);
p1.setRectangular(5,2);
System.out.println("p1 =" + p1);
p1.setX( 6 );
p1.setY( 3 );
System.out.println("p1 =" + p1);
p1.offset(6,4);
System.out.println("p1 =" + p1);
save(p1, fileName);
Point p2 = (Point) restore(fileName);
System.out.println("Had: " + p1);
System.out.println("Got: " + p2);
}


/** *//**
* Save a serializable object to a file
*/

static void save(Serializable p, String fn)
{

try
{
System.out.println("Writing to file: " + p);
FileOutputStream fo = new FileOutputStream(fn);
ObjectOutputStream so = new ObjectOutputStream(fo);
so.writeObject(p);
so.flush();

} catch (Exception e)
{
System.out.println(e);
System.exit(1);
}
}


/** *//**
* Restore a serializable object from the file
*/

static Object restore(String fn)
{

try
{
Object result;
System.out.println("Reading from file: " + fn);
FileInputStream fi = new FileInputStream(fn);
ObjectInputStream si = new ObjectInputStream(fi);
return si.readObject();

} catch (Exception e)
{
System.out.println(e);
System.exit(1);
}
return null;
}
}
跑跑Demo输出的结果:
p1 =(0, 0)
Property X changed from 0.0 to 5.0
Property Y changed from 0.0 to 2.0
p1 =(5, 2)
Property X changed from 5.0 to 6.0
Property Y changed from 2.0 to 3.0
p1 =(6, 3)
Property X changed from 6.0 to 12.0
Property Y changed from 3.0 to 7.0
p1 =(12, 7)
Writing to file: (12, 7)
Reading from file: test.tmp
Had: (12, 7)
Got: (12, 7)

好,跟上次一样,将编译的代码反编来对照一下。看看AspectJ根据“规则”(aspect的定义)修改了代码的哪些地方,给你一个明白!
三个文件:Point.class , BoundPoint.class , Demo.class
代码上,我添加了一些注释,反编译出来的文件般都没什么注释的。
Point.class
package bean;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import org.aspectj.lang.Signature;
import org.aspectj.runtime.internal.AroundClosure;
import org.aspectj.runtime.reflect.Factory;

//BoundPoint.aj 中的声明“declare parents: Point implements Serializable;”使Point实现了Serializable
public class Point implements Serializable


{

Point()

{
//为Point添加向关注者提供属性改变通知的支持,target为this(自身)
BoundPoint.ajc$interFieldInit$bean_BoundPoint$bean_Point$support(this);
x = 0;
y = 0;
}

public int getX()

{
return x;
}

public int getY()

{
return y;
}

public void setRectangular(int newX, int newY)

{
int i = newX;
Point point = this;
//原来的setX(newX)函数被定义为pointcut了,在aspectj是代码生成器,这里就难免不作变动咯,呵呵,setX(newX)前后的通知
//但是有点奇怪,同下面定义的setX_aroundBody1$advice方法签名不一致哦,有待研究
setX_aroundBody1$advice(this, point, i, BoundPoint.aspectOf(), point, null, ajc$tjp_0);
int j = newY;
Point point1 = this;
//setY(newY)前后的通知
setY_aroundBody3$advice(this, point1, j, BoundPoint.aspectOf(), point1, null, ajc$tjp_1);
}

public void setX(int newX)

{
x = newX;
}

public void setY(int newY)

{
y = newY;
}

public void offset(int deltaX, int deltaY)

{
setRectangular(x + deltaX, y + deltaY);
}

public String toString()

{
return (new StringBuilder("(")).append(getX()).append(", ").append(getY()).append(")").toString();
}


/** *//**
* 改变属性(x)值
* @param point
* @param point1
* @param i
*/
private static final void setX_aroundBody0(Point point, Point point1, int i)

{
point1.setX(i);
}


/** *//**
* 调用setX(newX)前后的通知,想一想,因为Point实现属性改变通知支持,那么当改变属性(用setter函数),用aop实现的,
* 那么setX(newX)的代码就会被修改。属性还是要改便的呀,所以“setX_aroundBody0(this, s1, ajc_aroundClosure);”的作用
* 就是