ダークホースプログラマー_張孝祥_Javaインフラストラクチャの強化_エージェント


----------- androidトレーニング、javaトレーニング、あなたとの交流を期待しています! ------------
エージェントとは
 
類関係から言えば、エージェントは組み合わせと継承の中庸の道である.
 
コンビネーションは、別のクラスのオブジェクトをメンバー属性としてこのクラスに格納し、このメンバー属性オブジェクトを呼び出す方法でこのような機能を得ることです.
継承は、1つのクラスを拡張し、そのすべての機能インタフェースをコピーして、このクラスをis aの別のクラスに見せることです.
エージェントの本質は,エージェントのクラスを呼び出す機能によって実現される,部分的に継承される効果,すなわち,コピーインタフェース機能を組み合わせて達成することである.
実際,エージェントと継承の内部実装方式は非常に似ている.
 
エージェントクラスと被エージェントクラスには共通のインタフェースがあり、エージェントクラスのインタフェースメソッドは被エージェントクラスのインタフェースメソッドを呼び出し、被エージェントクラスと同じ機能を達成できるほか、メソッドの前後に他のシステムコードを追加したり、伝達されたパラメータを変更したり、戻り値を変更したりすることができます.
 
エージェントのメリット:
1.エージェントクラスのメソッドに追加の機能コードを追加します.
クラスに異なるエージェントを設定することで、このようなメソッドに異なる包囲コードを追加することで、わずかに異なる効果を得ることができます.
 
2.同じエージェントクラスエージェントに異なる実装クラスを与え、実装方式を動的に変更する.
エージェントクラスには異なるエージェントターゲットを指定できますが、エージェントターゲットの実装方法は異なります.Clientプログラムはエージェントクラスのメソッドを呼び出し、エージェントターゲットを変更することによって、この呼び出されたメソッドに異なる実装方法や動作を切り替えることができます.
  
異なるオブジェクトのエージェントクラスは大きく異なります.
1.エージェント対象クラス(Target)を含む
2.エージェントクラスの所有者と交差する機能
3.交差機能の前後、包囲方式、catchブロックに追加コードを追加することができます.
 
エージェントを必要とするクラスは多く、エージェントはこの固定されたモードに従うため、フレームワークを設定することでエージェントを生成する操作を簡素化することができます.
 
動的エージェント:
 
実際、JVMは、実行中に動的エージェントクラスを生成する方法を提供しています.
使用する2つのクラス:
 java.lang.reflect.Proxy
java.lang.InvocationHandler
 
//  Proxy          Class  ,     $Proxy0-N
Class proxyCls =Proxy.getProxyClass(AInterface.class.getClassLoader(), AInterface.class);
//                ,        InvocationHandler  ,   invoke  。
// invoke           Method         ,                 。
//      ,            ,     Target,    Advice。
class MyInvocationHandler implementsInvocationHandler{
         privateAInterface target = ...;
         privateAdvice advice = ...;
         publicObject invoke(Object proxy, Method method, Object[] args)throws Throwable{
                   advice.beforeMethod(method,args);
                   ObjectreturnVal = method.invoke(target, args);
                   advice.afterMethod(method,args);
                   if(returnVal== null)
                            returnnull;
                   returnreturnVal;
         }
}
//          ,      
AInterface proxy =(AInterface)proxyCls.getConstructor(InvocationHandler.class).newInstance(newMyInvocationHandler());

  
プロキシオブジェクトの作成を簡単にする方法は、次のとおりです.
AInterface proxy = Proxy.newProxyInstance(
         AInterface.class.getClassLoader(),
         newClass[]{AInterface.class},
         newInvocationHandler(){
                   privateAInterface target = ...;
                   privateAdvice advice = ...;
                   publicObject invoke(Object proxy, Method method, Object[] args) throws Throwable{
                            advice.beforeMethod(method,args);
                            ObjectreturnVal = method.invoke(target, args);
                            advice.afterMethod(method,args);
                            if(returnVal== null)
                                     returnnull;
                            returnreturnVal;
                   }
         }
);
interface Advice{
         voidbeforeMethod(Method method, Object[] args);
         voidafterMethod(Method method, Object[] args);
}

 
動的エージェントクラスの実装原理:
 
一般的なエージェント関係は、被エージェントターゲットを組合せてメンバー属性としてエージェントクラスに格納し、メソッドを呼び出すときにそのメンバーを呼び出す対応するメソッドです.
一方、JVMによって自動的に生成される動的エージェントクラスは、1つの呼び出しハンドラであるInvocationHandlerを動的エージェントクラスに組み入れ、エージェントターゲットをInvocationHandlerに組み入れ、メソッドの呼び出し時にInvocationHandlerのinvokeメソッドを呼び出し、その後、invokeメソッドによって被代理クラスの対応メソッドを呼び出し、invokeメソッドによって呼び出しプロセスを処理することもできる.AdviceオブジェクトもエージェントクラスではなくInvocationHandlerオブジェクトに組み込まれています.
 
