【设计模式】动态代理Proxy_01

大家都知道设计模式中一个比较难理解的模式--动态代理模式,下面我们来通过一步一步完善一个工程来学习动态代理。

首先我们创建一个JavaProject,名字为"ProxyTest"。

创建一个类Tank.java,是一个坦克类,然后我们创建一个接口Moveable.java
Moveable.java:

package cn.edu.hpu.proxy;

public interface Moveable {
 void move();
}

Tank类实现Moveable接口
Tank.java:

package cn.edu.hpu.proxy;

import java.util.Random;

public class Tank implements Moveable{

  @Override
  public void move() {
    System.out.println("坦克正在移动中...");
      try {
            //睡眠10秒以内的随机时间,表示坦克正在移动中
            Thread.sleep(new Random().nextInt(10000));
          } catch (InterruptedException e) {
          // TODO Auto-generated catch block
              e.printStackTrace();
          }
  }

}

下面问题来了:
我想知道一个方法的运行时间,如何得到?

大家很容易想到的是,在方法刚刚执行后和就要结束之前计算一下当前时间,然后相减获得执行时间:

@Override
public void move() {
	long start=System.currentTimeMillis();
	System.out.println("坦克正在移动中...");
	try {
		//睡眠10秒以内的随机时间,表示坦克正在移动中
		Thread.sleep(new Random().nextInt(10000));
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	long end=System.currentTimeMillis();
	System.out.println("运行时间:"+(end-start)+"ms");
}

那么,如果不允许修改move中的方法呢?比如说这个方法是别人的包里提供给你的,是人家已经写好的源代码,就编译了class给你,就没有给你源码,你没法在源码上修改怎么办?解决这件事情之前,我们先写一个测试类:

package cn.edu.hpu.proxy;

public class Client {
    public static void main(String[] args) {
         Moveable m=new Tank();
         m.move();
    }
}

不要求计算move()方法执行的时间(JDK为其准备的时间等...),要计算内部代码执行的时间。

我们再写一个Tank2,继承Tank

package cn.edu.hpu.proxy;

public class Tank2 extends Tank{

     @Override
     public void move() {
         long start=System.currentTimeMillis();
         super.move();
         long end=System.currentTimeMillis();
         System.out.println("运行时间:"+(end-start)+"ms");
     }

}

用继承来实现把原来的方法前后加一些逻辑,这是一种方式,是没有问题的。

除了上面这种方式之外,还有另外一种方式:
写一个Tank3,实现和Tank一样的接口

package cn.edu.hpu.proxy;

public class Tank3 implements Moveable{
	Tank t;

	public Tank3(Tank t) {
		super();
		this.t = t;
	}

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");

	}

}

上面一个使用继承来实现,一个是一个类存有另外一个类的对象,叫聚合。
这两种都是简单的代理的实现。

现在请问大家:认为这两种方式那种更好?为什么?
聚合好。为什么呢?
下面来看看。

除了时间的代理之外,还有其他的各种各样的代理,如在方法开始前后做日志记录,或加一段事务控制,或检查运行权限等。

由于Tank3它是记录时间的,而且是Tank的一个代理类,所以我们给它换一个名字,叫"TankTimeProxy"类。

package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Tank t;

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

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");

	}

}

我们再写一个类是对Tank类加日志信息的日志代理类:

package cn.edu.hpu.proxy;

public class TankLogProxy implements Moveable{
	Tank t;

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

	@Override
	public void move() {
		System.out.println("坦克准备移动...");
		t.move();
		System.out.println("坦克移动结束...");
	}

}

下面我们要对日志进行叠加,规定先先记录时间,再记录日志,怎么办?需要在写一个Tank3去继承Tank2(里面已经包含了时间记录代理),然后把日志记录代码加在move方法前后。如果规定先记录日志,再记录时间,怎么办?又要创建一个Tank4,继承Tank类,然后把日志记录代码加在move方法前后,之后再创建Tank5继承自Tank4,在在move方法前后加上时间记录代码.....你会发现,用继承的方式实现代理的话,这个类会无限制的增加下去,各种各样的代理功能如果想实现叠加的话,这个类要无限的往下继承,无边无际了...

所以,回到刚刚的话题,继承并不是好的实现代理的方式,聚合比较合适。
接下来看看聚合的优点。首先我们把TankTimeProxy和TankLogProxy中的Tank引用类改成Moveable接口(包括构造函数的参数类型)。
拿出TankTimeProxy与TankLogProxy看看:

TankTimeProxy:

