[Java] 动态代理 02 --生成代理主题角色

现在有出现了一个问题? 如果我现在有多个类,那我是不是要去实现多个计时,多个日志,那不是和刚才的继承一样,造成了类的大量产生(重复),这样显然是不合理的,那我们带怎么办喃?我们现在就可以使用动态代理
我们来自己写一个动态代理类,名字叫Proxy
源码:
package com.bjsxt.proxy;

public class Proxy {
     //这个类的作用就是用来产生新的代理类
     public static Object newProxyInstance() {  // JDK6 Complier API, CGLib, ASM
       /* 把这个类当成一个string的字符串(源码)
          现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象
      
           动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法),
           会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的
       */
        String rt = "
";
        String src = "package com.bjsxt.proxy;" + rt + rt +

        "public class TankTimeProxy implements Moveable {" + rt +

        "    public TankTimeProxy(Moveable t) {" + rt + 
                 "        this.t = t;" + rt + 
                 "    }" + rt + rt +
                 "    Moveable t;" + rt + rt + 
                 "    @Override" + rt +
                 "    public void move() {" + rt +
                 "        long start=System.currentTimeMillis();" + rt +
                 "        t.move();" + rt +
                 "        long end=System.currentTimeMillis();" + rt +
                 "        System.out.println((end - start));" + rt + 
                 "    }"  + rt +
                 "}"; 
          
           return null ;
     }

}

上面的注释解释的很清楚了。

现在我们就来动态的编译这段代码
一般动态编译文件有这些方法(用JDK6的complier API(大于1.6都行,只是这个是1.6的新特性),CGlib,ASM(直接生成二进制的class文件))
   我们直接用 JDK 的 complier 
   我们要做的步骤:
     (1), 把字符串进行编译
     (2), 生成一个类
     (3), 写入内存
     (4), 生成对象
 下面我们就来一一实现, 我们先写一个测试类,叫 Test1.java
源码:
第一步 :  准备好字符串代码 String src

第二步 : 利用文件IO, 生成 TankTimeProxy

Test1.java

package com.bjsxt.compiler.test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class Test1 {
    // 这个类的作用就是用来产生新的代理类
    public static void main(String[] args) throws IOException { // JDK6 Complier API, CGLib, ASM
        /*
         * 把这个类当成一个string的字符串(源码) 现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象
         * 
         * 动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法),
         * 会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的
         */
        String rt = "
";
        String src = "package com.bjsxt.proxy;" + rt + rt +

        "public class TankTimeProxy implements Moveable {" + rt +

        "    public TankTimeProxy(Moveable t) {" + rt + 
                 "        this.t = t;" + rt + 
                 "    }" + rt + rt +
                 "    Moveable t;" + rt + rt + 
                 "    @Override" + rt +
                 "    public void move() {" + rt +
                 "        long start=System.currentTimeMillis();" + rt +
                 "        t.move();" + rt +
                 "        long end=System.currentTimeMillis();" + rt +
                 "        System.out.println((end - start));" + rt + 
                 "    }"  + rt +
                 "}"; 
        // 获取当前系统目录(就是项目根目录)
        String fileName = System.getProperty("user.dir") + "/src/com/bjsxt/proxy/TankTimeProxy.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了
    }
}

 在做这一步之前,如果你的文件里有 TankTimeProxy.java 文件,你把它删除了,不需要了,因为我可以动态的来生成了。
          运行代码,完成之后,右键项目,刷新,你会看到出现了一个 TankTimeProxy.java 文件 .OK ,第二步完成。
第三步 :我们来生成一个类
            // 这句话的作用就是获取系统当前默认的编译器(其实就 javac)
		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 文件的产生),就会看到多了一个 TankTimeProxy.class 文件,第三步成功

第四步:

  我们把文件加入内存(原本一般的做法是class.loader,就OK了,但是调用这个方法的前提就是,你的 class 文件目录必须在 classpath 的文件目录下),我们这里用一种通用的做法
		// 这里使用url加载器
		URL[] urls = new URL[] { new URL("file:/"
				+ System.getProperty("user.dir") + "/src") };
		URLClassLoader ul = new URLClassLoader(urls); // 这里需要一个数组地址
		Class c = ul.loadClass("com.bjsxt.proxy.TankTimeProxy");
		// 把类加到内存
		System.out.println(c);
测试:输出c,OK,第四步完成
最后一步,生成对象
    // 反射来创建对象
		Constructor ctr = c.getConstructor(Moveable.class);  // 获取构造方法

		Moveable m = (Moveable) ctr.newInstance(new Tank()); // m 是用反射来创建的对象

		m.move();
运行结果:
D:JavaMyEclipseWorkspacesProxy/src/com/bjsxt/proxy/TankTimeProxy.java
com.sun.tools.javac.api.JavacTool
class com.bjsxt.proxy.TankTimeProxy
starttime : 1390141936679
Tank Moving...
6156
Ok,我们要求的功能全部实现了。
5.如果现在我们实现不是一个特定的接口(意思就是不是实现Moveable接口,而是实现的其他接口),那我们怎么办喃?
  那我们把接口也当参数传进来
    





原文地址:https://www.cnblogs.com/robbychan/p/3786557.html