Posted on 2010-08-16 10:55 
java小爬虫 阅读(1646) 
评论(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(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
        CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, 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;
package proxy;

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

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


 public class Proxy
public class Proxy  {
{

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

 String rt = "\r\n";
        String rt = "\r\n";
 String t = "\t";
        String t = "\t";
 String methodStr = "" ;
        String methodStr = "" ;
 
                
 Method[] methods = intface.getDeclaredMethods();
        Method[] methods = intface.getDeclaredMethods();

 for (Method method : methods)
        for (Method method : methods)  {
{
 methodStr += "@Override"+ rt + t +
            methodStr += "@Override"+ rt + t +    
 "public void "+method.getName()+"() {"+ rt + t +
            "public void "+method.getName()+"() {"+ rt + t +    
 "long begintime = System.currentTimeMillis();"+ rt + t +
            "long begintime = System.currentTimeMillis();"+ rt + t +    
 "System.out.println(\" Tank is begining to move !\");"+ rt + t +
            "System.out.println(\" Tank is begining to move !\");"+ rt + t +    
 "obj."+method.getName()+"();"+ rt + t +
            "obj."+method.getName()+"();"+ rt + t +    
 "long endtime = System.currentTimeMillis();"+ rt + t +
            "long endtime = System.currentTimeMillis();"+ rt + t +    
 "System.out.println(\" Tank is stop !\");"+ rt + t +
            "System.out.println(\" Tank is stop !\");"+ rt + t +    
 "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +
            "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +    
 "}"+ rt + t;
            "}"+ rt + t; 
 }
        }
 
        
 System.out.println(methodStr);
        System.out.println(methodStr);
 
        
 String src ="package proxy;"+ rt + t +
        String src ="package proxy;"+ rt + t +  
 "import "+intface.getName()+";"+ rt + t +
        "import "+intface.getName()+";"+ rt + t + 
 "public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +
        "public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +    
 "private "+intface.getSimpleName()+" obj;    "+ rt + t +
        "private "+intface.getSimpleName()+" obj;    "+ rt + t +    
 "public $Proxy1("+intface.getSimpleName()+" obj) {"+ rt + t +
        "public $Proxy1("+intface.getSimpleName()+" obj) {"+ rt + t +    
 "super();"+ rt + t +
        "super();"+ rt + t +    
 "this.obj = obj;"+ rt + t +
        "this.obj = obj;"+ rt + t +    
 "}" + rt + t +
        "}" + rt + t +    
 methodStr +rt +
        methodStr +rt +
 "}";
        "}";
 
         
 String fileName =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
        String fileName =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
 File f = new File(fileName);
        File f = new File(fileName);
 FileWriter fw = new FileWriter(f);
        FileWriter fw = new FileWriter(f);

 if(f.exists())
        if(f.exists()) {
{
 f.delete();
            f.delete();
 fw.flush();
            fw.flush();
 f = new File(fileName);
            f = new File(fileName);
 };
            };
 fw.write(src);
        fw.write(src);
 fw.flush();
        fw.flush();
 fw.close();
        fw.close();
 
        
 //compile
        //compile
 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
 Iterable units = fileMgr.getJavaFileObjects(fileName);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
 CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
        CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
 compilationTask.call();
        compilationTask.call();
 fileMgr.close();
        fileMgr.close();
 
        
 //load into memory and create an instance
        //load into memory and create an instance

 URL[] urls = new URL[]
        URL[] urls = new URL[]  {new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
{new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
 URLClassLoader ul = new URLClassLoader(urls);
        URLClassLoader ul = new URLClassLoader(urls);
 Class c = ul.loadClass("proxy.$Proxy1");
        Class c = ul.loadClass("proxy.$Proxy1");    
 
                                
 Constructor ctr = c.getConstructor(intface);
        Constructor ctr = c.getConstructor(intface);
 Object m = ctr.newInstance(target);
        Object m = ctr.newInstance(target);
 return m;
        return m;
 }
    }
 }
}

但是新的问题又出现了:
在代理类中,被代理方法前后的处理逻辑已经被“写死了”,很难改变增加的功能,这又该如何处理呢?
我们可以这样考虑,增加一个调用处理器InvocationHandler,把对方法的处理逻辑进行进一步的封闭,并把InvocationHandler分离出来,如果可以的话,就实现了对代理逻辑的可修改性。
那么InvocationHandler里面应该封闭些什么东西呢?

 for (Method method : methods)
for (Method method : methods)  {
{
 methodStr += "@Override"+ rt + t +
            methodStr += "@Override"+ rt + t +    
 "public void "+method.getName()+"() {"+ rt + t +
            "public void "+method.getName()+"() {"+ rt + t +    
 "long begintime = System.currentTimeMillis();"+ rt + t +
            "long begintime = System.currentTimeMillis();"+ rt + t +    
 "System.out.println(\" Tank is begining to move !\");"+ rt + t +
            "System.out.println(\" Tank is begining to move !\");"+ rt + t +    
 "obj."+method.getName()+"();"+ rt + t +
            "obj."+method.getName()+"();"+ rt + t +    
 "long endtime = System.currentTimeMillis();"+ rt + t +
            "long endtime = System.currentTimeMillis();"+ rt + t +    
 "System.out.println(\" Tank is stop !\");"+ rt + t +
            "System.out.println(\" Tank is stop !\");"+ rt + t +    
 "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +
            "System.out.println(\"move time : \"+(endtime-begintime));"+ rt + t +    
 "}"+ rt + t;
            "}"+ rt + t; 
 }
        }
从这个片段代理可以看出,我们应该在InvocationHandler中封装obj对象,即被代理类的接口(实现类)。还应该实现:被分离出去的InvocationHandler能被代理类调用,我们应该把InvocationHandler聚合进来。
代码演变示例:
 package proxy;
package proxy;

 import java.lang.reflect.Method;
import java.lang.reflect.Method;


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

 package proxy;
package proxy;

 import java.lang.reflect.Method;
import java.lang.reflect.Method;


 public class TimeHandler implements InvocationHandler
public class TimeHandler implements InvocationHandler {
{
 
    
 private Object target;
    private Object target;


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

 @Override
    @Override

 public void invoke(Object o, Method m)
    public void invoke(Object o, Method m)  {
{
 long start = System.currentTimeMillis();
        long start = System.currentTimeMillis();
 System.out.println("starttime:" + start);
        System.out.println("starttime:" + start);
 System.out.println(o.getClass().getName());
        System.out.println(o.getClass().getName());

 try
        try  {
{
 m.invoke(target);
            m.invoke(target);

 } catch (Exception e)
        } catch (Exception e)  {
{
 e.printStackTrace();
            e.printStackTrace();
 }
        }
 long end = System.currentTimeMillis();
        long end = System.currentTimeMillis();
 System.out.println("time:" + (end-start));
        System.out.println("time:" + (end-start));
 System.out.println("endtime:" + end);
        System.out.println("endtime:" + end);
 }
    }

 }
}

 package proxy;
package proxy;

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

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

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


 public class Proxy
public class Proxy  {
{

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

 String rt = "\r\n";
        String rt = "\r\n";
 String t = "\t";
        String t = "\t";
 String methodStr = "" ;
        String methodStr = "" ;
 
                
 Method[] methods = intface.getDeclaredMethods();
        Method[] methods = intface.getDeclaredMethods();

 for(Method method : methods)
        for(Method method : methods)  {
{
 methodStr += "@Override" + rt + t + t +
            methodStr += "@Override" + rt + t + t + 
 "public void " + method.getName() + "() {" + rt + t + t + t +
                         "public void " + method.getName() + "() {" + rt + t + t + t + 
 "try {" + rt + t + t + t + t +
                         "try {" + rt + t + t + t + t +  
 "Method md = " + intface.getName() + ".class.getMethod(\"" + method.getName() + "\");" + 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 +
                         "h.invoke(this, md);" + rt + t + t + t +
 "}catch(Exception e) {" + rt + t + t + t +
                         "}catch(Exception e) {" + rt + t + t + t +      
 "e.printStackTrace();" + rt + t + t +
                         "e.printStackTrace();" + rt + t + t +     
 "}" + rt + t +
                         "}" + rt + t +                     
 "}" + rt + t + t ;
                         "}" + rt + t + t ;
 }
        }
 
        
 System.out.println(methodStr);
        System.out.println(methodStr);
 
        
 String src ="package proxy;"+ rt + t +
        String src ="package proxy;"+ rt + t +  
 "import "+intface.getName()+";"+ rt + t +
        "import "+intface.getName()+";"+ rt + t + 
 "import java.lang.reflect.Method;"+ rt + t +
        "import java.lang.reflect.Method;"+ rt + t + 
 "public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +
        "public class $Proxy1 implements "+intface.getSimpleName()+" {"+ rt + t +    
 "private InvocationHandler h ;    "+ rt + t +
        "private InvocationHandler h ;    "+ rt + t +    
 "public $Proxy1(InvocationHandler h) {"+ rt + t +
        "public $Proxy1(InvocationHandler h) {"+ rt + t +    
 "super();"+ rt + t +
        "super();"+ rt + t +    
 "this.h = h;"+ rt + t +
        "this.h = h;"+ rt + t +    
 "}" + rt + t +
        "}" + rt + t +    
 methodStr +rt +
        methodStr +rt +
 "}";
        "}";
 
         
 String fileName =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
        String fileName =System.getProperty("user.dir")+"/src/proxy/$Proxy1.java";
 File f = new File(fileName);
        File f = new File(fileName);
 FileWriter fw = new FileWriter(f);
        FileWriter fw = new FileWriter(f);

 if(f.exists())
        if(f.exists()) {
{
 f.delete();
            f.delete();
 fw.flush();
            fw.flush();
 f = new File(fileName);
            f = new File(fileName);
 };
            };
 fw.write(src);
        fw.write(src);
 fw.flush();
        fw.flush();
 fw.close();
        fw.close();
 
        
 //compile
        //compile
 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
 Iterable units = fileMgr.getJavaFileObjects(fileName);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
 CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
        CompilationTask compilationTask = compiler.getTask(null, fileMgr, null, null, null, units);
 compilationTask.call();
        compilationTask.call();
 fileMgr.close();
        fileMgr.close();
 
        
 //load into memory and create an instance
        //load into memory and create an instance

 URL[] urls = new URL[]
        URL[] urls = new URL[]  {new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
{new URL("file:/" + System.getProperty("user.dir")+ "/src/")};
 URLClassLoader ul = new URLClassLoader(urls);
        URLClassLoader ul = new URLClassLoader(urls);
 Class c = ul.loadClass("proxy.$Proxy1");
        Class c = ul.loadClass("proxy.$Proxy1");    
 
                                
 Constructor ctr = c.getConstructor(InvocationHandler.class);
        Constructor ctr = c.getConstructor(InvocationHandler.class);
 Object m = ctr.newInstance(h);
        Object m = ctr.newInstance(h);
 return m;
        return m;
 }
    }
 }
}

这样,就实现了对任意对象,任意方法的代理。
缺点就是:多接口代理没有实现,被代理对象的方法没有支持参数。