jdkダイナミックエージェントでの問題-proxyを呼び出すtoStringメソッドによるスタックオーバーフロー
4921 ワード
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
UserManager target = new UserManagerImpl();
UserManager proxy = (UserManager) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new UserManagerProxy(target));
proxy.addUser();
}
}
interface UserManager {
public void addUser();
}
class UserManagerImpl implements UserManager {
@Override
public void addUser() {
System.out.println("add user...");
}
}
class UserManagerProxy implements InvocationHandler {
private UserManager target;
public UserManagerProxy(UserManager target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("check privilege " + proxy);
method.invoke(target, args);
return null;
}
}
エージェント処理クラスを実行するSystem.out.println("check privilege "+ proxy);するとjavaが現れました.lang.StackOverflowErrorエラー.原因はproxyのtoString法に初歩的に位置づけることができる.
しかし、デバッグ後、proxyは$Proxy 0クラスに属していることがわかりました.$Proxy 0というClassは実行時に生成されたクラスで、ネット上に牛人が$Proxy 0のソースコードを貼っています.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements UserManager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
.booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void addUser() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
toStringメソッドを呼び出す場合、hのinvokeメソッドが呼び出され、hがInvocationHandlerの例であるため、再帰的な呼び出しであるため、上述javaが現れる.lang.StackOverflowErrorエラー.
ちなみにjavaダイナミックエージェントを簡単に作成する手順は、次のとおりです.
1.インタフェースIを定義する
2.このインタフェースIの実装クラスImplを記述する
3.InvocationHandlerインタフェースの実装クラスHを記述し,Hクラスオブジェクトを構築する際にエージェントするオブジェクトtargetを取り込み,targetが実際の動作を完了する.中のinvokeメソッドでは、自分が実現したい論理を作成し、実際に完了するアクションを呼び出すことができます.
4.Proxyを呼び出す.新ProxyInstanceメソッドでは,伝達される3つのパラメータはそれぞれエージェントクラスのクラスローダ(ImplインスタンスのgetClass()である.getClassLoader())
、エージェントクラスが実装インタフェースのリスト(ImplインスタンスgetClass()を用いることができる.getInterfaces()、InvocationHandler実装クラスの例.
これにより$Proxy 0クラスのオブジェクトが生成され,$Proxy 0クラスはIインタフェースを実現するため,オブジェクトを強制的にIに変換することができる.
もう一度言って新ProxyInstanceメソッドの実際の手順:
1.入力されたInvocationHandlerインスタンスパラメータを使用してProxyクラスのhインスタンスを初期化します.空のオブジェクトを入力すると、空のポインタエラーが放出されます.つまり、hは空にできません.
2.実行時にエージェントClassを生成する$Proxy 0
3.上で動的に生成された$Proxy 0クラスを利用して、そのクラスのオブジェクトを構築し、返します.