動的プロキシを使用するユースケースとは何ですか?
バイトコードの生成とリフレクションにどのように関連していますか?
推奨読書はありますか?
これを強くお勧めします resource 。
まず、プロキシパターンのユースケースを理解する必要があります。プロキシの主な目的は、ターゲットオブジェクトの機能を強化することではなく、ターゲットオブジェクトへのアクセスを制御することです。アクセス制御には、同期、認証、リモートアクセス(RPC)、遅延インスタンス化(Hibernate、Mybatis)、AOP(トランザクション)が含まれます。
静的プロキシとは対照的に、動的プロキシは、実行時にJavaリフレクションを必要とするバイトコードを生成します。動的アプローチでは、プロキシクラスを作成する必要がないため、利便性が向上します。
動的プロキシクラスは、クラスのインスタンスのインターフェイスの1つを介したメソッド呼び出しが実行されるように、実行時に指定されたインターフェイスのリストを実装するクラスですエンコードされ、統一されたインターフェイスを介して別のオブジェクトにディスパッチされます。プロキシクラスの事前生成を必要とせずに、インターフェイスのリストのタイプセーフプロキシオブジェクトを作成するために使用できます。動的プロキシクラスは、インターフェイスAPIを提供するオブジェクトの呼び出しのタイプセーフリフレクションディスパッチを提供する必要があるアプリケーションまたはライブラリに役立ちます。
動的プロキシの興味深い使用法を思いつきました。
別の依存サービスと結合されている重要ではないサービスに問題があり、その依存サービスが利用できなくなったときにフォールトトレラントになる方法を模索したいと考えていました。
そこで、私はLoadSheddingProxyを作成しました。これは2つのデリゲートを取ります-1つは「JNDIルックアップ後の」「通常の」サービスのリモート実装です。もう1つのオブジェクトは、「ダミー」の負荷制限実装です。タイムアウトをキャッチし、再試行する前に一定の時間ダミーに迂回する各メソッド呼び出しを囲む単純なロジックがあります。使用方法は次のとおりです。
// This is part of your ServiceLocator class
public static MyServiceInterface getMyService() throws Exception
{
MyServiceInterface loadShedder = new MyServiceInterface() {
public Thingy[] getThingys(Stuff[] whatever) throws Exception {
return new Thingy[0];
}
//... etc - basically a dummy version of your service goes here
}
Context ctx = JndiUtil.getJNDIContext(MY_CLUSTER);
try {
MyServiceInterface impl = ((MyServiceHome) PortableRemoteObject.narrow(
ctx.lookup(MyServiceHome.JNDI_NAME),
MyServiceHome.class)).create();
// Here's where the proxy comes in
return (MyService) Proxy.newProxyInstance(
MyServiceHome.class.getClassLoader(),
new Class[] { MyServiceInterface.class },
new LoadSheddingProxy(MyServiceHome.JNDI_NAME, impl, loadShedder, 60000)); // 10 minute retry
} catch (RemoteException e) { // If we can't even look up the service we can fail by shedding load too
logger.warn("Shedding load");
return loadShedder;
} finally {
if (ctx != null) {
ctx.close();
}
}
}
プロキシは次のとおりです。
public class LoadSheddingProxy implements InvocationHandler {
static final Logger logger = ApplicationLogger.getLogger(LoadSheddingProxy.class);
Object primaryImpl, loadDumpingImpl;
long retry;
String serviceName;
// map is static because we may have many instances of a proxy around repeatedly looked-up remote objects
static final Map<String, Long> servicesLastTimedOut = new HashMap<String, Long>();
public LoadSheddingProxy(String serviceName, Object primaryImpl, Object loadDumpingImpl, long retry)
{
this.serviceName = serviceName;
this.primaryImpl = primaryImpl;
this.loadDumpingImpl = loadDumpingImpl;
this.retry = retry;
}
public Object invoke(Object obj, Method m, Object[] args) throws Throwable
{
try
{
if (!servicesLastTimedOut.containsKey(serviceName) || timeToRetry()) {
Object ret = m.invoke(primaryImpl, args);
servicesLastTimedOut.remove(serviceName);
return ret;
}
return m.invoke(loadDumpingImpl, args);
}
catch (InvocationTargetException e)
{
Throwable targetException = e.getTargetException();
// DETECT TIMEOUT HERE SOMEHOW - not sure this is the way to do it???
if (targetException instanceof RemoteException) {
servicesLastTimedOut.put(serviceName, Long.valueOf(System.currentTimeMillis()));
}
throw targetException;
}
}
private boolean timeToRetry() {
long lastFailedAt = servicesLastTimedOut.get(serviceName).longValue();
return (System.currentTimeMillis() - lastFailedAt) > retry;
}
}
クラス - Java.lang.reflect.Proxy
では、 InvocationHandler
でメソッド呼び出しを処理することにより、インターフェイスを動的に実装できます。 Javaのリフレクション機能の一部と見なされますが、バイトコード生成とは関係ありません。
Sunには、プロキシクラスの使用について チュートリアル があります。 Googleも役立ちます。
使用例の1つは休止状態です。モデルクラスインターフェイスを実装するオブジェクトを提供しますが、ゲッターとセッターの下にはdb関連のコードがあります。つまり単純なPOJOのように使用しますが、実際には多くのことが隠れています。
たとえば、遅延ロードされたプロパティのgetterを呼び出すだけですが、実際にはプロパティ(おそらく大きなオブジェクト構造全体)がデータベースから取得されます。
詳細については、 cglib libraryを確認してください。