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クラスを利用して、そのクラスのオブジェクトを構築し、返します.