当前位置:文档之家› 动态代理模式

动态代理模式

动态代理模式
动态代理模式

package com.bjsxt.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));

// 表明tank正在移动中

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

想知道看此段代码运行了多长时间。

方法里面运行的时间。

一个类里面有另外一个类的对象,这叫做聚合

一个用继承实现此功能,一个用聚合实现此功能。

聚合好,继承不灵活

记录日志、记录时间、记录权限的控制

用继承的方式会无限制的迭加下去。类爆炸现象!!!

代理之间互相的组合!因为共同实现的是Movable接口!!

关键是实现同一接口!

这是静态代理!

先日志、后时间还是先时时间,还是日志

代理的类根据需求还可能无限膨胀下去。

动态代理解决类太多的问题!

实现动态的编译:/JDK6 Complier API, CGLib, ASM

代理的总代理

深入java虚拟机----二进制代码的实现。

被代理的类都实现了一种结果!!继承也能实现,但是不推荐使用!!

1 实现

package https://www.doczj.com/doc/9c12612395.html,piler.test;

import java.io.File;

import java.io.FileWriter;

import https://www.doczj.com/doc/9c12612395.html,ng.reflect.Constructor;

import https://www.doczj.com/doc/9c12612395.html,.URL;

import https://www.doczj.com/doc/9c12612395.html,.URLClassLoader;

import javax.tools.JavaCompiler;

import javax.tools.StandardJavaFileManager;

import javax.tools.ToolProvider;

import https://www.doczj.com/doc/9c12612395.html,pilationTask;

import com.bjsxt.proxy.Moveable;

import com.bjsxt.proxy.Tank;

public class Test1 {

public static void main(String[] args) throws Exception{

String rt = "\r\n";

String src =

"package com.bjsxt.proxy;" + rt +

"public class TankTimeProxy implements Moveable {" + rt +

" public TankTimeProxy(Moveable t) {" + rt +

" super();" + rt +

" this.t = t;" + rt +

" }" + rt +

" Moveable t;" + rt +

" @Override" + rt +

" public void move() {" + rt +

" long start = System.currentTimeMillis();" + rt +

" System.out.println(\"starttime:\" + start);" + rt +

" t.move();" + rt +

" long end = System.currentTimeMillis();" + rt +

" System.out.println(\"time:\" + (end-start));" + rt +

" }" + rt +

"}";

System.out.println(System.getProperty("user.dir") ); // D:\Documents\GalileoSr2\Proxy

String fileName = System.getProperty("user.dir")

+ "/src/com/bjsxt/proxy/TankTimeProxy.java";

File f = new File(fileName);

FileWriter fw = new FileWriter(f);

fw.write(src);

fw.flush();

fw.close();

//compile

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 and create an instance

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); // class com.bjsxt.proxy.TankTimeProxy

Constructor ctr = c.getConstructor(Moveable.class);

Moveable m = (Moveable)ctr.newInstance(new Tank());

m.move();

}

}

上面只是实现了movable接口的动态代理

现在可以实现任意接口的动态代理:

把接口也传进去

只要传任何接口,能够实现任意接口的对象!Java 中通过动态代理类实现。

//可以对任意的对象、任意的接口方法,实现任意的代理

2 代理模式、动态代理和面向方面

分类:模式 2006-01-11 22:52 4423人阅读评论(5) 收藏举报

代理模式、动态代理和面向方面

https://www.doczj.com/doc/9c12612395.html,/hivon/article/details/576691

代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理。比如过春节要回家,由于你要上班,没时间去买票,就得票务中介代你购买,这就是一种代理模式。这个情景可以形象的描述如下:

class:火车站

{

卖票:

{……}

}

火车站是卖票的地方,我们假设只能在火车站买到票。卖票的动作实质是火车站类完成的。

Class:票务中介

{

卖票:

{

收中介费;

火车站.卖票;

}

}

顾客找票务中介买票的时候,调用票务中介.卖票。票务中介其实做了两件事,一是去火车站买票,二是不能白帮你卖票,肯定要收中介费。而你得到的好处是不用直接去火车站买票,节省了买票的时间用来上班。

以上我们简单模拟了代理模式的情景和为什么要使用代理模式,下面我们以一个例子来具体分析一下JAVA中的代理模式。

假设有一个信息管理系统,用些用户有浏览信息的权限,有些用户有浏览、添加和修改信息的权限,还有些用户有除了上述的权限,还有删除信息的权限,那么我们最容易想到的做法如下:

public class ViewAction

{

//由userId计算权限

……

String permission = ……;

if(permission.equals(Constants.VIEW))

{

System.out.println(“You could view the information……”);

……

}

}

