CGLib实现变化字段探测的供能

    为了巩固 cglib 的知识,下面我们实现一个稍微复杂一点的例子。例、请实现一个拦截器,使其能够检测一个 javabean 的哪些字段改变了。

( 1 )首先定义一个 javabean 。

public class personinfo

{

     private string name;

 

     private string email;

 

     private int age;

 

     private string address;

 

     public string getemail()

     {

         return email;

     }

 

     public void setemail(string email)

     {

         this.email = email;

     }

 

     public string getname()

     {

         return name;

     }

 

     public void setname(string name)

     {

         this.name = name;

     }

 

     public string getaddress()

     {

         return address;

     }

 

     public void setaddress(string address)

     {

         this.address = address;

     }

 

     public int getage()

     {

         return age;

     }

 

     public void setage(int age)

     {

         this.age = age;

     }

}

( 2 )定义一个 methodinterceptor ,这一步是最关键的 。

import java.lang.reflect.method;

import java.util.collections;

import java.util.hashset;

import java.util.set;

 

import net.sf.cglib.proxy.methodinterceptor;

import net.sf.cglib.proxy.methodproxy;

 

public class javabeandatachangeinterceptor implements methodinterceptor

{

     private static final string set = "set";

 

     private set changedpropset;

 

     public javabeandatachangeinterceptor()

     {

         changedpropset = new hashset();

     }

 

     public object intercept(object obj, method method, object[] args,

              methodproxy proxy) throws throwable

     {

         string name = method.getname();

         if (name.startswith(set))

         {

              string s = name.substring(set.length());

              changedpropset.add(s);

         }

         return proxy.invokesuper(obj, args);

     }

 

     public set getchangedpropset()

     {

         return collections.unmodifiableset(changedpropset);

     }

 

     public void reset()

     {

         changedpropset.clear();

     }

}

定义一个集合 changedpropset 用来存放修改了的字段名,增加了一个方法 reset 用来清空此集合,增加了一个 getchangedpropset 方法用来供外界得到修改了的字段,为了防止调用者对 changedpropset 做修改,因此我们采用 collections.unmodifiableset 对返回的集合进行不可修改的修饰。

在 intercept 方法中,我们判断如果被调用的方法以 set 开头,则把此字段名放入 changedpropset 集合中。

( 3 )定义剖析用工具类。

import net.sf.cglib.proxy.callback;

import net.sf.cglib.proxy.factory;

 

public class javabeaninterceptorutils

{

     public static javabeandatachangeinterceptor getinterceptor(

              object obj)

     {

         if (!(obj instanceof factory))

         {

              return null;

         }

         factory f = (factory) obj;

         callback[] callbacks = f.getcallbacks();

         for (int i = 0, n = callbacks.length; i < n; i++)

         {

              callback callback = callbacks[i];

              if (callback instanceof javabeandatachangeinterceptor)

              {

                   return (javabeandatachangeinterceptor) callback;

              }

         }

         return null;

     }

}

  这个 javabeaninterceptorutils 只有一个方法 getinterceptor ,这个方法用于从一个被 cglib 代理的 javabean 中取出拦截器 javabeandatachangeinterceptor 。

  前边提到了, cglib 实现拦截的方式就是生成被拦截类的子类,这个子类实现了 net.sf.cglib.proxy.factory 接口,这个接口中有一个非常重要的方法 getcallbacks() ,通过这个方法我们可以得到所有的拦截器 。

( 4 ) 主程序

public class mainapp

{

     public static void main(string[] args)

     {

         enhancer enhancer = new enhancer();

         enhancer.setsuperclass(personinfo.class);

         enhancer.setcallback(new javabeandatachangeinterceptor());

 

         personinfo info = (personinfo) enhancer.create();

         // 对生成的 javabean 做一些初始化

         info.setaddress(" 地址 1");

         info.setage(21);

         info.setname("tom");

 

         // 得到拦截器

         javabeandatachangeinterceptor interceptor = javabeaninterceptorutils

                   .getinterceptor(info);

         // 复位修改字段记录集合

         interceptor.reset();

 

         // 对 javabean 做一些修改

         editpersoninf(info);

 

         // 得到修改了的字段

         iterator it = interceptor.getchangedpropset().iterator();

         while (it.hasnext())

         {

              system.out.println(it.next());

         }

     }

 

     private static void editpersoninf(personinfo info)

     {

         info.setname("jim");

         info.setaddress("n.y street");

     }

}   

运行结果:

address

name

 

  这个“变化字段拦截器”是有一定实际意义的,比如可以用来实现“只保存修改了的字段以提高效率”等功能 。

 

  很多资料中都说如果要使用 jdk proxy ,被代理的对象的类必须要实现接口,这种说法是不严谨的。从上边的例子我们可以看出,正确的说法应该是:如果要使用 jdk proxy ,那么我们要通过代理调用的方法必须定义在一个接口中。“面向接口编程而不是面向实现编程”是 oop 开发中的一条基本原则,因此这种限制并不会对我们的开发造成障碍。