ホームページ Java &#&ベース Java動的プロキシの原理

Java動的プロキシの原理

Nov 29, 2019 pm 05:06 PM
java

Java動的プロキシの原理

Java 動的プロキシ メカニズムの出現により、Java 開発者は、プロキシ クラスを手動で作成することなく、インターフェイスのセットとデリゲート クラス オブジェクトを指定するだけで、プロキシ クラスを動的に取得できるようになります。 (推奨: Java ビデオ チュートリアル )

プロキシ クラスは、リフレクション実行のためにすべてのメソッド呼び出しをデリゲート オブジェクトにディスパッチする責任を負います。ディスパッチ実行プロセス中に、開発者は必要に応じて調整することもできますデリゲート クラス オブジェクトとその関数。これは非常に柔軟で柔軟なプロキシ フレームワークです。次に、動的エージェントについて学び始めます。

動的プロキシの簡単な説明

Java の動的プロキシ メカニズムには、2 つの重要なクラスまたはインターフェイスがあります。1 つは InvocationHandler( Interface )、もう 1 つはプロキシ (クラス) です。

1. InvocationHandler (インターフェイス) の説明:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
ログイン後にコピー

すべての動的プロキシ クラスは InvocationHandler インターフェイスを実装する必要があり、各プロキシ クラスのインスタンスは、プロキシ オブジェクトを通じてメソッドを呼び出すと、このメソッドの呼び出しは、InvocationHandler インターフェイスの invoke メソッドによって呼び出されるように転送されます。 InvocationHandler インターフェイスの唯一のメソッドである invoke メソッドを見てみましょう:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
ログイン後にコピー

このメソッドは 3 つのパラメータを受け取り、オブジェクト タイプを返します。それぞれの意味は次のとおりです:

proxy:プロキシしている実際のオブジェクトを参照します。

method: 実際のオブジェクトのメソッドを呼び出したい Method オブジェクトを参照します。

args: のメソッドを呼び出すときに受け取られるパラメータを参照します。実際のオブジェクト パラメータ

によって返されるオブジェクトは、実際のオブジェクト メソッドの戻り値の型を参照します。上記については、次の例で詳しく理解します。

the value to return from the method invocation on the proxy instance.
ログイン後にコピー

2. プロキシ (クラス) の説明:

プロキシは、動的プロキシ クラスとインスタンスを作成するための静的メソッドを提供し、それらのメソッドによって作成されるすべての動的プロキシ クラスのスーパークラスでもあります。

Proxy クラスの機能は、プロキシ オブジェクトを動的に作成することです。よく newProxyInstance メソッドを使用します。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
ログイン後にコピー

パラメーターの理解:

// 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
loader - the class loader to define the proxy class  
// 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
interfaces - the list of interfaces for the proxy class to implement 
// 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
h - the invocation handler to dispatch method invocations to
ログイン後にコピー

戻り結果の理解: プロキシ オブジェクトのインスタンス

指定された呼び出しハンドラーを持つプロキシ インスタンス指定されたクラス ローダーによって定義され、指定されたインターフェイスを実装するプロキシ クラスの

単純な Java プロキシ

動的プロキシのテストと理解のために Java プロジェクトを作成します。プロジェクト構造は次のとおりです。

Java動的プロキシの原理

# 1. まずインターフェイス Interface を定義し、2 つのメソッドを追加します。

package com.huhx.proxy;

public interface Interface {
    void getMyName();

    String getNameById(String id);
}
ログイン後にコピー

2. 上記のインターフェイス RealObject を実装する実際のクラスを定義します:

package com.huhx.proxy;

public class RealObject implements Interface {
    @Override
    public void getMyName() {
        System.out.println("my name is huhx");
    }

    @Override
    public String getNameById(String id) {
        System.out.println("argument id: " + id);
        return "huhx";
    }
}
ログイン後にコピー

3. プロキシ オブジェクトを定義します。実装された上記のインターフェイス インターフェイス:

package com.huhx.proxy;

public class SimpleProxy implements Interface {
    private Interface proxied;

    public SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }

    @Override
    public void getMyName() {
        System.out.println("proxy getmyname");
        proxied.getMyName();
    }

    @Override
    public String getNameById(String id) {
        System.out.println("proxy getnamebyid");
        return proxied.getNameById(id);
    }
}
ログイン後にコピー

4. SimpleMain Main メソッドで、上記の結果をテストします:

package com.huhx.proxy;

public class SimpleMain {
    private static void consume(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) {
        consume(new RealObject());
        System.out.println("========================================================");
        consume(new SimpleProxy(new RealObject()));
    }
}
ログイン後にコピー

5. 実行結果:

my name is huhx
argument id: 1
name: huhx
========================================================
proxy getmyname
my name is huhx
proxy getnamebyid
argument id: 1
name: huhx
ログイン後にコピー

Java の動的プロキシ

