zoukankan      html  css  js  c++  java
  • [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接口,而是实现的其他接口),那我们怎么办喃?
      那我们把接口也当参数传进来
        





  • 相关阅读:
    在平面中,一个点绕任意点旋转θ度后的点的坐标
    消息队列
    通过注册表修改默认打开方式
    Beagleboneblack的MLO文件干了些啥
    input子系统 KeyPad-Touch上报数据格式与机制
    字符编码
    find命令之exec
    Jmeter(一)-Linux上的安装和使用
    for循环删除linkedlist中的元素。。。。。。
    Java中组装String字符串常用的几种防范
  • 原文地址:https://www.cnblogs.com/robbychan/p/3786557.html
Copyright © 2011-2022 走看看