其他的动作都和浏览信息的动作差不多。我们来看这样的类,很容易看出它的一些缺点来:第一、它把权限计算和动作执行都放在一个类里,两者的功能相互混在一起,容易造成思路的混乱,而且修改维护和测试都不好;一句话来说,它不满

足单一职责原则。第二是客户调用的时候依赖具体的类,造成扩展和运行期内的调用的困难,不满足依赖颠倒原则。

既然有这么多的问题,我们有必要对该类进行重新设计。其实大家早已想到,这个类应该使用代理模式。是啊,和我们买火车票的动作一样,动作类不能直接执行那个动作,而是要先检查权限,然后才能执行;先检查权限,后执行的那各类其实就是一个代理类,修改后的代码如下:

public interface Action

{

public void doAction();

}

首先是设计一个接口,用来满足依赖颠倒原则。

Public class ViewAction implements Action

{

public void doAction()

{

//做View的动作

System.out.println(“You could view the information……”);

……

}

}

这个类跟火车站一样,是动作的真实执行者。

Public class ProxyViewAction implements Action

{

private Action action = new ViewAction();

public void doAction()

{

//调用权限类的方法取得用户权限

if(Permission.getPermission(userId).equals(Constants.VIEW ))

{

action.doAction();

}

}

}

这是代理类,很容易理解。在我们的ProxyViewAction类中,除了做了客户真正想要做的动作:doAction()以外,还进行了额外的动作检查用户的权限。而作核心动作doAction()是在一个干干净净的类:ViewAction中进行,这个类只做核心动作,对其他的不关心,满足了单一职责原则。

客户端通过调用代理类来执行动作,而代理类一是将权限判断和动作的执行分离开来,满足了单一职责原则;二是实现了一个接口,从而满足了依赖颠倒原则。比第一个思路好了很多。

代理又被称为委派,说的是代理类并不真正的执行那个核心动作,而是委派给另外一个类去执行,如ProxyView类中,ProxyView类并没有真正执行doAction()方法,而是交给ViewAction类去执行。

我们再来看代理类ProxyViewAction,可以看到它不仅依赖于接口Action,而且依赖于具体的实现ViewAction。这样对我们的系统扩展很不利,比如我们有Add 动作、Delete动作、Modify动作等等,我们需要对每一个动作都写一个代理类,而这些代理类都做同样的事情,先进行权限判断,然后再委派。所以我们需要对这些代理再进行一次抽象,让它只依赖接口Action,而不依赖于具体的实现。

要实现这样的想法,我们需要将代理类中的具体实现提走,让代理的使用者在运行期提供具体的实现类,即所谓的依赖注入,如下:

Public class ProxyAction implements Action

{

private Action action;

public ProxyAction(Action action)

{

this.action = action;

}

public void doAction()

{

//调用权限类的方法取得用户权限

if(Permission.getPermission(userId).equals(action.getClas s().getName()))

{

action.doAction();

}

}

}

这样,我们就将所有实现了Action接口的实现使用一个代理类来代理它们。除了ViewAction类能用,以后扩展的 AddAction、 ModifyAction、DeleteAction类等等,都可以使用一个代理类:ProxyAction。

而我们的客户端类似如下:

Action action = ProxyAction(new ViewAction);

Action.doAction();

通过对代理类的依赖注入,我们使得代理类初步有了一定扩展性。但是我们还要看到,这个代理类依赖于某一个确定的接口。这仍然不能满足我们的实际要求,如我们的系统的权限控制一般是整个系统级的,这样系统级的权限控制,我们很难在整个系统里抽象出一个统一的接口,可能会有多个接口,按照上面的代理模式,我们需要对每一个接口写一个代理类,同样,这些类的功能都是一样的。这显然不是一个好地解决办法。

基于上面的原因,我们需要解决一个系统在没有统一的接口的情况下,对一些零散的对象的某一些动作使用代理模式的问题。JAVA API为我们引入了动态代理或动态委派的技术。

动态代理的核心是InvocationHandler接口,要使用动态代理就必须实现该接口。这个接口的委派任务是在invoke(Object proxy, Method m, Object[] args)方法里面实现的:

//在调用核心功能之前作一些动作

……

//调用核心功能

m.invoke(obj, args);

//在调用核心功能以后做一些动作

……

我们可以看到动态代理其实用的是反射机制来调用核心功能的:m.invoke(obj, args);正是这种反射机制的使用使得我们调用核心功能更加灵活,而不用依赖于某一个具体的接口,而是依赖于Object对象。

下面我们来具体看看动态代理或动态委派如何使用:

public class ProxyAction implements InvocationHandler {

private Object action;

public ProxyAction(Object action)

{

this.action = action;

}

public static Object getInstance(Object action)

{

return

Proxy.newProxyInstance(action.getClass().getClassLoader(),

action.getClass().getInterfaces(),new ProxyAction(action));

}

public Object invoke(Object proxy, Method m, Object[] args)

throws Throwable {

Object result;

try {

//在委派之前作动作,如权限判断等

System.out.println("before method " + m.getName());

//进行委派

result = m.invoke(action, args);

} catch (InvocationTargetException e) {

throw e.getTargetException();

} catch (Exception e) {

throw new RuntimeException("unexpected invocation exception: " + e.getMessage());

} finally {

//在委派之后做动作

System.out.println("after method " + m.getName());

}

return result;

}

}

这个代理类,首先是实现了InvocationHandler接口;然后在getInstance()方法里得到了代理类的实例;在invoke()方法里实现代理功能,也很简单。

下面我们来看客户端:

Action action = (Action)ProxyAction.getInstance(new ViewAction()); Action.doAction();

我们可以看到代理类对接口的依赖也转移到了客户端上,这样,代理类不依赖于某个接口。对于同样的代理类ProxyAction,我们也可以有如下的客户端调用:Engine engine = (Engine)ProxyAction.getInstance(new EngineImpl()); Engine.execute();

只要engineImpl类实现了Engine接口,就可以像上面那样使用。

现在我们可以看到,动态代理的确是拥有相当的灵活性。但我们同时也看到了,这个代理类写起来比较麻烦,而且也差不多每次都写这样千篇一律的东西,只有委派前的动作和委派后的动作在不同的代理里有着不同,其他的东西都需要照写。如果这样的代理类写多了,也会有一些冗余代理。需要我们进一步优化,这里我们使用模板方法模式来对这个代理类进行优化,如下:

public abstract class BaseProxy implements InvocationHandler {

private Object obj;

protected BaseProxy(Object obj)

{

this.obj = obj;

}

public static Object getInstance(Object obj,InvocationHandler instance) {

return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),instance);

}

public Object invoke(Object proxy, Method m, Object[] args)

throws Throwable {

// TODO Auto-generated method stub

Object result;

try {

System.out.println("before method " + m.getName());

this.doBegin();

result = m.invoke(obj, args);

} catch (InvocationTargetException e) {

throw e.getTargetException();

} catch (Exception e) {

throw new RuntimeException("unexpected invocation exception: " + e.getMessage());

} finally {

System.out.println("after method " + m.getName());

this.doAfter();

}

return result;

}

public abstract void doBegin();

public abstract void doAfter();

}

这样,代理的实现类只需要关注实现委派前的动作和委派后的动作就行,如下:public class ProxyImpl extends BaseProxy {

protected ProxyImpl(Object o)

{

super(o);

}

public static Object getInstance(Object foo)

{

return getInstance(foo,new ProxyImpl(foo));

}

//委派前的动作

public void doBegin() {

// TODO Auto-generated method stub

System.out.println("begin doing....haha");

}

//委派后的动作

public void doAfter() {

// TODO Auto-generated method stub

System.out.println("after doing.....yeah");

}

}

从上面的代码,我们可以看出代理实现类的确是简单多了,只关注了委派前和委派后的动作,这是我们作为一个代理真正需要关心的。

至此,代理模式和动态代理已经告一段落。我们将动态代理引申一点说开去,来作为这篇文章的蛇足。

这个话题就是面向方面的编程,或者说AOP。我们看上面的ProxyImpl类,它的两个方法doBegin()和doAfter(),这是做核心动作之前和之后的两个截取段。正是这两个截取段,却是我们AOP的基础。在OOP里,doBegin(),核心动作,doAfter()这三个动作在多个类里始终在一起,但他们所要完成的逻辑却是不同的,如doBegin()可能做的是权限,在所有的类里它都做权限;而在每个类里核心动作却各不相同;doAfter()可能做的是日志,在所有的类里它都做日志。正是因为在所有

的类里,doBegin()或doAfter()都做的是同样的逻辑,因此我们需要将它们提取出来,单独分析、设计和编码,这就是我们的AOP的思想。

这样说来,我们的动态代理就能作为实现AOP的基础了。好了,就说这么多,关于AOP技术,我们可以去关注关于这方面的知识。

分享到:

?上一篇:Servlet和JSP的协调运行——通过调整Servlet和JSP来提高你的企业应用的运行性能

?下一篇:扩展Struts(译文)

3 三言两语话动态代理

2005-09-01 23:28 by FantasySoft, 1724 visits, 收藏, 编辑