上記の単純な Java プロキシを完了したら、今度は Java の動的プロキシを学習し始めます。プロキシ。プロキシを動的に作成し、プロキシされたメソッドへの呼び出しを動的に処理できるため、プロキシの概念よりも一歩進んでいます。ダイナミック プロキシ上で行われたすべての呼び出しは、単一のコール ハンドラーにリダイレクトされます。その役割は、呼び出しのタイプを明らかにし、適切な対応策を決定することです。以下では、Java 動的プロキシの理解を深めるためにケースを使用します。

1. InvocationHandler を継承するプロセッサを作成します。 DynamicProxyHandler

package com.huhx.dynamicproxy;

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

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        System.out.println("dynamic proxy handler constuctor: " + proxied.getClass());
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("dynamic proxy name: " + proxy.getClass());
        System.out.println("method: " + method.getName());
        System.out.println("args: " + Arrays.toString(args));
        
        Object invokeObject = method.invoke(proxied, args);
        if (invokeObject != null) {
            System.out.println("invoke object: " + invokeObject.getClass());
        } else {
            System.out.println("invoke object is null");
        }
        return invokeObject;
    }
}
ログイン後にコピー

2. A を書きます。 test Main メソッド、DynamicProxyMain:

package com.huhx.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.huhx.proxy.Interface;
import com.huhx.proxy.RealObject;

public class DynamicProxyMain {
    public static void consumer(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) throws Exception, SecurityException, Throwable {
        RealObject realObject = new RealObject();
        consumer(realObject);
        System.out.println("==============================");

        // 动态代理
        ClassLoader classLoader = Interface.class.getClassLoader();
        Class<?>[] interfaces = new Class[] { Interface.class };
        InvocationHandler handler = new DynamicProxyHandler(realObject);
        Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler);

        System.out.println("in dynamicproxyMain proxy: " + proxy.getClass());
        consumer(proxy);
    }
}
ログイン後にコピー

3. 実行結果は次のとおりです:

my name is huhx
argument id: 1
name: huhx
==============================
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
in dynamicproxyMain proxy: class com.sun.proxy.$Proxy0
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getNameById
args: [1]
argument id: 1
invoke object: class java.lang.String
name: huhx
ログイン後にコピー

上記の出力結果から、次の結論を引き出すことができます。 :

プロキシ オブジェクトに関連付けられた InvocationHandler は、プロキシ オブジェクトがメソッドを呼び出した場合にのみ、その invoke メソッドを実行します。

invoke の 3 つのパラメータの理解: オブジェクト プロキシはプロキシのオブジェクトです, Method メソッドは実際のオブジェクトでメソッドを呼び出す Method クラス、Object[] args は実際のオブジェクトで呼び出されるメソッドのパラメータです

#Java 動的の原理proxy

1. 動的プロキシのキー コードは Proxy.newProxyInstance(classLoader, Interfaces, handler) です。ソース コードに従って見てみましょう:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  // handler不能为空
    if (h == null) {
        throw new NullPointerException();
    }

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
  // 通过loader和接口,得到代理的Class对象
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
            // create proxy instance with doPrivilege as the proxy class may
            // implement non-public interfaces that requires a special permission
            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    return newInstance(cons, ih);
                }
            });
        } else {
       // 创建代理对象的实例
            return newInstance(cons, ih);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    }
}
ログイン後にコピー

2. newInstance メソッドのソース コードを見てみましょう:

private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h} );
    } catch (IllegalAccessException | InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString());
        }
    }
}
ログイン後にコピー

3. プロキシ オブジェクトを通じてメソッドを呼び出すと、この呼び出しが行われます。メソッドは、InvocationHandler インターフェースの invoke メソッドに転送されます。

この文を反映したコードがソース コードに見つからなかったので、テスト クラスの main メソッドに次のコードを追加しました。
if (proxy instanceof Proxy) {
    InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
    invocationHandler.invoke(proxy, realObject.getClass().getMethod("getMyName"), null);
    System.out.println("--------------------------------------");
}
ログイン後にコピー

这段代码的输出结果如下,与上述中调用代理对象中的getMyName方法输出是一样的,不知道Jvm底层是否是这样判断的:

dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
--------------------------------------
ログイン後にコピー

更多java知识请关注java基础教程栏目。

以上がJava動的プロキシの原理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルの量を見つけるためのJavaプログラム カプセルの量を見つけるためのJavaプログラム Feb 07, 2025 am 11:37 AM

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Spring Tool Suiteで最初のSpring Bootアプリケーションを実行するにはどうすればよいですか? Spring Tool Suiteで最初のSpring Bootアプリケーションを実行するにはどうすればよいですか? Feb 07, 2025 pm 12:11 PM

Spring Bootは、Java開発に革命をもたらす堅牢でスケーラブルな、生産対応のJavaアプリケーションの作成を簡素化します。 スプリングエコシステムに固有の「構成に関する慣習」アプローチは、手動のセットアップを最小化します。

See all articles