2014-08-27 33 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時,也可以重用所生成的類。