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接口,而是实现的其他接口),那我们怎么办喃?
      那我们把接口也当参数传进来
        





  • 相关阅读:
    npm optionalDependencies 依赖处理
    grafana 8.0 新的报警机制
    cube.js prometheus 监控
    cube.js 新版本playground 特性
    data mesh & data lake & data fabric
    java 几个开源dataframe 的实现包
    archaius netflix 的配置管理工具框架
    dremio 文件夹数据分区
    dremio 17 发布了
    Linux系统挂载未分配硬盘空间
  • 原文地址:https://www.cnblogs.com/robbychan/p/3786557.html
Copyright © 2011-2022 走看看