プロキシモードのJava動的プロキシ実装方法
今日、偶然、JDK の動的プロキシについて少し調べてみたくなり、以前から少しは知っていて、その使用法をテストしたかっただけなので、これらのインターフェイスとクラスを簡単に作成しました。
インターフェイス クラス: UserService.java
package com.yixi.proxy; public interface UserService { public int save() ; public void update(int id); }
実装クラス: UserServiceImpl.java
package com.yixi.proxy; public class UserServiceImpl implements UserService { @Override public int save() { System.out.println("user save...."); return 1; } @Override public void update(int id) { System.out.println("update a user " + id); } }
それから、必要なInvocationHandlerをすぐに書きました: この関数は非常に単純で、メソッドの実行の開始時刻と終了時刻を記録するだけです
TimeInvocationHandler .java
package com.yixi.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("startTime : " +System.currentTimeMillis()); Object obj = method.invoke(proxy, args); System.out.println("endTime : " +System.currentTimeMillis()); return obj; } }
それではすべての準備が完了しました。もちろん、テストの作成を開始します。
Test.java
package com.yixi.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { 9 TimeInvocationHandler timeHandler = new TimeInvocationHandler(); UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler); u.update(2); u.save(); } }
喜んで実行しますが、結果は画面いっぱいの例外です:
startTime : 1352877835040 startTime : 1352877835040 startTime : 1352877835040 Exception in thread "main" java.lang.reflect.UndeclaredThrowableException at $Proxy0.update(Unknown Source) at com.yixi.proxy.Test.main(Test.java:11) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) ... 2 more
com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) 例外。問題は TimeInvocationHandle の 12 行目にあることを明確に示しています。つまり、
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("startTime : " +System.currentTimeMillis()); Object obj = method.invoke(proxy, args); System.out.println("endTime : " +System.currentTimeMillis()); return obj; }
Modify TimeInvocationHandler.java
package com.yixi.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimeInvocationHandler implements InvocationHandler { private Object o; public TimeInvocationHandler(Object o){ this.o = o; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("startTime : " +System.currentTimeMillis()); Object obj = method.invoke(o, args); System.out.println("endTime : " +System.currentTimeMillis()); return obj; } }
Modify Test.java
rrreeこれが正しい出力結果です:
package com.yixi.proxy; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl()); UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler); u.update(2); u.save(); } }
コードを減らしたい場合は、匿名クラスを直接記述することができます:
package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.メソッド;
import java.lang.reflect.Proxy ;
public class Test {
public static void main(String[] args) {
Final UserServiceImpl usi = new UserServiceImpl();
UserService u = (UserService) Proxy.newProxyInstance(
usi.getClass().getClassLoader() 、
usi.getClass() because Object[] args)
“ +System.currentTimeMillis()); return obj;
u.update(2);
u.save( );
}
}
method.invoke(target,args); の最初のパラメータはターゲット オブジェクトであるため、invocationHandler の Invoke メソッドは次のようにする必要があります。 オブジェクト プロキシ パラメータは何に使用されますか? 下を見てみよう!
(私の意見では)最も重要なメソッド呼び出しについて、JDK が何を言うかを見てみましょう:
startTime : 1352879531334 update a user 2 endTime : 1352879531334 startTime : 1352879531334 user save.... endTime : 1352879531335
proxy - メソッドが呼び出されるプロキシ インスタンス? この文はどういう意味ですか? 演技?メソッドはプロキシメソッドですか? それでは、プロキシを実行するメソッドは Object obj = method.invoke(proxy, args); であるべきではないでしょうか? そのときはディスカッション グループに参加せず、Google にアクセスしましたが、何もインスピレーションが得られませんでした。ソース コードを見てみようかと思いました。
Proxy クラスのソース コードを開いて、どのような構築メソッドがあるかを確認します。
invoke Object invoke(Object proxy, Method method, Object[] args) throws Throwable在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 参数: proxy - 在其上调用方法的代理实例 method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。 args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
InvocationHandler を Proxy 構築メソッドのパラメータとして使用します。それでは、InvocationHandler は何に使用されるのでしょうか? InvocationHandler の invoke() メソッドとの関係はありますか?
最初に考えたのは、プロキシが内部で次のステートメントを呼び出すことです:
protected InvocationHandler h; protected Proxy(InvocationHandler h) { this.h = h; }
まずこれを見てみましょう
在这里你就会发现貌似有点感觉了:当u.update(2)时 àProxy就会调用 handler.invoke(proxyClass,update,2) à 也就是调用了proxyClass.update(2);
当u.save();时àProxy就会调用handler.invoke(proxyClass,save,null) à也就是调用了proxyClass.save();
当Test.java改成这样时:
public class Test { public static void main(String[] args) { final UserServiceImpl usi = new UserServiceImpl(); UserService u = (UserService) Proxy.newProxyInstance( usi.getClass().getClassLoader(), usi.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } }); u.update(2); u.save(); } }
注意这时候的匿名类的方法的返回的是null,运行一下就会发现:
Exception in thread "main" java.lang.NullPointerException at $Proxy0.save(Unknown Source) at com.yixi.proxy.Test.main(Test.java:17)
17行有空指针 也就是这里的u.save()方法有为null的元素 难道是u是空的? 不应该啊如果u是null的话那么u.update(2)在那里就会报空指针异常了,当我把17行注释掉以后异常没了说明u.update()能正常执行。那这到底是为什么呢?
其实这就是invoke方法返回null的缘故:
注意一下UserService类中的两个方法:
public interface UserService { public int save() ; public void update(int id); }
所以在invoke方法返回null的时候代理的update方法接收到返回值是null, 而它本来就是返回void 所以没有报异常, 而代理save必须返回int型的数值 我们这返回的还是null,JVM无法将null转化为int型 所以就报了异常了
这样解释就能解释通了,也能相对证明前面的猜测。
InvocationHandler中invoke方法中第一个参数proxy貌似只是为了让Proxy类能给自己的InvocationHandler对象的引用调用方法时能传入代理对象proxyClass的引用,来完成proxyClass需要完成的业务。
更多プロキシモードのJava動的プロキシ実装方法相关文章请关注PHP中文网!

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

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

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

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

ホットトピック











一部のアプリケーションが適切に機能しないようにする会社のセキュリティソフトウェアのトラブルシューティングとソリューション。多くの企業は、内部ネットワークセキュリティを確保するためにセキュリティソフトウェアを展開します。 ...

多くのアプリケーションシナリオでソートを実装するために名前を数値に変換するソリューションでは、ユーザーはグループ、特に1つでソートする必要がある場合があります...

システムドッキングでのフィールドマッピング処理は、システムドッキングを実行する際に難しい問題に遭遇することがよくあります。システムのインターフェイスフィールドを効果的にマッピングする方法A ...

intellijideaultimatiateバージョンを使用してスプリングを開始します...

データベース操作にMyBatis-Plusまたはその他のORMフレームワークを使用する場合、エンティティクラスの属性名に基づいてクエリ条件を構築する必要があることがよくあります。あなたが毎回手動で...

Javaオブジェクトと配列の変換:リスクの詳細な議論と鋳造タイプ変換の正しい方法多くのJava初心者は、オブジェクトのアレイへの変換に遭遇します...

Redisキャッシュソリューションは、製品ランキングリストの要件をどのように実現しますか?開発プロセス中に、多くの場合、ランキングの要件に対処する必要があります。

eコマースプラットフォーム上のSKUおよびSPUテーブルの設計の詳細な説明この記事では、eコマースプラットフォームでのSKUとSPUのデータベース設計の問題、特にユーザー定義の販売を扱う方法について説明します。
