簡體   English   中英

如何使用ByteBuddy攔截器替代CGLib?

[英]How to use ByteBuddy interceptor to replace CGLib?

我正在嘗試用 ByteBuddy 實現替換我的 CGLib 攔截器,但它不起作用。 在 CGLIB 中,我捕獲每個方法調用並將其發送到 ObjectProxy 中的一個方法。 我已經閱讀了這個這個以及其他很多人,但它仍然不起作用。

這是我的代碼草稿:TestByteBuddy.java

import static java.lang.ClassLoader.getSystemClassLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;


/**
 *
 * @author Marcelo D. Ré {@literal <marcelo.re@gmail.com>}
 */
public class TestByteBuddy {
    private final static Logger LOGGER = Logger.getLogger(TestByteBuddy.class .getName());
    static {
        if (LOGGER.getLevel() == null) {
            LOGGER.setLevel(Level.INFO);
        }
    }

    public TestByteBuddy() {
        System.out.println("Inite BB test...");
    }
    
    public void test() {
        
        try {
            Class<?> poClass = new ByteBuddy()
                    .subclass(BBFoo.class)
                    .defineField("___ogm___interceptor", ObjectProxy.class, Visibility.PUBLIC)
                    .implement(ITest.class)
                    .method(ElementMatchers.any()) // isDeclaredBy(ITest.class)
                    .intercept(MethodDelegation.toField("___ogm___interceptor"))   // MethodDelegation.to(bbi)
                    .make()
                    .load(getSystemClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                    .getLoaded();

            // crear la instancia
            BBFoo po = (BBFoo) poClass.newInstance();
            
            // agregar el interceptor
            ObjectProxy bbi = new ObjectProxy();
            bbi.setInternalState("primer objeto");
            poClass.getField("___ogm___interceptor").set(po, bbi);
            
            
            // crear la instancia
            BBFoo po2 = (BBFoo) poClass.newInstance();
            // agregar el interceptor
            ObjectProxy bbi2 = new ObjectProxy();
            bbi2.setInternalState("segundo objeto");
            poClass.getField("___ogm___interceptor").set(po2, bbi2);
            
            // testear los objetos
         
            System.out.println("*************************************");
            System.out.println("invocar a setTestString....");
            po.setTestString("Objeto 1");
            po.sayHello();

            System.out.println(""+po.testString);
            ((ITest)po).testCall();
            ((ITest)po).incValCall();
            System.out.println("ObjectProxy InternalState: "+((ITest)po).getInternalState());
            
            System.out.println("*************************************");
            System.out.println("invocar a setTestString....");
            po2.setTestString("Objeto 2");
            
            po2.sayHello();
            System.out.println(""+po.testString);
         
            ((ITest)po2).testCall();
            ((ITest)po2).incValCall();
            System.out.println("ObjectProxy InternalState: "+((ITest)po2).getInternalState());
            System.out.println("*************************************");
            
        } catch (SecurityException ex) {
            Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchFieldException ex) {
            Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
        
    public static void main(String[] args) {
        new TestByteBuddy().test();
    }
}

ObjectProxy.java

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperMethod;
import net.bytebuddy.implementation.bind.annotation.This;

/**
 *
 * @author Marcelo D. Ré {@literal <marcelo.re@gmail.com>}
 */
public class ObjectProxy implements ITest {
    private final static Logger LOGGER = Logger.getLogger(ObjectProxy.class .getName());
    static {
        if (LOGGER.getLevel() == null) {
            LOGGER.setLevel(Level.INFO);
        }
    }

    String internalState; 

    public ObjectProxy() {
    
    }
    
    @Override
    public String getInternalState() {
        return internalState;
    }
    
    @Override
    public void setInternalState(String internalState) {
        this.internalState = internalState;
    }
    
    @Override
    public void testCall() {
        System.out.println("ObjectProxy TestCall");
    }

    @Override
    public int incValCall() {
        System.out.println("ObjectProxy  incValCall");
        return 0;
    }
    
    // a este método se llama cuando se usa MethodDeletation
    @RuntimeType
    public Object intercept(@This Object self, 
                            @Origin Method method, 
                            @AllArguments Object[] args, 
                            @SuperMethod Method superMethod) throws Throwable {
        Object res = null;
        System.out.println(">>>>>>>>> Intercepted: "+method.getName());
        switch(method.getName()){
            case "testCall": 
                System.out.println("intercept TestCall");
                this.testCall();
                break;
        
            case "incValCall": 
                System.out.println("intercept incValCall");
                this.incValCall();
                break;
                
            default: 
                System.out.println("intercept default: "+self.getClass().toString() +" : "+ superMethod.getName()+" : "+method.getName());
                res = superMethod.invoke(self, args);
            }
            System.out.println("^^^^^^^^^^^^^^^^");
        return res;
     }

ITest.java

public interface ITest {
    public void testCall();
    public int incValCall();
    
    public String getInternalState();
    public void setInternalState(String internalState) ;
    
}

BBFoo.java

public class BBFoo {
    private final static Logger LOGGER = Logger.getLogger(BBFoo.class .getName());
    static {
        if (LOGGER.getLevel() == null) {
            LOGGER.setLevel(Level.INFO);
        }
    }
    
    public String testString;
    private int val = 0;
    
    public void sayHello(){
        System.out.println("Hola mundo! soy "+this.testString);
    }
    
    public int incVal() {
        val++;
        return val;
    }
    
    public void setTestString(String s) {
        testString = s;
    }
    
    public String getTestString() {
        return testString;
    }
}

這是我得到的 output: 在此處輸入圖像描述


Rafael 回復后第一版

根據建議,我重新編輯了 TestByteBuddy.java,現在它看起來像這樣:


import static java.lang.ClassLoader.getSystemClassLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

/**
 *
 * @author Marcelo D. Ré {@literal <marcelo.re@gmail.com>}
 */
public class TestByteBuddy {
    private final static Logger LOGGER = Logger.getLogger(TestByteBuddy.class .getName());
    static {
        if (LOGGER.getLevel() == null) {
            LOGGER.setLevel(Level.INFO);
        }
    }

    public TestByteBuddy() {
        System.out.println("Inite BB test...");
    }
    
    private <T> T getProxyIntance(Class<T> c) {
        T po = null;
        try {
            Class<?> poClass = new ByteBuddy()
                    .subclass(c)
                    .defineField("___ogm___interceptor", ObjectProxy.class, Visibility.PUBLIC)
                    .implement(ITest.class)
                    .method(ElementMatchers.any()) // isDeclaredBy(ITest.class)
                    .intercept(MethodDelegation   // This.class,Origin.class,AllArguments.class,SuperMethod.class)
                                .withDefaultConfiguration() //.withBinders(TargetMethodAnnotationDrivenBinder.ParameterBinder.DEFAULTS)
                                .filter(ElementMatchers.named("intercept"))
                                .toField("___ogm___interceptor"))   // MethodDelegation.to(bbi)
                    .make()
                    .load(getSystemClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                    .getLoaded();

            // crear la instancia
            po = (T) poClass.newInstance();
            
            // agregar el interceptor
            ObjectProxy bbi = new ObjectProxy();
            poClass.getField("___ogm___interceptor").set(po, bbi);
            
        } catch (NoSuchFieldException | InstantiationException | IllegalAccessException ex) {
            Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
        }
        return po;
    }
    
    public void test() {
        
        try {


            // crear la instancia
            BBFoo po = this.getProxyIntance(BBFoo.class);
            
            // crear la instancia
            BBFoo po2 = this.getProxyIntance(BBFoo.class);
            
            
            // testear los objetos
            System.out.println("*************************************");
            System.out.println("invocar a setTestString....");
            po.setTestString("Objeto 1");
            po.sayHello();

            System.out.println(""+po.testString);
            ((ITest)po).testCall();
            int i = ((ITest)po).incValCall();
            System.out.println("ObjectProxy InternalState: "+((ITest)po).getInternalState());
            
            System.out.println("*************************************");
            System.out.println("invocar a setTestString....");
            po2.setTestString("Objeto 2");
            
            po2.sayHello();
            System.out.println(""+po.testString);
         
            ((ITest)po2).testCall();
            i = ((ITest)po2).incValCall();
            System.out.println("ObjectProxy InternalState: "+((ITest)po2).getInternalState());
            System.out.println("*************************************");
          
            BBFooEx poEx = this.getProxyIntance(BBFooEx.class);
            
            BBFooExEx poExEx = this.getProxyIntance(BBFooExEx.class);
            
        } catch (SecurityException ex) {
            Logger.getLogger(TestByteBuddy.class.getName()).log(Level.SEVERE, null, ex);
        } 
        
    }
        
    public static void main(String[] args) {
        new TestByteBuddy().test();
    }
}

並且我必須在ObjectProxy.java中的每個方法中添加@IgnoreForBinding注釋,除了intercept

但是現在,如果我再添加這兩個 class,它會失敗:BBFooEx.java

public class BBFooEx extends BBFoo {
    private final static Logger LOGGER = Logger.getLogger(BBFooEx.class .getName());
    static {
        if (LOGGER.getLevel() == null) {
            LOGGER.setLevel(Level.INFO);
        }
    }
    private String svex;
    
    
    public BBFooEx() {
        this(null, "default");
    }
    
    public BBFooEx(String s, String svex) {
        super(s);
        this.svex = svex;
    }
    
    public HashMap<String, String> hmString;
    public HashMap<String, BBFooEx> ohmSVE;
    
    public void initHashMapString() {
        this.hmString = new HashMap<>();
        this.hmString.put("hmString 1", "hmString 1");
        this.hmString.put("hmString 1", "hmString 2");
        this.hmString.put("hmString 1", "hmString 3");
    }
    
    
}

BBFooExEx.java

public class BBFooExEx extends BBFooEx {
    private final static Logger LOGGER = Logger.getLogger(BBFooExEx.class .getName());
    static {
        if (LOGGER.getLevel() == null) {
            LOGGER.setLevel(Level.INFO);
        }
    }

    public BBFooExEx() {
        ohmSVE = new HashMap<>();
        initHashMapString();
    }
    
    
}

當 ByteBuddy 嘗試實例化 BBFooExEx 時,它失敗並出現 NullPointerException:

Exception in thread "main" java.lang.NullPointerException
    at bb.BBFooExEx$ByteBuddy$bUrgo9zW.initHashMapString(Unknown Source)
    at bb.BBFooExEx.<init>(BBFooExEx.java:26)
    at bb.BBFooExEx$ByteBuddy$bUrgo9zW.<init>(Unknown Source)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at java.base/java.lang.Class.newInstance(Class.java:584)
    at bb.TestByteBuddy.getProxyIntance(TestByteBuddy.java:50)
    at bb.TestByteBuddy.test(TestByteBuddy.java:110)

有時您可能針對錯誤的方法。 使用:MethodDelegation.withDefaultConfiguration().filter(named("intercept")).toField(...)

這樣,您可以確保 Byte Buddy 僅嘗試您的目標方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM