Java学習の:JDKダイナミックエージェントとCGLIBダイナミックエージェント


エージェントの概念:簡単な理解は、あるオブジェクトにエージェントオブジェクトを作成することによって、元のオブジェクトを直接参照するのではなく、作成したエージェントオブジェクトによって元のオブジェクトへの参照を制御することです.
動的エージェント:プログラムの実行時にJava反射メカニズムによって動的に生成され、手動でコードを記述する必要がありません.動的エージェントはプログラミング作業を簡素化するだけでなく、Java反射メカニズムが任意のタイプの動的エージェントクラスを生成できるため、ソフトウェアシステムの拡張性を向上させる.
≪エージェントの原理|Agent原理|emdw≫:エージェント・オブジェクトの内部には、リアル・オブジェクトを操作できるように、リアル・オブジェクトへの参照が含まれています.また、エージェント・オブジェクトは、リアル・オブジェクトと同じインタフェースを提供し、リアル・オブジェクトの代わりにいつでも使用できます.また、プロキシオブジェクトは、実際のオブジェクト操作を実行するときに、実際のオブジェクトをカプセル化することに相当する他の操作を追加することができます.
以下では、JDK動的エージェントとCGLIBに基づいてエージェントがそれぞれどのように実装されるかを簡単な例で示す.
プロキシインタフェースUserServices:
1 package com.liang.test;

2 

3 public interface UserService {

4     public void say(String arg);

5 }

 
UserService実装クラスUserServiceImpl:
1 package com.liang.test;

2 

3 public class UserServiceImpl implements UserService {

4     @Override

5     public void say(String arg) {

6         System.out.println("hello, I am " + arg);

7     }

8 }

 
JDKはjava.lang.reflectパッケージの下にあるProxyクラスとInvocationHandlerインタフェースを介して動的エージェントクラスと動的エージェントオブジェクトを生成します.
まず、InvocationHandlerインタフェースを実装する呼び出しコントローラオブジェクトMyInvocationHandlerを作成します:(ダイナミックエージェントオブジェクトのターゲットメソッドを実行すると、実際にはMyInvocationHandlerを呼び出すinvokeメソッドに置き換えられます)
 1 package com.liang.test;

 2 

 3 import java.lang.reflect.InvocationHandler;

 4 import java.lang.reflect.Method;

 5 

 6 public class MyInvocationHandler implements InvocationHandler { //  InvocationHandler

 7 

 8     private Object object; //     

 9     

10     public MyInvocationHandler(Object object){ //       

11         this.object = object;

12     }

13     @Override

14     public Object invoke(Object proxy, Method method, Object[] args)

15             throws Throwable { //say()            ,                

16         System.out.println("before method");

17         Object obj = method.invoke(object, args); //      UserService say()  

18         System.out.println("after method");

19         return obj;

20     }

21 

22 }

MyInvocationHandlerとProxyを組み合わせて、UserServiceの動的エージェントクラスを作成します.
 1 package com.liang.test;

 2 

 3 import java.lang.reflect.InvocationHandler;

 4 import java.lang.reflect.Proxy;

 5 

 6 public class TestProxy {

 7 

 8     public static void main(String[] args) {

 9         UserService userService = new UserServiceImpl();

10         InvocationHandler handler = new MyInvocationHandler(userService);

11 

12         UserService proxy = (UserService)Proxy.newProxyInstance(userService.getClass()

13                 .getClassLoader(), userService.getClass().getInterfaces(),

14                 handler);

15         proxy.say("   "); //      

16     }

17 

18 }

実行結果:
before method

hello, I am    

after method

エージェントインスタンスのsayメソッドを実行するときに実行されるのはMyInvocationHandlerのinvoke()メソッドであることがわかる.
 
CGLIBは非常に下位のバイトコード技術を採用し,被エージェントオブジェクトにサブクラスを作成し,サブクラスでメソッドブロックの技術を用いてすべての親メソッドの呼び出しをブロックし,順次増強コードを植え込む.CGLIBが動的エージェントを実装する例は次のとおりです.
 1 package com.liang.test;

 2 

 3 import java.lang.reflect.Method;

 4 

 5 import net.sf.cglib.proxy.Enhancer;

 6 import net.sf.cglib.proxy.MethodInterceptor;

 7 import net.sf.cglib.proxy.MethodProxy;

 8 

 9 public class CglibProxy implements MethodInterceptor {

10     

11     private Enhancer enhancer = new Enhancer();

12 

13     public Object getProxy(Class clazz) {

14         enhancer.setSuperclass(clazz); //           

15         enhancer.setCallback(this);

16         return enhancer.create();//       

17     }

18 

19     @Override

20     public Object intercept(Object object, Method method, Object[] args,

21             MethodProxy proxy) throws Throwable { //            

22         System.out.println("cglib: before method");

23         Object obj = proxy.invokeSuper(object, args); //             

24         System.out.println("cglib: after method");

25         return obj;

26     }

27 

28 }

 
テストコード:
 1 package com.liang.test;

 2 

 3 public class TestProxy {

 4 

 5     public static void main(String[] args) {

 6         CglibProxy proxy = new CglibProxy();

 7         UserServiceImpl userServiceImpl = (UserServiceImpl)proxy.getProxy(UserServiceImpl.class);

 8         userServiceImpl.say("   ");

 9     }

10 

11 }

 
実行結果:
cglib: before method

hello, I am    

cglib: after method

 
 
まとめ:JDK動的エージェントとCGLIB動的エージェントの最も一般的な応用はSpringAOPの最下位実装であり、JDKベースの動的エージェントがエージェントオブジェクトを作成するのにかかる時間はCGLIBより短いが、JDKはインタフェースにエージェントインスタンスを作成するしかなく、newProxyInstance(ClassLoader loader,Class[]interfaces,InvocationHandler h)からでは、2番目のパラメータがエージェントの例で実装されたインタフェースのリストであることがわかります.一方、CGLIBによって作成されるプロキシオブジェクトの性能はJDKによって作成されるプロキシオブジェクトよりも高いが、比較的時間がかかり、CGLIBはサブクラスを動的に作成することによってプロキシオブジェクトを生成するため、finalによって修飾される方法ではCGLIBによってプロキシを行うことができない.これに基づいて,singgletonのエージェントオブジェクトやインスタンスプールを持つエージェントにはCGLIB動的エージェント技術が適しており,毎回newで新しいインスタンスを出す一般的なJDKの動的エージェント技術が用いられる.
 今回の学習資料は以下を参照した.
1.「Spring 2.x-企業応用開発の詳細に精通している」--陳雄華著;
  2.http://rejoy.iteye.com/blog/1627405——JDK動的代理実現原理