CGLIB动态代理学习

       面向切面的编程(AOP)和 IOC作为Spring的两个最重要的模块,Spring其他的模块都是建立在它们之上。正好最近在学习Spring的相关知识,看到AOP知识中的动态代理

时用到了CGLIB的相关知识,隧上网整理一下其相关知识,待以后备用。

 

cglib基本信息

  1. cglib的官方网站: http://cglib.sourceforge.net/
  2. 官网的samples : http://cglib.sourceforge.net/xref/samples/
  3. CGLIB学习文档:http://www.iteye.com/topic/799827http://www.open-open.com/doc/view/0212ea10f7eb4535a971b4f62d581aae

 

使用CGLIB创建动态代理

1、先看一个简单的代理示例

public class CGLIBSample {
 public static void main(String args[]){
  Enhancer en = new Enhancer();
  en.setSuperclass(CGLIBSample.class);//CGLIB是动态生成目标类的子类,此处设置动态代理的父类
  en.setCallback(new CGLIBMethodIntercept());//设置回调
  CGLIBSample samp = (CGLIBSample)en.create();//创建目标代理子类
  samp.print();
 }
 
 public void print(){
  System.out.println("CGLIB学习示例");
 }
}
class CGLIBMethodIntercept implements MethodInterceptor{
 
 /**
  * obj  代理目标类对象
  * arg1 拦截方法,即目标类的方法,一般用不到
  * arg2 方法的参数
  * proxyMethod  代理类方法,通常首选使用,应为他更快
  */
 public Object intercept(Object obj, Method arg1, Object[] arg2,
   MethodProxy proxyMethod) throws Throwable {
  System.out.println("前置增强(通知)");//在调用方法前我们可以做一些事情,比如说Spring的前置通知
  //通过代理子类访问目标类的方法,此处也可以使用arg1直接调用
  //但是这两种方法是有区别
  Object result = proxyMethod.invokeSuper(obj, arg2);
  System.out.println("后置增强(通知)");
  return result;
 }
 
}

 

2、接下来我们看一下创建代理使用的主要类图

 

1、Callback接口从图中可以看出是一个很重要的接口,所有被Enhancer类调用的回调(callback)接口都实现了这个接口

2、MethodInterceptor 接口是最常用的回调接口,它经常被基于代理的AOP用来拦截方法的调用。当它作为代理方法的回调时,当对基于代理方法的调用时,在调用原对象的方法之前会调用这个方法,如下图所示。

3、FixedValue 为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。

4、NoOp NoOp回调把对方法的调用直接委派到这个方法在父类中的实现

5、LazyLoader 当实际的对象需要延迟加载时,可以使用它回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用

6、Dispatcher Dispatcher回调和LazyLoder回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用

7、ProxyRefDispatcher ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以吧代理对象作为装载对象方法的一个参数传递

8、CallbackFilter 有选择的对一些方法使用回调

3、使用CallbackFilter在方法层设置回调