package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Moveable t;

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

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");

	}

}

TankLogProxy:

package cn.edu.hpu.proxy;

public class TankLogProxy implements Moveable{
	Tank t;

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

	@Override
	public void move() {
		System.out.println("坦克准备移动...");
		t.move();
		System.out.println("坦克移动结束...");
	}

}

我们其中的引用类Moveable t;既可以是实现了Moveable接口的Tank类,又可以是实现了Moveable接口的代理类,那么,代理之间的嵌套是不是可以实现呢?可以,加入我们想在日志代理外面嵌套时间代理,即先记录时间后记录日志,那么我们直接把引用类Moveable t;的实现指向日志代理类TankLogProxy即可。
即:

Moveable t=new TankTimeProxy(new TankLogProxy(new Tank()));
t.move();

运行TankTimeProxy的move方法时,

@Override
public void move() {
long start=System.currentTimeMillis();
System.out.println("开始时间:"+start+"ms");
//此时t指向TankLogProxy类,调用的是TankLogProxy类的move方法
t.move();
long end=System.currentTimeMillis();
System.out.println("运行时间:"+(end-start)+"ms");

}

我们知道TankLogProxy类的move方法是这样的(也就是上面的t.move();):

@Override
public void move() {
System.out.println("坦克准备移动...");
//这里的t指向的是Tank,所以执行的是Tank的move方法
t.move();
System.out.println("坦克移动结束...");
}

把TankTimeProxy的move方法中的t.move();解刨出来就是:

</pre><pre name="code" class="java">@Override
public void move() {
	long start=System.currentTimeMillis();
	System.out.println("开始时间:"+start+"ms");

	System.out.println("坦克准备移动...");
	//Tank的move方法
	t.move();
	System.out.println("坦克移动结束...");

	long end=System.currentTimeMillis();
	System.out.println("运行时间:"+(end-start)+"ms");

}

注意:此时Tank类(没加任何代理,只有最原始的移动方法):

package cn.edu.hpu.proxy;

import java.util.Random;

