您的位置:首页 > 编程语言 > Java开发

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

2016-01-27 11:11 721 查看
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();}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: