从坦克聊聊代理模式之动态代理2

本文可作为 北京尚学堂设计模式的 学习笔记
在上一节文章里 我们能让Proxy类代理Imoveable接口 但是也只有这一个接口 别的还不行

今天我们让它能代理所有接口

(在看这篇文章之前 请一定先看看上一篇)

先看看项目目录

从坦克聊聊代理模式之动态代理2_第1张图片


那第一步就是分析接口逻辑 在上一篇文章里 我们要在坦克移动前后记录时间 把硬代码写到了字符串里 这个就不可以改变了

为了做出在被代理对象前后做操作的逻辑 我们写出下面这个类

package proxy;

import java.lang.reflect.Method;

public class TimeInvHan implements InvocationHandler {

	public TimeInvHan(Object object) {
		super();
		this.obj = object;
	}

	Object obj;

	@Override
	public void invoke(Method m, Object o) {

		long start = System.currentTimeMillis();
		System.out.println("starttime:" + start);
		try {
			m.invoke(obj, new Object[]{});
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		
		}
		long end = System.currentTimeMillis();
		System.out.println("end time:" + end);
		System.out.println("expend time:" + (end - start));

	}
}

TimeInvHan 里面有个Object类型的属性obj 它所代表的就是被代理的对象 (就是最原始的那个坦克)
再看invoke 这个方法
Method m 这是我们被代理对象的方法 (在说简单点就是 move方法)
Object o 这个参数我们类里没有用到 (我们所讨论的情况 比较简单 但是在复杂的情况下这个o就会用到了)
m.invoke(obj, new Object[]{});
这行代码就更简单了 就是调用obj的m方法 其参数为new Object[]{} ----(就是为空)
然后我们在这个核心代码前后加上 我们要的逻辑 --记录时间

InvocationHandler 这个接口也很简单 如下

package proxy;

import java.lang.reflect.Method;

public interface InvocationHandler {

    void invoke(Method m,Object o);
}


(大家肯定还很疑惑 这个InvocationHandler到底是干什么的 先别急 往下看)

那第二步就是修改Proxy里面的那个字符串 改接口

newInstance 里面的参数Class 就是接口 在上面的例子中就是Imoveable

public static Object newInstance(Class h,InvocationHandler in) throws Exception{
		String rt = "\r\n";
		String methodstr="";
		Method[] methods=h.getMethods();
		for(Method m:methods){
			methodstr+="@Override"+rt;
			methodstr+="public "+m.getReturnType()+" "+m.getName()+"() { "+rt 
					+"   try{" + rt
					+"      Method md = " + h.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
					+"      h.invoke(md,"+"this);" +rt
					+"      }catch(NoSuchMethodException | SecurityException e){"+rt
					+"         e.printStackTrace();" +rt
					+"   }" + rt
			        +"  }";
		}
				String src = "package proxy.compiler.test;" + rt
				+ "import proxy.InvocationHandler;"+rt
				+ " import java.lang.reflect.Method;"+rt
				+ "public class Proxy1 implements "+ h.getName() + "{" + rt
				+ "    public Proxy1(InvocationHandler t) {" + rt
				+ "        super();" + rt 
				+"        this.h = t;" + rt
				+ "    }" + rt +

				
				"    InvocationHandler h;" + rt +
                 methodstr+rt+
				"}";
				
	}
	........
}



String methodstr="";
        Method[] methods=h.getMethods();
        for(Method m:methods){
            methodstr+="@Override"+rt;
            methodstr+="public "+m.getReturnType()+" "+m.getName()+"() { "+rt
                    +"   try{" + rt
                    +"      Method md = " + h.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
                    +"      h.invoke(md,"+"this);" +rt
                    +"      }catch(NoSuchMethodException | SecurityException e){"+rt
                    +"         e.printStackTrace();" +rt
                    +"   }" + rt
                    +"  }";
        }

上面这部分就是将h这个接口里面的所有方法都放入字符串中

 
 

下面是proxy的全部代码

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 newInstance(Class h,InvocationHandler in) throws Exception{
        String rt = "\r\n";
        String methodstr="";
        Method[] methods=h.getMethods();
        for(Method m:methods){
            methodstr+="@Override"+rt;
            methodstr+="public "+m.getReturnType()+" "+m.getName()+"() { "+rt
                    +"   try{" + rt
                    +"      Method md = " + h.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt
                    +"      h.invoke(md,"+"this);" +rt
                    +"      }catch(NoSuchMethodException | SecurityException e){"+rt
                    +"         e.printStackTrace();" +rt
                    +"   }" + rt
                    +"  }";
            
        }
                String src = "package proxy.compiler.test;" + rt
                + "import proxy.InvocationHandler;"+rt
                + " import java.lang.reflect.Method;"+rt
                + "public class Proxy1 implements "+ h.getName() + "{" + rt
                + "    public Proxy1(InvocationHandler t) {" + rt
                + "        super();" + rt
                +"        this.h = t;" + rt
                + "    }" + rt +

                
                "    InvocationHandler h;" + rt +
                 methodstr+rt+
                "}";
                
        
        String fileName ="d:/src/proxy/compiler/test/Proxy1.java";

        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();

        //compoler
        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = javaCompiler
                .getStandardFileManager(null, null, null);
        Iterable units = fileManager.getJavaFileObjects(fileName);
        CompilationTask task = javaCompiler.getTask(null, fileManager, null,
                null, null, units);
        task.call();
        fileManager.close();

        // load into memory and create an instance
        URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("proxy.compiler.test.Proxy1");

        
        Constructor cons= c.getConstructor(InvocationHandler.class);
        Object m=cons.newInstance(in);
        return m;
    }
}


