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 開發中的一條基本原則,因此這種限制並不會對我們的開發造成障礙。