在之前的一篇关于Decorator模式的Post中,曾经提到了动态代理(Dynamic Proxy)简化了Decorator模式的实现。作为例子的代码很容易理解,但这个简化的背后仍包含着很多值得去发掘的内容。

首先我们来明确一下动态代理的定义:一个动态代理类在运行期implements 一组interface,使得interface实现类的方法调用被分派至其他的类(另外的interface 实现类或者任意的类)的方法。讲得更通俗一些,要了解动态代理,我们就要知道什么东西动态了,代理了什么?首先,一个Proxy代理了一组interface的方法。注意,代理的是interface,而不是Class,也不是abstract Class;其次,Proxy具有的型别由绑定的interface所决定的,动态就体现在此。也许看着这样的定义,还是会一头雾水,那么我们画幅图来看看吧。

从图中,我们可以看到Dynamic Proxy并没有实现Resource这个接口,但是包含了Resource接口实现类的实例;在Dynamic Proxy的create方法中,通过调用Proxy.newProxyInstance创建一个Proxy,并将该Proxy与Resource接口绑定,最后将Proxy显式类型转换成Resource接口类型并返回,这样调用者就可以通过Proxy调用interface定义的方法了;由于Proxy与Resource接口绑定了,对Resource接口的方法调用,都会交由Proxy的invoke方法去处理。而invoke方法会根据不同的方法,或给以全新的实现,或直接将方法调用交给Proxy中包含的Resource接口实现类的实例去处理。综合上面所说的,作为一个Dynamic Proxy,它必须满足以下三个条件:

1、实现了InvocationHandler接口,实现接口中定义的invoke方法;

2、包含接口实现类的实例;

3、通过Proxy.newProxyInstance方法实现Proxy与接口之间的绑定。

以下代码给出了一个简单的Dynamic Proxy实现:

public interface Resource {

public void operationA();

public void operationB();

}

public class ConcreteResource implements Resource {

public void operationA() {

System.out.println("Operation A.");

}

public void operationB() {

System.out.println("Operation B.");

}

}

public class DynamicProxy implements InvocationHandler {

private Resource resource;

public DynamicProxy() {

resource = new ConcreteResource();

}

public Resource create() {

Resource returnResource = null;

returnResource = (Resource) Proxy.newProxyInstance (Resource.class.getClassLoader(), new Class[]{ Resource.cla ss }, this);

return returnResource;

}

public Object invoke(Object obj, Method method, Object [] args) {

Object o = null;

try {

if (method.getName().equals("operationA")) {

System.out.println("OperationA in Proxy");

} else {

o = method.invoke(obj, args);

}

} catch (Exception e) {

e.printStackTrace();

}

return o;

}

}

public class Test {

public static void main(String[] args) {

DynamicProxy proxy = new DynamicProxy();

Resource resource = proxy.create();

resource.operationA();

}

}

Categories: Thought Ware, Practical Java, All About Soft

Add your comment

6 条回复

1.#1楼duanxp[未注册用户]2007-03-05 10:45

代码错了

回复引用

2.#2楼duanxp[未注册用户]2007-03-05 10:51

修改了一下DynamicProxy类

public class DynamicProxy implements InvocationHandler{

private Resource resource;

public Resource create() {

this.resource = new ConcreteResource();

return (Resource) Proxy.newProxyInstance

(resource.getClass().getClassLoader(), resource.getClass().getInterfaces(), this);

}

public Object invoke(Object obj, Method method, Object[] args) throws Throwable {

Object o = null;

try {

if(method.getName().equals("operationA")) {

System.out.println("OperationA in Proxy");

} else {

o = method.invoke(resource, args);

}

} catch (Exception e) {

e.printStackTrace();

}

return o;

}

}

回复引用

3.#3楼[楼主]FantasySoft2007-03-05 13:30

@duanxp

谢谢您的回复。我觉得两种代码写法都是可行的,而且结果也应该一样。但不可否认,您的代码会来得更加清晰和直接。

回复引用查看

4.#4楼^_^[未注册用户]2007-06-21 14:49

o = method.invoke(obj, args);

错了,

应该是

o = method.invoke(resource, args);

回复引用

5.#5楼匿名[未注册用户]2008-06-08 18:14

认同4楼的观点,但是

public Object invoke(Object obj, Method method, Object[] args) throws Throwable

输入参数Object obj 是有什么用呢?请指教,多谢了。

回复引用

6.#6楼Kent6470e[未注册用户]2009-02-13 15:07

Object obj是将DynamicProxy的引用重新传回来,可能会在invoke中用到。

谁调用了invoke?答:新生成的Proxy类。

为啥能够传DynamicProxy的引用回来?答:见Proxy.newProxyInstance的最后一个参数。

回复引用

相关主题
文本预览
相关文档 最新文档