2年間のJava開発!JDKダイナミックプロキシの使い方がわからなくても、給料を上げることはできません。

JDK動的プロキシ

動的エージェントを引き出すために、ケースを見てみましょう!

広東広東の朝9時に、かわいい男の子がフリップフロップを身に着けて鳥かごを持って朝の喫茶店に入った。そうです、これは広州の典型的なチャーター会社です!彼の名前でいくつかの建物、生計を立てるためだけに借りて、仕事はありません、この人は本当に退屈です!

ここに質問があります:家賃の徴収は仕事として数えられますか?まあ、実際には、実際のチャーターは自分家賃を徴収するのではなく、仲介業者にそれを任せています。どうして?これには、セキュリティやプライバシーなどが関係していると言えます。考えてみれば、チャーターが自分で家賃を徴収すると、今のところ入居者が多く、他のチャーターは動揺しているだけで、借りるかどうかを尋ねたり、チャーターに時間を無駄にしたりするなど、トラブルを起こすだけです。もちろんそれだけではありません...

使いやすい

さて、賃貸代理店が出てきて、テナントが家を見て、価格を交渉し、契約書に署名し、鍵を届けるなどして、仲介者(代理人)に仕事を任せ、家主はただお金を待つだけです。

2年間のJava開発! JDKダイナミックプロキシの使い方がわからなくても、給料を上げることはできません。

実際のビジネスではなく、コードで出力ステートメントを使用してください。

まず、地主インターフェース地主実装クラスを定義します(なぜインターフェースが必要なのですか?プロキシクラスはインターフェースに基づいて取得され、地主クラスは公開されるべきではありません)

public interface ILandlordService {
    void deliver();
}
public class LandlordServiceImpl implements ILandlordService {
    public void deliver() {
        try {
            System.out.println("告知房东出租成功,房东收钱");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止");
        }
    }
}

次に、元のメソッドを拡張するために使用されるjava.lang.reflect.InvocationHandlerインターフェイスを実装するクラスを作成します(パッケージに注意してください、それは見当違いです)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TransactionInvocationHandler implements InvocationHandler {
    // 需要被代理的对象(房东)
    private Object target;

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /*
        实现接口需要重写的方法
        proxy:代理对象
        method:房东接口中被调用的方法
        args:房东接口中被调用的方法需要的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object retVal = null;
        try {
            System.out.println("交付前与中介的交谈");
            // 房东收钱
            retVal = method.invoke(target, args);
            System.out.println("租房成功");
            System.out.println("给钥匙");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止交付");
        }
        return retVal;
    }
}

最後にテストクラス(テナント)を作成します

import org.junit.Test;
import java.lang.reflect.Proxy;

public class TenantTest {

    @Test
    public void test() {

        //这两个创建对象和设置值应在spring中配置,让spring创建、设值
        // 创建一个房东对象
        ILandlordService landlordService = new LandlordServiceImpl();
        // 创建增强类对象
        TransactionInvocationHandler transactionInvocationHandler = new TransactionInvocationHandler();
        // 把房东对象设置到增强类对象中使用
        transactionInvocationHandler.setTarget(landlordService);

        /*
             使用强转得到房东类代理对象(中介)
             newProxyInstance方法需要三个参数
             ClassLoader loader:类加载器,直接使用本类的类加载器(系统类加载器)
             Class<?>[] interfaces:接口数组
             InvocationHandler h:增强类
         */
        ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                // 这里landlordService对象暂时在测试类中创建,
                // 使用spring后,注入到TransactionInvocationHandler(增强类)的字段(target)中,
                // 直接到增强类中获取,这样测试类(租客)就不知道实现类了(房东)
                landlordService.getClass().getInterfaces(),
                transactionInvocationHandler
        );
        // 调用代理对象的deliver(),会执行增强类的invoke方法,参数method为代理对象调用的方法
        proxy.deliver();
    }
}
执行结果:
交付前与中介的交谈
告知房东出租成功,房东收钱
租房成功
给钥匙

現時点では、多くの人が困惑していますが、プロキシクラスを作成するにはどうすればよいですか?拡張クラスのinvokeメソッドはどのように呼び出すことができますか?それでは、原理を見てみましょう!スクリーンショットを確認することをお勧めします。コードが少し多すぎます。

原理

クラスでnewProxyInstanceメソッドをテストし、クリックします

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
    this.getClass().getClassLoader(),
    landlordService.getClass().getInterfaces(),
    transactionInvocationHandler
);

プロキシクラスのnewProxyInstanceメソッドに移動して、不要なコードを保存します

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    /*
    * Look up or generate the designated proxy class and its constructor.
    * 查找或生成 指定的代理类和它的构造函数
    */
    /*
        获取生成类的构造器
        loader:类加载器
        interfaces:接口数组
    */
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

newProxyInstanceメソッドに移動して、不要なコードを保存し、try-catchを実行します

private static Object newProxyInstance(Class<?> caller,
                                           Constructor<?> cons,
                                           InvocationHandler h) {
    // caller有关安全的,这里忽略
    checkNewProxyPermission(caller, cons.getDeclaringClass());
     /* 
        反射调用构造器创建这个类的对象
        cons:代理类构造器
        h:增强类
    */
    return cons.newInstance(new Object[]{h});
}

次のコードを使用して、プロキシクラスのバイトコードファイルを取得します

ProxyGeneratorはJDK11では公開されなくなりました。ここで使用したソースコードは、JDK8を使用し
てプロキシクラスのバイトコードを取得するためのJKD11です

import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;

public class DynamicProxyClassGenerator {
    public static void main(String[] args) throws Exception {
        // 实现类的class对象
        generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy");
    }

    public static void generateClassFile(Class<?> targetClass, String proxyName) throws Exception {
        // 根据类信息和提供的代理类名称,生成字节码
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, targetClass.getInterfaces());
        String path = targetClass.getResource(".").getPath();
        System.out.println(path);
        FileOutputStream out = null;
        // 保留到硬盘中
        out = new FileOutputStream(path + proxyName + ".class");
        out.write(classFile);
        out.close();
    }
}

プロキシクラスによって生成されるクラスファイルは次のようになります。deliver()メソッドは、定義したクラスに基づいており、Objectクラスによって取得されたメソッドコンテンツは省略されています。

パラメータ化されたコンストラクタを呼び出してオブジェクトを作成します

import com.hao.service.ILandlordService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class LandlordServiceProxy extends Proxy implements ILandlordService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

     // 创建对象时调用的方法
    public LandlordServiceProxy(InvocationHandler var1) throws  {
        // 调用父类(proxy类)构造器
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
    }

    public final String toString() throws  {
    }

    public final void deliver() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    // 把增强类设置到proxy类的字段上
    this.h = h;
}
protected InvocationHandler h;

最後に、Proxy.newProxyInstance()はプロキシクラス(中間)を返します

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
);
proxy.deliver();

次に、deliver()メソッドを呼び出し、プロキシクラスのdeliver()メソッドを入力します。

public final void deliver() throws  {
    try {
        // 调用父类的h字段(增强类)中的invoke方法,m3在本类static{}中获取
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

m3は静的です{}

static {
    try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        // 通过反射得到deliver方法
        m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
}

やっと

プロキシクラスを呼び出すと、最終的には拡張クラスのinvokeメソッドが呼び出されることがわかります。ここをご覧いただきありがとうございます。記事に欠点がある場合は訂正してください。記事が役に立ったと思われる場合は、ぜひお知らせください。Java関連の技術記事や業界情報を毎日共有します。皆さんを歓迎して記事をフォローして転送してください。

おすすめ

転載: blog.51cto.com/14966465/2542738