zoukankan      html  css  js  c++  java
  • [Java] 动态代理 04

    6. 现在我们来解决下一个问题,我们每个接口都是来实现计时功能的?显然不是,那肯定还有其他功能三

             那我们怎么样来做,才可以是我们想实现什么功能,就实现什么功能喃?动态代理?

      我们按照一贯的做法,继续把功能也传经来。

    Moveable.java

    package com.bjsxt.proxy;
    
    public interface Moveable {
    	void move();
    }

    被代理对象 Tank.java

    package com.bjsxt.proxy;
    
    import java.util.Random;
    
    public class Tank implements Moveable {
    
        @Override
        public void move() {
            System.out.println("Tank Moving...");
            try {
                Thread.sleep(new Random().nextInt(10000)); // 产生  100  毫秒  (10秒 ) 以内的随机数
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    InvocationHandler
    package com.bjsxt.proxy;
    
    import java.lang.reflect.Method;
    
    public interface InvocationHandler { //指定方法(你需要时间,日志,还是其他)
        public void invoke(Object o,Method m) throws Exception; //告诉那个对象去执行这个方法
    }
    TimeHandler
    package com.bjsxt.proxy;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class TimeHandler implements InvocationHandler {
    	
    	private Object target;  // 被代理对象
    	
    	public TimeHandler(Object target) {
    		super();
    		this.target = target;
    	}
    
    	@Override
    	public void invoke(Object o, Method m) throws Exception {
    		long start = System.currentTimeMillis();
    		System.out.println("Starttime : " + start);
    		try {
    			m.invoke(target);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		long end = System.currentTimeMillis();
    		System.out.println("times :  " + (end - start));
    
    	}
    }
    Proxy.java
    package com.bjsxt.proxy;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import java.net.URL;
    import java.net.URLClassLoader;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    
    import com.bjsxt.proxy.Moveable;
    import com.bjsxt.proxy.Tank;
    
    public class Proxy {
    	// 这个类的作用就是用来产生新的代理类
    	public static Object newProxyInstance(Class infce, InvocationHandler h)
    			throws IOException, ClassNotFoundException, SecurityException,
    			NoSuchMethodException, IllegalArgumentException,
    			InstantiationException, IllegalAccessException,
    			InvocationTargetException { // JDK6 Complier
    										// API, CGLib,
    										// ASM
    		/*
    		 * 把这个类当成一个string的字符串(源码) 现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象
    		 * 
    		 * 动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法),
    		 * 会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的
    		 */
    		String methodStr = "";
    		String rt = "
    ";
    
    		Method ms[] = infce.getMethods();
    		/*
    		 * for (Method m : ms) { methodStr = "@Override" + rt +
    		 * "    public void " + m.getName() + "() {" + rt +
    		 * "        long start = System.currentTimeMillis();" + rt +
    		 * "        t." + m.getName() + "();" + rt +
    		 * "        long end = System.currentTimeMillis();" + rt +
    		 * "        System.out.println((end - start));" + rt + "    }"; }
    		 */
    		for (Method m : ms) {
    			methodStr = methodStr + "@Override" + rt + "    public void "
    					+ m.getName() + "() {" + rt + "    try {" + rt
    					+ "        Method md = " + infce.getName()
    					+ ".class.getMethod("" + m.getName() + "");" + rt
    					+ "        h.invoke(this, md) ;" + rt
    					+ "} catch(Exception e) {" + rt
    					+ "    e.printStackTrace(); " + "}" + rt + "    }";
    		}
    
    		String src = "package com.bjsxt.proxy;" + rt
    				+ "import java.lang.reflect.Method;" + rt +
    
    				"public class $Proxy1 implements " + infce.getName()
    				+ " {" + rt +
    
    				"    public $Proxy1(InvocationHandler h) {" + rt
    				+ "        this.h = h;" + rt + "    }" + rt + rt
    				+ "    com.bjsxt.proxy.InvocationHandler h;" + rt + methodStr
    				+ rt + "}";
    		// 获取当前系统目录(就是项目根目录)
    		String fileName = "d:/src/com/bjsxt/proxy/$Proxy1.java";
    		System.out.println(fileName);
    
    		// System.out.println(fileName);
    		File f = new File(fileName);
    		FileWriter writer = new FileWriter(f);
    		writer.write(src);
    		writer.flush();
    		writer.close();
    		// 看是否生成代码,右键项目,刷新就OK了
    		/*
    		 * 第三步 : 我们来生成一个类
    		 */
    		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 拿到java的编译器
    
    		System.out.println(compiler.getClass().getName());
    
    		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,
    				null, null);// 文件的 管理器
    
    		Iterable untis = fileMgr.getJavaFileObjects(fileName); // 找到文件,把文件放在
    																// Iterable(数组)中
    
    		CompilationTask t = compiler.getTask(null, fileMgr, null, null, null,
    				untis);// 定好编译文件任务
    		t.call(); // 编译文件
    
    		fileMgr.close();// 关闭文件管理器
    		/*
    		 * 运行 :
    		 * 编译之后,打开Navigator(这个可以看到类详细的变化,就是看得到class文件的产生),就会看到多了一个$Proxy1
    		 * .class 文件,第三步成功
    		 */
    		// Load into memory and create an instance
    		/*
    		 * 第四步: 我们把文件加入内存(原本一般的做法是class.loader,就OK了,但是调用这个方法的前提就是,
    		 * 你的class文件目录必须在classpath的文件目录下),我们这里用一种通用的做法
    		 */
    		// 这里使用url加载器
    		URL[] urls = new URL[] { new URL("file:/" + "d:/src/") };
    		URLClassLoader ul = new URLClassLoader(urls); // 这里需要一个数组地址
    		Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
    		// 把类加到内存
    		System.out.println(c);
    		// 测试:输出c,OK,第四步完成
    		// 最后一步,生成对象
    		// 反射来创建对象
    		Constructor ctr = c.getConstructor(InvocationHandler.class); // 获取构造方法
    
    		Object m = (Object) ctr.newInstance(h);
    		// m.move();
    		return m;
    	}
    }
    生成的 TankTimeProxy
    package com.bjsxt.proxy;
    
    import java.lang.reflect.Method;
    
    public class TankTimeProxy implements com.bjsxt.proxy.Moveable {
    	public TankTimeProxy(InvocationHandler h) {
    		this.h = h;
    	}
    
    	com.bjsxt.proxy.InvocationHandler h;
    
    	@Override
    	public void move() {
    		try {
    			Method md = com.bjsxt.proxy.Moveable.class.getMethod("move");
    			h.invoke(this, md);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    生成的 $Proxy1
    package com.bjsxt.proxy;
    import java.lang.reflect.Method;
    
    public class $Proxy1 implements com.bjsxt.proxy.Moveable {
        public $Proxy1(InvocationHandler h) {
            this.h = h;
        }
        com.bjsxt.proxy.InvocationHandler h;
        @Override
        public void move() {
    		try {
    			Method md = com.bjsxt.proxy.Moveable.class.getMethod("move");
    			h.invoke(this, md) ;
    		} catch(Exception e) {
    			e.printStackTrace(); 
    		}
        }
    }
    Client
    package com.bjsxt.proxy;
    
    import java.io.Serializable;
    
    public class Client {
    	public static void main(String[] args) throws Exception {
    
    		Tank t = new Tank();
    		InvocationHandler h = new TimeHandler(t);
    		Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h);
    
    		m.move();
    	}
    }
    // 可以对任意的对象、任意的接口方法,实现任意的代理
    运行输出 : 
    d:/src/com/bjsxt/proxy/$Proxy1.java
    com.sun.tools.javac.api.JavacTool
    class com.bjsxt.proxy.$Proxy1
    Starttime : 1390448630091
    com.bjsxt.proxy.$Proxy1
    Tank Moving...
    times :  7619







  • 相关阅读:
    jQuery length 和 size()区别
    HTML5中Web Sql学后总结
    HTML5中的history API的理解和使用
    js中同步与异步的理解
    字符编码理解
    excel之VBA总结
    android29之UI控件的抽屉式实现方法之一(DrawerLayout和NavigationView)
    Linux环境下django初入
    Springboot整合MybatisPlus(超详细)完整教程~
    自定义持久层框架设计实现思路
  • 原文地址:https://www.cnblogs.com/robbychan/p/3786554.html
Copyright © 2011-2022 走看看