2014-08-27 97 views
6

我试图将Cglib代理转换为ByteBuddy。 Cglib有net.sf.cglib.proxy.Proxy接口来拦截所有的方法调用。我检查了ByteBuddy的文档,但无法找到这样的例子。没有这样的接口,我实例化与ByteBuddy的每个对象,我再次重复同样的事情,并agin。有没有更好的方式与ByteBuddy做到这一点?ByteBuddy代理接口

这里是我的示例代码片段:

服务:

public class MyService { 

    public void sayFoo() { 
     System.out.println("foo"); 
    } 

    public void sayBar() { 
     System.out.println("bar"); 
    } 
} 

拦截:

public class MyServiceInterceptor { 

    public void sayFoo(@SuperCall Callable<Void> zuper) { 
     try { 
      zuper.call(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public void sayBar(@SuperCall Callable<Void> zuper) { 
     try { 
      zuper.call(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

测试:

import net.bytebuddy.ByteBuddy; 
import net.bytebuddy.ClassFileVersion; 
import net.bytebuddy.dynamic.ClassLoadingStrategy; 
import net.bytebuddy.instrumentation.MethodDelegation; 
import net.bytebuddy.instrumentation.method.matcher.MethodMatchers; 

public class Main { 

    public static void main(String[] args) throws Exception { 
     ByteBuddy buddy = new ByteBuddy(ClassFileVersion.forCurrentJavaVersion()); 
     Class<? extends MyService> serviceClass = 
       buddy.subclass(MyService.class) 
       .method(MethodMatchers.named("sayFoo").or(MethodMatchers.named("sayBar"))) 
       .intercept(MethodDelegation.to(new MyServiceInterceptor())) 
       .make() 
       .load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
       .getLoaded(); 

     MyService service = serviceClass.newInstance(); 

     service.sayFoo(); 
     service.sayBar(); 
    } 
} 

回答

9

字节好友将看任何可能的目标方法和绑定它,如果这是可能的话。如果有多个可能的目标方法,它将绑定最具体的目标方法,或者在模糊不清的情况下抛出异常。在你的例子中,绑定是不明确的,但是当你用截取的方法(Service)命名拦截器方法(在MyServiceInterceptor中)时,Byte Buddy认为用相同名称的拦截器方法拦截每个方法可能是你想要的做。正如javadoc of the MethodInterceptor字节好友提到将:

  1. 发现它能够结合并放弃其他任何方法。
  2. 检查一个方法是否使用@BindingPriority进行了注释,并选择具有最高优先级的方法。
  3. 如果至少有一个方法与拦截方法的名称相同,则截取。
  4. 如果使用@Argument批注,请选择具有最特定参数类型的方法,并类似于Java编译器解析最具体的绑定,以识别重载方法调用的目标。
  5. 采用最多参数的方法。

您还可以通过添加/删除AmbiguityResolvers来更改此默认行为。

如果要指定一个拦截器方法,它能够拦截具有超强方法比你可以编写如下拦截器类中的任何方法:

public class MyServiceInterceptor { 
    @RuntimeType 
    public static Object intercept(@SuperCall Callable<?> zuper) throws Exception { 
    return zuper.call(); 
    } 
} 

方法的名称不事情,Byte Buddy将绑定拦截器,因为它是唯一可能的目标。您需要添加@RuntimeType annotation,因为@SuperCall代理返回Object,并且Byte Buddy需要在截取的方法内投射(也可能取消箱)值。

有了这个拦截器(注意,该方法也static,这样一来,字节好友并不需要添加一个字段持有MyServiceInterceptor一个实例),你可以简单地写:

public class Main { 
    public static void main(String[] args) throws Exception { 
    Class<? extends MyService> serviceClass = new ByteBuddy() 
     .subclass(MyService.class) 
     .method(ElementMatchers.named("sayFoo").or(ElementMatchers.named("sayBar"))) 
     .intercept(MethodDelegation.to(MyServiceInterceptor.class)) 
     .make() 
     .load(Main.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
     .getLoaded(); 

    MyService service = serviceClass.newInstance(); 

    service.sayFoo(); 
    service.sayBar(); 
    } 
} 

,你会得到期望的结果。如示例所示,你可以写的

new ByteBuddy(); 

代替

new ByteBuddy(ClassFileVersion.forCurrentJavaVersion()); 

这是同样的事情。

Byte Buddy不使用任何接口,因为它要避免生成的类与任何Byte Buddy类的依赖关系。这样做,即使在加载不知道Byte Buddy依赖关系的ClassLoader时,也可以重用所生成的类。