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