设计模式之Proxy(代理):模拟JDK的动态代理

1.静态代理
    代理的实现方式有两种,一是继承,二是聚合。示例如下:计算Tank中move方法的运行时间,不包括JDK为其准备运行环境的时间。

package com.zj.proxy;

public interface Moveable {
    void move();
    void stop();
}
package com.zj.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));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void stop() {
        System.out.println("Tank stop....");
    }
}
package com.zj.proxy;

public class TankExtendProxy extends Tank{
    /*
     * 方法1:继承Tank,复写move方法,前后加上时间计时,然后相减。
     *     TankExtendProxy相当于Tank的代理:调用TankExtendProxy的move时,
     *     总会调用TankExtendProxy父类的move,即相当于调用Tank的move。
     */
    @Override
    public void move() {
        long start=System.currentTimeMillis();
        super.move();
        long end=System.currentTimeMillis();
        //计算方法的运行时间
        System.out.println("time="+(end-start));
    }
}
package com.zj.proxy;

public class TankTimeProxy implements Moveable{
    Moveable t;

    public TankTimeProxy(Moveable t) {
        super();
        this.t = t;
    }

    /*
     * 方法2:聚合(一个类中有另外一个类的对象)。Tank类型的成员变量,实现Moveable接口,复写move方法,调用Tank对象的move方法,前后加上时间计时,然后相减。
     *     TankTimeProxy相当于Tank的代理:调用TankTimeProxy的move即相当于调用Tank的move。
     */
    @Override
    public void move() {
        long start=System.currentTimeMillis();
        System.out.println("Time proxy: Start time....");
        t.move();
        long end=System.currentTimeMillis();
        //计算方法的运行时间
        System.out.println("Time proxy: run time= "+(end-start));
    }

    @Override
    public void stop() {

    }
}
package com.zj.proxy;

public class TankLogProxy implements Moveable{
    Moveable t;

    public TankLogProxy(Moveable t) {
        super();
        this.t = t;
    }

    @Override
    public void move() {
        System.out.println("Log Proxy: Tank start move....");
        t.move();
        System.out.println("Log Proxy: Tank stop....");
    }

    @Override
    public void stop() {

    }
}
package com.zj.proxy;

import org.junit.Test;

/*
 * 1.代理的实现:计算move的运行时间,不包括JDK为其准备运行环境的时间
 *   方法1:继承Tank,复写move方法,前后加上时间计时,然后相减。
 *   方法2:聚合(一个类中有另外一个类的对象)。Tank类型的成员变量,实现Moveable接口,复写move方法,调用Tank对象的move方法,前后加上时间计时,然后相减。
 * 
 * 2.上述两种方法都是对Tank的代理,问哪种方法好?
 *   答:聚合比继承好。
 */
public class Client {
    public static void main(String[] args) {
        Moveable m=new Tank();
        m.move();
    }

    //测试继承方式实现的代理
    @Test
    public void testTankExtendProxy() {
        TankExtendProxy tep=new TankExtendProxy();
        tep.move();
    }

    //测试聚合方式实现代理:先记录时间,后记录日志
    @Test
    public void testTimeLog(){
        Tank t=new Tank();

        TankTimeProxy ttp=new TankTimeProxy(t);
        TankLogProxy tlp=new TankLogProxy(ttp);
        Moveable m=tlp;
        m.move();
    }

    //测试聚合方式实现代理:先记录日志,后记录时间
    @Test
    public void testLogTime(){
        Tank t=new Tank();

        TankLogProxy tlp=new TankLogProxy(t);
        TankTimeProxy ttp=new TankTimeProxy(tlp);
        Moveable m=ttp;
        m.move();
    }
}

2.动态代理
    如果有很多对象,要想对任意对象、任意的接口方法,实现任意代理,又改怎么办呢?答案是动态代理,下面我们简单模拟JDK的动态代理。

package com.zj.proxy2;

public interface Moveable {
    void move();
}
package com.zj.proxy2;
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));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
package com.zj.proxy2;

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