実装コードは、次のようになります.
class MyProxy implementsCollection{
         InvocationHandlerhandler;
         MyProxy(InvocationHandlerhandler){
                   this.handler= handler;
         }
         publicboolean add(E e){
                   return(boolean)handler.invoke(this, this.getClass().getMethod("add",T.getClass()), T.getClass());
         }
         ...
}

 
注意:
Objectクラスから継承されるメソッドではhashCode,equals,toStringのみが被エージェントクラスに配布され,他のObjectメソッドではエージェントクラス自体のメソッドが使用される.
したがってproxy.getClass().getName()は、プロキシクラスの名前ではなくプロキシクラスの名前を返します.
 
特に注意:
実際,InvocationHandlerにおけるinvokeメソッドの戻り値はObjectであるが,メソッド呼び出しの実際の戻り値タイプが正しいことが実験的に分かったので,エージェントクラスがメソッドの戻り値を返す際に複雑な変換過程を行い,これは反射によって実現される可能性があることを示した.(と思う)
  
AOP(Aspect oriented program)は、側面向けにプログラミングされています.
システムにはクロストラフィックが存在し、1つのクロストラフィックはシステムの1つの側面に切り込むことである.
 
クロストラフィックのプログラミング問題は側面向けのプログラミングであり,AOPの目標はクロストラフィックをモジュール化することであり,スライスコードを元のメソッドの周囲に移動することができ,これはメソッドに直接スライスコードを記述する効果と同様である.
  
様々なインタフェースのクラスにエージェントクラスを記述することは非常に退屈で面倒なことであるため,JVMはこのような機能を実現してくれた.
JVMは、実行中にクラスのバイトコードを動的に生成することができ、このような動的に生成されたクラスは、エージェントクラス、すなわち動的エージェントクラスとして使用されることが多い.
JVMによって生成される動的クラスは、1つ以上のインタフェースを実装する必要があります.したがって、JVMによって生成される動的クラスは、同じインタフェースを持つターゲットクラスのエージェントとしてのみ使用できます.
 
クラスに実装インタフェースがない場合、JVMは動的プロキシクラスを生成できません.これはサードパーティ製クラスライブラリCGLIBを使用することができ、クラスのサブクラスを動的に生成することができ、サブクラスは自然に親と同じインタフェースを持っています.
  
springのような構成可能なAOPフレームワークを実現する:
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
 
class AopFrameworkTest
{
         publicstatic void main(String[] args) throws Exception
         {
                   InputStreamin = AopFrameworkTest.class.getResourceAsStream("config.properties");
                   Speakablebean = (Speakable)new BeanFactory(in).getBean("xxx");
                   System.out.println(bean.speak());
         }
}
 
 
class ProxyFactoryBean
{
         privateObject target;
         privateAdvice advice;
         publicvoid setTarget(Object tar){
                   target= tar;
         }
         publicvoid setAdvice(Advice adv){
                   advice= adv;
         }
 
         publicObject getProxy()throws Exception{
                   returnProxy.newProxyInstance(
                            target.getClass().getClassLoader(),
                            target.getClass().getInterfaces(),
                            newInvocationHandler(){
                                     publicObject invoke(Object proxy, Method method, Object[] args)throws Throwable{
                                               advice.beforeMethod(method,args);
                                               ObjectreturnVal = method.invoke(target, args);
                                               advice.afterMethod(method,args);
                                               if(returnVal== null)
                                                        returnnull;
                                               returnreturnVal;
                                     }
                            });
         }
}
 
class BeanFactory
{
         privateProperties prop = new Properties();
         publicBeanFactory(InputStream in){
                   try
                   {
                            prop.load(in);
                   }
                   catch(IOException e)
                   {
                            thrownew RuntimeException("        ");
                  }
         }
         publicObject getBean(String name)throws Exception{
                   StringclsName = prop.getProperty(name);
                   Objectobj = Class.forName(clsName).newInstance();
                   if(clsName.equals("ProxyFactoryBean")){
                            ProxyFactoryBeanproxyFactory = (ProxyFactoryBean)obj;
                            proxyFactory.setTarget(Class.forName(prop.getProperty("target")).newInstance());
                            proxyFactory.setAdvice((Advice)Class.forName(prop.getProperty("advice")).newInstance());
                            returnproxyFactory.getProxy();
                   }
                   returnobj;
         }
}

----------- androidトレーニング、javaトレーニング、あなたとの交流を期待しています! ------------