为了巩固
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
开发中的一条基本原则,因此这种限制并不会对我们的开发造成障碍。