public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("坦克正在移动中...");
		try {
			//睡眠10秒以内的随机时间,表示坦克正在移动中
			Thread.sleep(new Random().nextInt(10000));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

测试:

package cn.edu.hpu.proxy;

public class Client {
	public static void main(String[] args) {
		Moveable t=new TankTimeProxy(new TankLogProxy(new Tank()));
		t.move();
	}
}

测试结果:
开始时间:1435309953538ms
坦克准备移动...
坦克正在移动中...
坦克移动结束...
运行时间:1529ms

此时就实现了时间和日志代理的嵌套,当然还可以嵌套更多的代理逻辑,是不是比继承方便多了?
以后为了方便管理,也可以将代理逻辑的先后通过配置文件来配置。
关键就是实现统一接口。

我们接下来继续讨论(以后只考虑TankTimeProxy)。如果Moveable接口里还有一个方法,stop()停止方法,Tank必须实现这个方法,那么我要知道stop()方法运行的时间,还要加之前写的时间代理代码:
首先是Moveable接口

package cn.edu.hpu.proxy;

public interface Moveable {
	void move();
	void stop();
}

Tank类

package cn.edu.hpu.proxy;

import java.util.Random;

public class Tank implements Moveable{

	@Override
	public void move() {
		System.out.println("坦克正在移动中...");
		try {
			//睡眠10秒以内的随机时间,表示坦克正在移动中
			Thread.sleep(new Random().nextInt(10000));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void stop() {
		System.out.println("坦克停止中...");

	}

}

TankTimeProxy类

package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Moveable t;

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

	@Override
	public void move() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.move();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");

	}

	@Override
	public void stop() {
		long start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
		t.stop();
		long end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
	}
}

这个时候我们注意了,当我们平时在写程序的时候,如果你发现有一段代码你总是在那里写,这个方法里有,那个方法里也有,这个时候我们就要考虑将这段代码封装起来了。
这里我们发现TankTimeProxy类中有关时间记录的代码有重复,我们将其封装起来:

package cn.edu.hpu.proxy;

public class TankTimeProxy implements Moveable{
	Moveable t;
	long start;
	long end;

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

	public void before(){
		start=System.currentTimeMillis();
		System.out.println("开始时间:"+start+"ms");
	}

	public void after(){
		end=System.currentTimeMillis();
		System.out.println("运行时间:"+(end-start)+"ms");
	}

	@Override
	public void move() {
		this.before();
		t.move();
		this.after();
	}

	@Override
	public void stop() {
		this.before();
		t.stop();
		this.after();
	}
}

下面我们考虑一个很难的问题,假如说我想让TankTimeProxy这个代理叫做TimeProxy,意味着它不仅可以计算Tank类的运行方法,还可以把任何一个对象当做被代理对象,可以计算任何代理类的方法运行的时间。(至于为什么会有这种需求,比如说我们又加了一个交通工具Car,我们要对Car计算运行时间的话,要重写一个CarTimeProxy代理,如果还有上百种类型的类,对不同的类实现时间代理,你要写上百种代理类!!!)。

所以我们要实现一种"通用"时间代理,可以对任意对象代理。

我们可以使用Java的反射机制,具体方式详见总结02

转载请注明出处:http://blog.csdn.net/acmman/article/details/46827819

时间: 2025-01-19 15:51:22

【设计模式】动态代理Proxy_01的相关文章

设计模式之动态代理(dynamic proxy)

1 动态代理与静态代理 我们从上一篇设计模式之代理模式一文中已经知道,在代理模式中代理对象和被代理对象一般实现相同的接口,调用者与代理对象进行交互.代理的存在对于调用者来说是透明的,调用者看到的只是接口.这就是传统的代理模式静态代理的特点. 那么传统的静态代理模式有什么问题呢?如果需要代理的类只有一个,那么静态代理没什么问题,如果有很多类需要代理呢,用静态代理的话就需要为每一个类创建一个代理类,显然这么做太过繁琐也容易出错.为此,JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代

【设计模式】动态代理Proxy_02

我们继续上一次的动态代理探讨. 上一篇我们说道,所以我们要实现一种"通用"代理,可以对任意对象代理. 那么怎么实现呢? 我们规定产生代理的时候,被代理的类一定要实现一个接口.这样我们可以根据接口来生成代理对象,而不是根据具体的类. 我们明确一下我们的目标:我们可以对任何的对象,任何的类(前提是这个类实现了某个接口),我们就可以给它生成一个代理. 我们怎么样给它生成这个代理呢?为了模拟JDK的实现,我们添加一个新的类叫Proxy.java: package cn.edu.hpu.prox

【设计模式】【动态代理,在方法前和方法后加事务,AOP】

/** * Description: * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a> * <br/>Copyright (C), 2001-2010, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Da

跟屌丝大哥学习设计模式---代理模式之动态代理

动态代理 java中动态代理机制的引入使得代理模式的思想更加完善与进步,它允许动态的创建代理并支持对动态的对所代理的方法进行调用.Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:  (1). Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args).在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上

【设计模式】动态代理Proxy_03

我们继续上一篇总结. 我们把TankTimeProxy的类Load进内存之后我们要生成它的一个对象.我们先来回顾一下我们之前写好的Proxy类: package cn.edu.hpu.ProxyTest; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLClassLoader; import j

设计模式之代理模式(Proxy Pattern)

1 代理模式定义 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 本篇文章主要介绍的是静态代理,关于动态代理请参考:设计模式之动态代理(dynamic proxy) 2 代理模式的作用 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 通过代理类对原有类进行功能增强,如增加缓存.异常处理.日志处理等,Spring AOP用的就是(动态)代理模式 3 代理模式一

PHP4中实现动态代理

动态 一. 摘要本文简单阐述了Proxy模式及具体说明了如何在PHP4中实现动态代理, 本文只是给出了一个实现的方法的原型. 由于水平有限, 有任何意见和建议请反馈给Binzy [ Binzy at JustDN dot Com ]. 二. 概述在我们开始实现Dynamic Proxy之前, 也许我们应该先了解一下什么是Proxy和它有什么用. 下面是一篇来自博客堂加菲猫的不错的形象讲述Proxy的文章: "武当学艺与缓存代理". Proxy模式是"GoF"介绍的2

JDK动态代理简介

动态代理 代理模式是 Java 中的常用设计模式,代理类通过调用被代理类的相关方法,提供预处理.过滤.事后处理等服务,动态代理及通过反射机制动态实现代理机制.JDK 中的 java.lang.reflect.Proxy 类可以用来实现动态代理. 首先,准备一个简单的接口和实现类 /** * 接口 IHello.java */ public interface IHello { void hello(); } /** * 实现类 Hello.java */ public class Hello i

Java动态代理机制分析及扩展,第1部分

引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要 简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所 有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可 以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.通过阅 读本文,读者将会对 Java 动态代理机制有更加深入的理解.本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内 部实现. 代理:设计模式 代理是一种常用的设