随笔-35  评论-72  文章-0  trackbacks-0

在前面《[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 =(00)
Property X changed from 
0.0 to 5.0
Property Y changed from 
0.0 to 2.0
p1 
=(52)
Property X changed from 
5.0 to 6.0
Property Y changed from 
2.0 to 3.0
p1 
=(63)
Property X changed from 
6.0 to 12.0
Property Y changed from 
3.0 to 7.0
p1 
=(127)
Writing to file: (
127)
Reading from file: test.tmp
Had: (
127)
Got: (
127)

 

好,跟上次一样,将编译的代码反编来对照一下。看看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);”的作用
     * 就是