為了鞏固 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 用來存放修改了的字段名,增加了一個(gè)方法 reset 用來清空此集合,增加了一個(gè) getChangedPropSet 方法用來供外界得到修改了的字段,為了防止調(diào)用者對(duì) changedPropSet 做修改,因此我們采用 Collections.unmodifiableSet 對(duì)返回的集合進(jìn)行不可修改的修飾。
在 intercept 方法中,我們判斷如果被調(diào)用的方法以 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;
???? }
}
這個(gè) JavaBeanInterceptorUtils 只有一個(gè)方法 getInterceptor ,這個(gè)方法用于從一個(gè)被 CGLib 代理的 JavaBean 中取出攔截器 JavaBeanDataChangeInterceptor 。
前邊提到了,
CGLib
實(shí)現(xiàn)攔截的方式就是生成被攔截類的子類,這個(gè)子類實(shí)現(xiàn)了
net.sf.cglib.proxy.Factory
接口,這個(gè)接口中有一個(gè)非常重要的方法
getCallbacks()
,通過這個(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í)際意義的,比如可以用來實(shí)現(xiàn)“只保存修改了的字段以提高效率”等功能
。
很多資料中都說如果要使用
JDK Proxy
,被代理的對(duì)象的類必須要實(shí)現(xiàn)接口,這種說法是不嚴(yán)謹(jǐn)?shù)摹纳线叺睦游覀兛梢钥闯觯_的說法應(yīng)該是:如果要使用
JDK Proxy
,那么我們要通過代理調(diào)用的方法必須定義在一個(gè)接口中。“面向接口編程而不是面向?qū)崿F(xiàn)編程”是
OOP
開發(fā)中的一條基本原則,因此這種限制并不會(huì)對(duì)我們的開發(fā)造成障礙。