模拟JDK动态代理

模拟JDK动态代理

一、需求

代理Tank类,计算Tank类中方法的执行时间。
因此我们需要在方法执行的前后加上计算逻辑方法

二、方案

接口

package com.proxy.dynamic;

public interface Moveable {
    void move();
}

代理类

package com.proxy.dynamic;

import java.util.Random;

public class Tank implements Moveable{

    @Override
    public void move() {

        System.out.println("tank move...");
        try {
            Thread.sleep(new Random().nextInt(3000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }       
    }
}

请求处理类(继承自定义InvocationHandler接口)

package com.proxy.dynamic;


import java.lang.reflect.Method;
/**
 * 进行时间处理的Handler处理者
 * @author lenovo
 *
 */
public class TimeHandler implements InvocationHandler{
    private Object target;//被代理的对象,并不局限于Tank

    public TimeHandler(Object target) {
        super();
        this.target = target;
    }
    @Override
    public void invoke(Object o,Method m) {
        long start=System.currentTimeMillis();
        try {
            m.invoke(target); //Method类的invoke方法  (对象,方法参数)
        } catch (Exception e) {
            e.printStackTrace();
        } 
        long end=System.currentTimeMillis();
        System.out.println("time:"+(end-start));
    }

}

模拟Proxy类

需求:通过Proxy类动态生成Tank的代理类TmeProxy。
说明:在实际的操作中,动态代理类TmeProxy是看不到源码的。为了说明Proxy类做了些什么。我把需要的TmeProxy罗列如下。

package com.proxy.dynamic;
import java.lang.reflect.Method; 
public class TimeProxy  implements com.proxy.dynamic.Moveable{
    com.proxy.dynamic.InvocationHandler h; 
    public TimeProxy(InvocationHandler h) {
        super();
        this.h = h;
    }
@Override
    public void move(){
        try{
            Method md=com.proxy.dynamic.Moveable.class.getMethod("move"); 
            h.invoke(this ,md);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }
}

目标:
1. 动态拼接上面代码字符串。通过反射reflect。
2. 生成java文件,通过I/O流将动态字符串写入文件。
3. 编译java文件,生成class文件
4. 将class文件load到内存,得到class对象
5. 通过class对象生成代理对象

    package com.proxy.dynamic;

    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.JavaCompiler.CompilationTask;
    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;

    //如何把这段代码编译成class文件,JDK Complier API,CGLib(直接生成2进制文件,没有java文件),ASM

    public class Proxy {

        //用来产生代理类对象
        public static Object newProxyInstance(Class interf,InvocationHandler h) throws Exception{
            String methodStr="";
            String rt="\r\n"; //回车加换行
            Method[] methods=interf.getMethods();
            /*for(Method m:methods){
                methodStr+="@Override"+rt+
                        "public void "+m.getName()+"(){"+rt+
                        "       long start=System.currentTimeMillis();"+rt+
                        "       k."+m.getName()+"();"+rt+
                        "       long end=System.currentTimeMillis();"+rt+
                        "       System.out.println(\"time:\"+(end-start));"+rt+
                        "}" ;
            }*/
            for(Method m:methods){
                methodStr+="@Override"+rt+
                        "   public void "+m.getName()+"(){"+rt+
                        "       try{" +rt+
                        "           Method md="+interf.getName()+".class.getMethod(\""+m.getName()+"\"); "+rt+
                        "           h.invoke(this ,md);"+rt+
                        "       } catch (NoSuchMethodException | SecurityException e) {"+rt+
                        "           e.printStackTrace();"+rt+
                        "       }"+rt+
                        "   }" ;
            }


            String src=
                    "package com.proxy.dynamic;"+rt+
                    "import java.lang.reflect.Method; "+rt+

                    "public class TimeProxy  implements "+interf.getName()+"{"+rt+
                        "   com.proxy.dynamic.InvocationHandler h; "+rt+
                //      "   Moveable k;"+rt+
                        "   public TimeProxy(InvocationHandler h) {"+rt+
                        "       super();"+rt+
                        "       this.h = h;"+rt+
                        "   }"+rt+
                        methodStr+rt+
                        "}";
            /**
             * 生成一个java文件
             */

            //System.getProperty("user.dir")

            //获得项目全路径  D:\Projects\javaSE\java_00_design_patterns\Proxy_01000
            String fileName=System.getProperty("user.dir")
                                +"/src/com/proxy/dynamic/TimeProxy.java";  //一个路径
            File f=new File(fileName); //生成一个文件对象,创建一个实体的文件
            FileWriter fw=new FileWriter(f); //给这个文件创建一个流,用于向文件中写入
            fw.write(src); //写入字符串
            fw.flush();
            fw.close();
            /**
             * 编译这个java文件
             */
            //JDK6开始新提供的类  javax.tools.*
            JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();//拿到系统默认的java编译器,javac
            //System.out.println(compiler.getClass().getName());  //com.sun.tools.javac.api.JavacTool
            StandardJavaFileManager fileMgr=compiler.getStandardFileManager(null, null, null); //先生成一个管家
            Iterable<? extends JavaFileObject> units=fileMgr.getJavaFileObjects(fileName);         //让这个管家获得这个文件对象
            CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);//获得任务
            t.call();//执行编译
            fileMgr.close(); //关闭管家

            /**
             * load 到内存memory
             */

            // 无法用loadClass()方法装载到内存,因为必须保证你需要装载的class文件在classpath下,在eclipse中即/bin下,所以需要指定我的classpath

            URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src/")};  // 指定classpath
            URLClassLoader ul=new URLClassLoader(urls);//
            Class c=ul.loadClass("com.proxy.dynamic.TimeProxy");//把class文件load到内存
            System.out.println(c);

            /**
             * create Instance 生成对象
             */

            //无法用newInstance(),只限于带无参数构造方法的类


            Constructor ctr=  c.getConstructor(InvocationHandler.class); //参数 :有参构造方法的参数的类型
            Object o=ctr.newInstance(h);
            //Moveable tp=(Moveable)ctr.newInstance(new Tank());
            //tp.move();

            return o;
        }


    }

通过这个proxy的模拟可以学习到:
1. 代码方式编译java文件
2. 将任意位置的class装载到内存
因为使用类装载器对象的loadClass方法必须保证,class类在classpath下
3. 通过获得jvm中class对象来创建类对象

测试

package com.proxy.dynamic;

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);//自动生成代理类对象,这个代理类代理了Moeable接口中的所有方法,代理内容为InvocationHandler
            m.move();


/*      Class c=com.proxy.dynamic.Tank.class;
        Tank t=(Tank)c.newInstance();
        System.out.println( t instanceof Tank);*/

/*      Object o=Class.forName("com.proxy.dynamic.Tank").newInstance();
        Tank t=(Tank)o;
        System.out.println(t.getClass());*/

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值