從靜態代理到動態代理的演變

          Posted on 2010-08-16 10:55 java小爬蟲 閱讀(1633) 評論(0)  編輯  收藏
          如上文:代理模式和裝飾者模式中的靜態代理實例,它具有如下缺陷:
              1:代理類不可重用,具有相同代理邏輯的類會大量產生;
              2:被代理方法惟一,如果有多個方法都需要相同邏輯的代理,那么代理類中就有大量的相似的方法存在;
              3:代理方法不具有參數;
              4:只實現了單接口了代理;
          所以它并不具有實戰意義上的價值!

          那么,這些問題該如何解決呢?動態代理又是一步一步如何演變過來的呢?

          下面就讓我們來一步一步以實例的方式來探究它的演變的細節。

          如果我們能動態產生一個代理類的源文件,編譯后加載到內存,那么我們就可以獲取到動態的代理對象。
          package proxy;

          import java.io.File;
          import java.io.FileWriter;
          import java.lang.reflect.Constructor;
          import java.net.URL;
          import java.net.URLClassLoader;

          import javax.tools.JavaCompiler;
          import javax.tools.StandardJavaFileManager;
          import javax.tools.ToolProvider;
          import javax.tools.JavaCompiler.CompilationTask;

          public class Proxy {
              
          public static Object newProxyInstance(Object target) throws Exception { //JDK6 Complier API, CGLib, ASM

                  String rt 
          = "\r\n";
                  String t 
          = "\t";
                      
                  String src 
          ="package proxy;"+ rt + t +    
                  
          "public class TankTimeProxy implements Movable {"+ rt + t +    
                  
          "private Movable obj;    "+ rt + t +    
                  
          "public TankTimeProxy(Movable obj) {"+ rt + t +    
                  
          "super();"+ rt + t +    
                  
          "this.obj = obj;"+ rt + t +    
                  
          "}"+ rt + t +    
                  
          "@Override"+ rt + t +    
                  
          "public void move() {"+ rt + t +    
                  
          "long begintime = System.currentTimeMillis();"+ rt + t +    
                  
          "System.out.println(\" Tank is begining to move !\");"+ rt + t +    
                  
          "obj.move();"+ rt + t +    
                  
          "long endtime = System.currentTimeMillis();"+ rt + t +    
                  
          "System.out.println(\" Tank is stop !\");"+ rt + t +    
                  
          "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +    
                  
          "}"+ rt + t +    
                  
          "}";  
                  String fileName 
          =System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.java";
                  File f 
          = new File(fileName);
                  FileWriter fw 
          = new FileWriter(f);
                  
          if(f.exists()){
                      f.delete();
                      fw.flush();
                      f 
          = new File(fileName);
                      };
                  fw.write(src);
                  fw.flush();
                  fw.close();
                  
                  
          //compile
                  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                  StandardJavaFileManager fileMgr 
          = compiler.getStandardFileManager(nullnullnull);
                  Iterable units 
          = fileMgr.getJavaFileObjects(fileName);
                  CompilationTask compilationTask 
          = compiler.getTask(null, fileMgr, nullnullnull, units);
                  compilationTask.call();
                  fileMgr.close();
                  
                  
          //load into memory and create an instance
                  URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
                  URLClassLoader ul 
          = new URLClassLoader(urls);
                  Class c 
          = ul.loadClass("proxy.TankTimeProxy");    
                          
                  Constructor ctr 
          = c.getConstructor(target.getClass().getInterfaces()[0]);
                  Object m 
          = ctr.newInstance(target);
                  
          return m;
              }
          }

          測試類:
          package proxy;
          public class Client {
              
          public static void main(String[] args) throws Exception{
                  Movable tank 
          = (Movable)Proxy.newProxyInstance(new Tank());
                  tank.move();
              }
          }




          上面的類就實現了生成TankTimeProxy.java文件,編譯,加載并被調用的功能。(全部源碼見:代理模式和裝飾者模式異同點比較

          那么怎么實現代理任意對象呢?
          在產生代理類的時候,只要動態的注入目標對象,就實現了對任意對象的代理。
          怎么實現對任意方法的代理呢?通過java反射機制,可以獲取一個類的所有方法,即可以獲取目標類的所有方法,在組成代理類java源碼的時候,循環遍歷嵌入處理邏輯就可以任意對多方法的代理了。

          示例代碼如下:

          package proxy;

          import java.io.File;
          import java.io.FileWriter;
          import java.lang.reflect.Constructor;
          import java.lang.reflect.Method;
          import java.net.URL;
          import java.net.URLClassLoader;

          import javax.tools.JavaCompiler;
          import javax.tools.StandardJavaFileManager;
          import javax.tools.ToolProvider;
          import javax.tools.JavaCompiler.CompilationTask;

          public class Proxy {
              
          public static Object newProxyInstance(Class intface,Object target) throws Exception //JDK6 Complier API, CGLib, ASM

                  String rt 
          = "\r\n";
                  String t 
          = "\t";
                  String methodStr 
          = "" ;
                          
                  Method[] methods 
          = intface.getDeclaredMethods();
                  
          for (Method method : methods) {
                      methodStr 
          += "@Override"+ rt + t +    
                      
          "public void "+method.getName()+"() {"+ rt + t +    
                      
          "long begintime = System.currentTimeMillis();"+ rt + t +    
                      
          "System.out.println(\" Tank is begining to move !\");"+ rt + t +    
                      
          "obj."+method.getName()+"();"+ rt + t +    
                      
          "long endtime = System.currentTimeMillis();"+ rt + t +    
                      
          "System.out.println(\" Tank is stop !\");"+ rt + t +    
                      
          "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +    
                      
          "}"+ rt + t; 
                  }

                  
                  System.out.println(methodStr);
                  
                  String src 
          ="package proxy;"+ rt + t +  
                  
          "import "+intface.getName()+";"+ rt + t + 
                  
          "public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +    
                  
          "private "+intface.getSimpleName()+" obj;    "+ rt + t +    
                  
          "public $Proxy1("+intface.getSimpleName()+" obj) {"+ rt + t +    
                  
          "super();"+ rt + t +    
                  
          "this.obj = obj;"+ rt + t +    
                  
          "}" + rt + t +    
                  methodStr 
          +rt +
                  
          "}";
                   
                  String fileName 
          =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
                  File f 
          = new File(fileName);
                  FileWriter fw 
          = new FileWriter(f);
                  
          if(f.exists()){
                      f.delete();
                      fw.flush();
                      f 
          = new File(fileName);
                      }
          ;
                  fw.write(src);
                  fw.flush();
                  fw.close();
                  
                  
          //compile
                  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                  StandardJavaFileManager fileMgr 
          = compiler.getStandardFileManager(nullnullnull);
                  Iterable units 
          = fileMgr.getJavaFileObjects(fileName);
                  CompilationTask compilationTask 
          = compiler.getTask(null, fileMgr, nullnullnull, units);
                  compilationTask.call();
                  fileMgr.close();
                  
                  
          //load into memory and create an instance
                  URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
                  URLClassLoader ul 
          = new URLClassLoader(urls);
                  Class c 
          = ul.loadClass("proxy.$Proxy1");    
                                          
                  Constructor ctr 
          = c.getConstructor(intface);
                  Object m 
          = ctr.newInstance(target);
                  
          return m;
              }

          }


          但是新的問題又出現了:
          在代理類中,被代理方法前后的處理邏輯已經被“寫死了”,很難改變增加的功能,這又該如何處理呢?
          我們可以這樣考慮,增加一個調用處理器InvocationHandler,把對方法的處理邏輯進行進一步的封閉,并把InvocationHandler分離出來,如果可以的話,就實現了對代理邏輯的可修改性。
          那么InvocationHandler里面應該封閉些什么東西呢?

          for (Method method : methods) {
                      methodStr 
          += "@Override"+ rt + t +    
                      
          "public void "+method.getName()+"() {"+ rt + t +    
                      
          "long begintime = System.currentTimeMillis();"+ rt + t +    
                      
          "System.out.println(\" Tank is begining to move !\");"+ rt + t +    
                      
          "obj."+method.getName()+"();"+ rt + t +    
                      
          "long endtime = System.currentTimeMillis();"+ rt + t +    
                      
          "System.out.println(\" Tank is stop !\");"+ rt + t +    
                      
          "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +    
                      
          "}"+ rt + t; 
                  }
          從這個片段代理可以看出,我們應該在InvocationHandler中封裝obj對象,即被代理類的接口(實現類)。還應該實現:被分離出去的InvocationHandler能被代理類調用,我們應該把InvocationHandler聚合進來。

          代碼演變示例:
          package proxy;

          import java.lang.reflect.Method;

          public interface InvocationHandler {
              
          public void invoke(Object proxy, Method m);
          }


          package proxy;

          import java.lang.reflect.Method;

          public class TimeHandler implements InvocationHandler{
              
              
          private Object target;

              
          public TimeHandler(Object target) {
                  
          this.target = target;
              }


              @Override
              
          public void invoke(Object o, Method m) {
                  
          long start = System.currentTimeMillis();
                  System.out.println(
          "starttime:" + start);
                  System.out.println(o.getClass().getName());
                  
          try {
                      m.invoke(target);
                  }
           catch (Exception e) {
                      e.printStackTrace();
                  }

                  
          long end = System.currentTimeMillis();
                  System.out.println(
          "time:" + (end-start));
                  System.out.println(
          "endtime:" + end);
              }


          }


          package proxy;

          import java.io.File;
          import java.io.FileWriter;
          import java.lang.reflect.Constructor;

          import java.lang.reflect.Method;
          import java.net.URL;
          import java.net.URLClassLoader;

          import javax.tools.JavaCompiler;
          import javax.tools.StandardJavaFileManager;
          import javax.tools.ToolProvider;
          import javax.tools.JavaCompiler.CompilationTask;

          public class Proxy {
              
          public static Object newProxyInstance(Class intface,InvocationHandler h) throws Exception //JDK6 Complier API, CGLib, ASM

                  String rt 
          = "\r\n";
                  String t 
          = "\t";
                  String methodStr 
          = "" ;
                          
                  Method[] methods 
          = intface.getDeclaredMethods();
                  
          for(Method method : methods) {
                      methodStr 
          += "@Override" + rt + t + t + 
                                   
          "public void " + method.getName() + "() {" + rt + t + t + t + 
                                   
          "try {" + rt + t + t + t + t +  
                                   
          "Method md = " + intface.getName() + ".class.getMethod(\"" + method.getName() + "\");" + rt + t + t + t + t +   
                                   
          "h.invoke(this, md);" + rt + t + t + t +
                                   
          "}catch(Exception e) {" + rt + t + t + t +      
                                   
          "e.printStackTrace();" + rt + t + t +     
                                   
          "}" + rt + t +                     
                                   
          "}" + rt + t + t ;
                  }

                  
                  System.out.println(methodStr);
                  
                  String src 
          ="package proxy;"+ rt + t +  
                  
          "import "+intface.getName()+";"+ rt + t + 
                  
          "import java.lang.reflect.Method;"+ rt + t + 
                  
          "public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +    
                  
          "private InvocationHandler h ;    "+ rt + t +    
                  
          "public $Proxy1(InvocationHandler h) {"+ rt + t +    
                  
          "super();"+ rt + t +    
                  
          "this.h = h;"+ rt + t +    
                  
          "}" + rt + t +    
                  methodStr 
          +rt +
                  
          "}";
                   
                  String fileName 
          =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
                  File f 
          = new File(fileName);
                  FileWriter fw 
          = new FileWriter(f);
                  
          if(f.exists()){
                      f.delete();
                      fw.flush();
                      f 
          = new File(fileName);
                      }
          ;
                  fw.write(src);
                  fw.flush();
                  fw.close();
                  
                  
          //compile
                  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                  StandardJavaFileManager fileMgr 
          = compiler.getStandardFileManager(nullnullnull);
                  Iterable units 
          = fileMgr.getJavaFileObjects(fileName);
                  CompilationTask compilationTask 
          = compiler.getTask(null, fileMgr, nullnullnull, units);
                  compilationTask.call();
                  fileMgr.close();
                  
                  
          //load into memory and create an instance
                  URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
                  URLClassLoader ul 
          = new URLClassLoader(urls);
                  Class c 
          = ul.loadClass("proxy.$Proxy1");    
                                          
                  Constructor ctr 
          = c.getConstructor(InvocationHandler.class);
                  Object m 
          = ctr.newInstance(h);
                  
          return m;
              }

          }


          這樣,就實現了對任意對象,任意方法的代理。
          缺點就是:多接口代理沒有實現,被代理對象的方法沒有支持參數。

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 阜阳市| 成武县| 镶黄旗| 渑池县| 昭平县| 静海县| 定兴县| 琼结县| 台南县| 绿春县| 惠水县| 德兴市| 洮南市| 安顺市| 黑河市| 门源| 凌云县| 锡林郭勒盟| 司法| 阳朔县| 宜君县| 鄂托克前旗| 滦平县| 南平市| 莎车县| 休宁县| 常山县| 县级市| 上饶县| 古田县| 卫辉市| 南阳市| 铜鼓县| 内乡县| 伊春市| 宜兰市| 北海市| 开封市| 固安县| 江永县| 友谊县|