public class Proxy {
    public static Object newProxyInstance(Class interf, InvocationHandler h) throws Exception{
        /**********************source code*******************************/
        String rt="\r\n";

        String methodStr="";
        Method[] methods= interf.getMethods();

        for(Method m:methods){
            methodStr += "  @Override" + rt +
                         "  public " + m.getReturnType()+" "+m.getName()+"(){" + rt +
                         "      try {" + rt +
                         "          Method md="+interf.getName()+".class.getMethod(\""+m.getName()+"\");"+ rt +
                         "          h.invoke(this,md);" + rt +
                         "      } catch(Exception e){e.printStackTrace();}" + rt +
                         "  }";
        }

        String src=
            "package com.zj.proxy2;" + rt +
            "import java.lang.reflect.Method;" + rt +

            "public class MyProxy implements "+interf.getName()+" {" + rt +
            "   InvocationHandler h;" + rt +

            "   public MyProxy(InvocationHandler h) {" + rt +
            "       this.h = h;" + rt +
            "   }" + rt +

            methodStr + rt +
            "}";

        //拿到系统的当前目录
        String fileName=System.getProperty("user.dir")+"\\src\\com\\zj\\proxy2\\MyProxy.java";
        //System.out.println(fileName);//输出结果为F:\sys_zj\Proxy\src\com\zj\proxy2\MyProxy.java
        File f=new File(fileName);
        FileWriter fw=new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();

        /**********************compile*******************************/
        //Java的编译器,调用ToolProvider.getSystemJavaCompiler()可以拿到系统当前默认的Java编译器,即Javac
        JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();
        //System.out.println(compiler.getClass().getName());//输出结果为com.sun.tools.javac.api.JavacTool

        StandardJavaFileManager fileMgr=compiler.getStandardFileManager(null, null, null);
        Iterable units=fileMgr.getJavaFileObjects(fileName);
        CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();//调用任务,编译
        fileMgr.close();

        /**********************load into memory*******************************/
        //把硬盘上的java文件放到内存中
        URL[] urls=new URL[]{new URL("file:\\"+ System.getProperty("user.dir")+"\\src")};
        URLClassLoader ul=new URLClassLoader(urls);
        Class c=ul.loadClass("com.zj.proxy2.MyProxy");
        //System.out.println(c);//输出结果为class com.zj.proxy2.MyProxy

        /**********************create an instance*******************************/
        //拿到构造方法
        Constructor ctr=c.getConstructor(InvocationHandler.class);
        //获得代理对象
        Object obj=ctr.newInstance(h);

        return obj;
    }
}
package com.zj.proxy2;

import java.lang.reflect.Method;

public interface InvocationHandler {
    public void invoke(Object obj,Method m);
}
package com.zj.proxy2;

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 obj,Method m) {
        long start=System.currentTimeMillis();
        System.out.println("Time proxy: Start time....");
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        } 
        long end=System.currentTimeMillis();
        System.out.println("Time proxy: run time= "+(end-start));
    }
}
package com.zj.proxy2;

import java.lang.reflect.Method;

public class LogHandler implements InvocationHandler{

    //被代理对象
    private Object target;

    public LogHandler(Object target) {
        super();
        this.target = target;
    }

    @Override
    public void invoke(Object obj,Method m) {
        System.out.println("Log Proxy: Tank start move....");
        try {
            m.invoke(target);
        } catch (Exception e) {
            e.printStackTrace();
        } 
        long end=System.currentTimeMillis();
        System.out.println("Log Proxy: Tank stop....");
    }

}
package com.zj.proxy2;

/*
 * 动态代理的好处:可以对任意的对象、任意的接口方法,实现任意的代理;还可以实现代理的叠加
 * 
 * 1.什么是动态代理?
 * 2.动态代理是怎么产生的?
 * 3.动态代理的作用?
 */
public class Client {
    public static void main(String[] args) throws Exception {
        Tank t=new Tank();
        InvocationHandler h1=new TimeHandler(t);
        Moveable m1=(Moveable)Proxy.newProxyInstance(Moveable.class, h1);

        InvocationHandler h2=new LogHandler(m1);
        Moveable m2=(Moveable)Proxy.newProxyInstance(Moveable.class, h2);
        m2.move();
    }
}

运行结果如下:
这里写图片描述
其中Proxy类动态生成的代理类为MyProxy.java,如下:

package com.zj.proxy2;
import java.lang.reflect.Method;
public class MyProxy implements com.zj.proxy2.Moveable {
    InvocationHandler h;
    public MyProxy(InvocationHandler h) {
        this.h = h;
    }
    @Override
    public void move(){
        try {
            Method md=com.zj.proxy2.Moveable.class.getMethod("move");
            h.invoke(this,md);
        } catch(Exception e){e.printStackTrace();}
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值