CallbackFilter 允许我们在方法层设置回调。比如一个类中有两个两个方法,一个需要权限检查,一个不需要。我们就可以使用CallbackFilter回调实现。(Spring实例化策略

 

下面示例中UserService类中identifyMethod方法调用前需要进行权限验证

public class CallbackFilterTest {
 public static void main(String[] args){
  Enhancer en = new Enhancer();
  en.setSuperclass(UserService.class);
  CallbackFilter filter = new UserServiceCallbackFilter();
  en.setCallbackFilter(filter);
  Callback identifyCallBack = new IdentifyMethodInterceptor();
  Callback otherCallBack = NoOp.INSTANCE;//直接使用NoOper回调 for 不需要权限验证的方法
  Callback[] callbacks = new Callback[]{identifyCallBack,otherCallBack};//数组中的位置必须和CallbackFilter中的一致
  en.setCallbacks(callbacks);
  UserService userService = (UserService)en.create();
  userService.identifyMethod();
  userService.ordinaryMethod();
 }
}

class UserService{
 public void identifyMethod(){
  System.out.println("访问这个访问需要权限!");
 }
 public void ordinaryMethod(){
  System.out.println("这是一个普通方法,不需要验证访问权限!");
 }
}
/**
 * 为需要进行权限验证的回调类
 */
class IdentifyMethodInterceptor implements MethodInterceptor{

 public Object intercept(Object arg0, Method arg1, Object[] arg2,
   MethodProxy arg3) throws Throwable {
  System.out.println("进行访问权限验证……!");
  return arg3.invokeSuper(arg0, arg2);
 }
}
class UserServiceCallbackFilter implements CallbackFilter{
 private static final int IDENTIFY = 0;//回调数组下标,必须与enhance设置的回调数组下标对应
 private static final int ORDINARY = 1;
 public int accept(Method interceptorMethod) {
  if(interceptorMethod.getName().equals("identifyMethod"))
   return IDENTIFY;
  return ORDINARY;
 }
 
}

 4、使用Mixing(可以整合多个接口)

如果接口数组中有相同的方法,则代理类总是调用接口数组中首先声明的接口中的那个方法

public class MixinTest {
 public static void main(String[] args){
  Class[] interfaces = new Class[]{A.class,B.class};//代理目标类接口数组
  Object[] delegates = new Object[]{new AImplement(),new BImplement()};//代理目标类实现数组,需要和上边的接口数组顺序对应
  Object obj = Mixin.create(interfaces, delegates);
  ((A)obj).a();
  ((B)obj).b();
 }
 
}
interface A{
 public void a();
}
interface B{
 public void b();
}
class AImplement implements A{
 public void a(){
  System.out.println("调用A.a()");
 }
}
class BImplement implements B{
 public void b(){
  System.out.println("调用B.b()");
 }
}

 

 5、使用LazyLoader实现延迟加载

public class LazyLoaderTest {
 public static void main(String[] args){
  Enhancer en = new Enhancer();
  MyService ser = (MyService)en.create(MyService.class,new Lazy());//此时MyService对象其实还没有加载,只有在调用方法时才去真正示例化
  System.out.println("调用MyService.f()!");
  ser.f();
 }
}
class Lazy implements LazyLoader{
 public Object loadObject() throws Exception {
  System.out.println("延迟加载开始……");
  Thread.currentThread().sleep(5000);//当前线程sleep 5秒,模拟对象加载
  return new MyService();
 }
}
class MyService{
 public void f(){
  System.out.println("调用服务");
 }
}

 

CGLIB主要用到的知识在这里就记录的差不多了,下面在粘上Spring中使用CGLIB技术实现对bean的实例化。

/**
  * An inner class so we don't have a CGLIB dependency in core.
  */
 private static class CglibSubclassCreator {

  private static final Log logger = LogFactory.getLog(CglibSubclassCreator.class);

  private final RootBeanDefinition beanDefinition;

  private final BeanFactory owner;

  public CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
   this.beanDefinition = beanDefinition;
   this.owner = owner;
  }
  public Object instantiate(Constructor ctor, Object[] args) {
   Enhancer enhancer = new Enhancer();
   enhancer.setSuperclass(this.beanDefinition.getBeanClass());
   enhancer.setCallbackFilter(new CallbackFilterImpl());
   enhancer.setCallbacks(new Callback[] {
     NoOp.INSTANCE,
     new LookupOverrideMethodInterceptor(),
     new ReplaceOverrideMethodInterceptor()
   });

   return (ctor == null) ?
     enhancer.create() :
     enhancer.create(ctor.getParameterTypes(), args);
  }


  /**
   * Class providing hashCode and equals methods required by CGLIB to
   * ensure that CGLIB doesn't generate a distinct class per bean.
   * Identity is based on class and bean definition. 
   */
  private class CglibIdentitySupport {

   /**
    * Exposed for equals method to allow access to enclosing class field
    */
   protected RootBeanDefinition getBeanDefinition() {
    return beanDefinition;
   }

   @Override
   public boolean equals(Object other) {
    return (other.getClass().equals(getClass()) &&
      ((CglibIdentitySupport) other).getBeanDefinition().equals(beanDefinition));
   }

   @Override
   public int hashCode() {
    return beanDefinition.hashCode();
   }
  }


  /**
   * CGLIB MethodInterceptor to override methods, replacing them with an
   * implementation that returns a bean looked up in the container.
   */
  private class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {

   public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    // Cast is safe, as CallbackFilter filters are used selectively.
    LookupOverride lo = (LookupOverride) beanDefinition.getMethodOverrides().getOverride(method);
    return owner.getBean(lo.getBeanName());
   }   
  }


  /**
   * CGLIB MethodInterceptor to override methods, replacing them with a call
   * to a generic MethodReplacer.
   */
  private class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {

   public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    ReplaceOverride ro = (ReplaceOverride) beanDefinition.getMethodOverrides().getOverride(method);
    // TODO could cache if a singleton for minor performance optimization
    MethodReplacer mr = (MethodReplacer) owner.getBean(ro.getMethodReplacerBeanName());
    return mr.reimplement(obj, method, args);
   }
  }


  /**
   * CGLIB object to filter method interception behavior.
   */
  private class CallbackFilterImpl extends CglibIdentitySupport implements CallbackFilter {
   
   public int accept(Method method) {
    MethodOverride methodOverride = beanDefinition.getMethodOverrides().getOverride(method);
    if (logger.isTraceEnabled()) {
     logger.trace("Override for '" + method.getName() + "' is [" + methodOverride + "]");
    }
    if (methodOverride == null) {
     return PASSTHROUGH;
    }
    else if (methodOverride instanceof LookupOverride) {
     return LOOKUP_OVERRIDE;
    }
    else if (methodOverride instanceof ReplaceOverride) {
     return METHOD_REPLACER;
    }
    throw new UnsupportedOperationException(
      "Unexpected MethodOverride subclass: " + methodOverride.getClass().getName());
   }
  }
 }

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值