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

本文出自 “CowNew开源团队” 博客,转载请与作者联系!

你可能感兴趣的:(cglib,探测,xjl,供能,变化字段)