CGLib實(shí)現(xiàn)變化字段探測(cè)的供能
為了鞏固 cglib 的知識(shí),下面我們實(shí)現(xiàn)一個(gè)稍微復(fù)雜一點(diǎn)的例子。例、請(qǐng)實(shí)現(xiàn)一個(gè)攔截器,使其能夠檢測(cè)一個(gè) javabean 的哪些字段改變了。 ( 1 )首先定義一個(gè) 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 )定義一個(gè) methodinterceptor ,這一步是最關(guān)鍵的 。 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(); } } 定義一個(gè)集合 changedpropset 用來(lái)存放修改了的字段名,增加了一個(gè)方法 reset 用來(lái)清空此集合,增加了一個(gè) getchangedpropset 方法用來(lái)供外界得到修改了的字段,為了防止調(diào)用者對(duì) changedpropset 做修改,因此我們采用 collections.unmodifiableset 對(duì)返回的集合進(jìn)行不可修改的修飾。 在 intercept 方法中,我們判斷如果被調(diào)用的方法以 set 開(kāi)頭,則把此字段名放入 changedpropset 集合中。
( 3 )定義剖析用工具類(lèi)。 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; } } 這個(gè) javabeaninterceptorutils 只有一個(gè)方法 getinterceptor ,這個(gè)方法用于從一個(gè)被 cglib 代理的 javabean 中取出攔截器 javabeandatachangeinterceptor 。 前邊提到了, cglib 實(shí)現(xiàn)攔截的方式就是生成被攔截類(lèi)的子類(lèi),這個(gè)子類(lèi)實(shí)現(xiàn)了 net.sf.cglib.proxy.factory 接口,這個(gè)接口中有一個(gè)非常重要的方法 getcallbacks() ,通過(guò)這個(gè)方法我們可以得到所有的攔截器 。 ( 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(); // 對(duì)生成的 javabean 做一些初始化 info.setaddress(" 地址 1"); info.setage(21); info.setname("tom"); // 得到攔截器 javabeandatachangeinterceptor interceptor = javabeaninterceptorutils .getinterceptor(info); // 復(fù)位修改字段記錄集合 interceptor.reset(); // 對(duì) 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"); } } 運(yùn)行結(jié)果: address name 這個(gè)“變化字段攔截器”是有一定實(shí)際意義的,比如可以用來(lái)實(shí)現(xiàn)“只保存修改了的字段以提高效率”等功能 。 很多資料中都說(shuō)如果要使用 jdk proxy ,被代理的對(duì)象的類(lèi)必須要實(shí)現(xiàn)接口,這種說(shuō)法是不嚴(yán)謹(jǐn)?shù)摹纳线叺睦游覀兛梢钥闯觯_的說(shuō)法應(yīng)該是:如果要使用 jdk proxy ,那么我們要通過(guò)代理調(diào)用的方法必須定義在一個(gè)接口中。“面向接口編程而不是面向?qū)崿F(xiàn)編程”是 oop 開(kāi)發(fā)中的一條基本原則,因此這種限制并不會(huì)對(duì)我們的開(kāi)發(fā)造成障礙。