//这是测试代码
package proxy;

public class Client {
    public static void main(String[] args) throws Exception {
        Say a=new SayHelloOrCurse();                
        InvocationHandler in= new HelloLogInvHan(a);
        
        Say s=(Say)Proxy.newInstance(Say.class, in);
        s.hello();
        }
}
package proxy;

public class SayHelloOrCurse implements Say {

    @Override
    public void hello() {
        // TODO Auto-generated method stub
        System.out.println("hello world");
        
    }

    @Override
    public void curse() {
        // TODO Auto-generated method stub
        System.out.println("i curse you");
    }
}
package proxy;

public interface Say {
    void hello();
    void curse();
}

 测试结果如下
start log:
hello world
end log:


在Proxy.newInstance中
我们把类保存到了如下的位置
String fileName ="d:/src/proxy/compiler/test/Proxy1.java";
在相应位置可以找到Proxy1 如下

package proxy.compiler.test;
import proxy.InvocationHandler;
 import java.lang.reflect.Method;
public class Proxy1 implements proxy.Say{
    public Proxy1(InvocationHandler t) {
        super();
        this.h = t;
    }
    InvocationHandler h;
@Override
public void hello() { 
   try{
      Method md = proxy.Say.class.getMethod("hello");
      h.invoke(md,this);
      }catch(NoSuchMethodException | SecurityException e){
         e.printStackTrace();
   }
  }@Override
public void curse() { 
   try{
      Method md = proxy.Say.class.getMethod("curse");
      h.invoke(md,this);
      }catch(NoSuchMethodException | SecurityException e){
         e.printStackTrace();
   }
  }
}

这下大家应该明白了吧
在client测试时
Say s=(Say)Proxy.newInstance(Say.class, in);
这个s实际上就是Proxy1的一个实例对象
而相应的InvocationHandler在Proxy.newinstance的最后几行代码里已经注入了
至于InvocationHandler的invoke方法在文章上面已经给出
这下大家应该明白InvocationHandler的作用了吧

写到这里 我们其实就已经模拟了jdk动态代理的机理 当然我们这只是最原始的模拟 很多细节方面还没有考虑到
但最核心的原理 就是上面所说的那个

现在 我们用一次真实的jdk动态代理

package JDKProxy;

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

public class LogInvHan implements InvocationHandler {

	public LogInvHan(Object object) {
		super();
		this.obj = object;
	}

	Object obj;

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
		System.out.println("start log:" );
		try {
			method.invoke(obj, new Object[]{});
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("end log:" );
		return null;
	}
}

很疑惑public Object invoke(Object proxy, Method method, Object[] args)
这个proxy参数似乎没有用 我们的被代理对象在构造函数里已经传进来了
那它有什么用 我自己也没搞清楚 哪位朋友清楚的 请告诉我一声

package JDKProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class JdkProxyTest {
    
    public static void main(String[] args) {
        Imoveable t=new Tank();
        InvocationHandler h=new LogInvHan(t);
        
        Imoveable m=(Imoveable)Proxy.newProxyInstance(t.getClass().getClassLoader(),
                t.getClass().getInterfaces(), h);
        
        InvocationHandler ha=new TimeInvHan(m);
        Imoveable m2=(Imoveable)Proxy.newProxyInstance(t.getClass().getClassLoader(),
                t.getClass().getInterfaces(), ha);
        m2.move();
    }
}



这是多重代理
测试结果
starttime:1406365283656
start log:
i can move...
end log:
end time:1406365284343
expend time:687
其实这个多重代理 之前我是这样写的
       
   Imoveable t=new Tank();
        InvocationHandler h=new LogInvHan(t);
        InvocationHandler ha=new TimeInvHan(h);
        
        Imoveable m=(Imoveable)Proxy.newProxyInstance(t.getClass().getClassLoader(),
                t.getClass().getInterfaces(), ha);
        m.move();


在TimeInvHan的invoke方法里面
method.invoke(obj, new Object[]{});
这句代码里报这个错误
java.lang.IllegalArgumentException: object is not an instance of declaring class
改成如上那个才ok了  
大家对这个错误怎么看?










你可能感兴趣的:(java,设计模式,spring,jdk)