`
michael8335
  • 浏览: 187600 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

设计模式之动态代理

阅读更多
之前写了一篇代理模式的文章,同时介绍了使用代理模式的好处,但是那种代理模式也存在一定的弊端:代理类和被代理类紧紧的耦合在一起了,一个代理类只能为一个代理类服务。这种显然是不愿意看到的,下面用一个例子介绍一下Java的动态代理和深入分析一下Java的动态代理。
被代理对象接口
package com.yf.designpattern.proxy.dynamicproxy;

public interface Dog {
	public void info();

	public void run();
}


被代理对象
package com.yf.designpattern.proxy.dynamicproxy;

public class GunDog implements Dog {

	@Override
	public void info() {
		System.out.println("This is gun dog!!!");

	}

	@Override
	public void run() {
		System.out.println("The gun dog is running!!!");

	}

}
 

下面定义一个类似拦截器的类,该类的作用是对被代理类的功能增强,比如记录日志等其他功能
package com.yf.designpattern.proxy.dynamicproxy;

public class TxUtil {
	public void beginTx() {
		System.out.println("=======The transaction is begining========");
	}
	public void endTx(){
		System.out.println("=======The transaction is ending========");
	}
}
 

下面定义了一个织入类,该类实现了InvocationHandler接口(后面可以看到,该类就是代理类)
package com.yf.designpattern.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvokationHander implements InvocationHandler {
	private Object target;

	public void setTarget(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		TxUtil tu = new TxUtil();
		tu.beginTx();
		Object result = method.invoke(target, args);
		tu.endTx();
		return result;
	}

}
 

最后一个就是代理类的工厂类,该类主要产生各种代理类
package com.yf.designpattern.proxy.dynamicproxy;

import java.lang.reflect.Proxy;

public class MyProxyFactory {
	public static Object getProxy(Object target) throws Exception {
		MyInvokationHander myHander = new MyInvokationHander();
		myHander.setTarget(target);
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), myHander);
	}
}
 

最后一个是测试类,用于演示动态代理
package com.yf.designpattern.proxy.dynamicproxy;

public class Test {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		System.out.println("Before Proxy.......");
		Dog target=new GunDog();
		target.info();
		target.run();
		System.out.println("After Proxy.......");
		//get an instance of MyInvokationHander
		Dog dog=(Dog) MyProxyFactory.getProxy(target);
		//MyInvokationHander.invoke
		dog.info();
		//MyInvokationHander.invoke
		dog.run();
		System.out.println(dog.getClass().getName());
	}

}
 

通过执行,输出结果如下:
Before Proxy.......
This is gun dog!!!
The gun dog is running!!!
After Proxy.......
=======The transaction is begining========
This is gun dog!!!
=======The transaction is ending========
=======The transaction is begining========
The gun dog is running!!!
=======The transaction is ending========
$Proxy0
这里我们发现,在GunDog通过代理工厂代理之后,会在每个方法执行之前和执行之后都打印日志,而且更为神奇的是,产生的这个代理类既不是Dog,也不是GunDog,而是一个$Proxy0的东西,这是神马??下面我们跟随代码一步一步来解答,首先我们的GunDog调用了代理工厂的getProxy 方法(MyProxyFactory.getProxy(target);),这个方法中关键的就是Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), myHander); 通过查看JDK的API可知,Proxy是Java自带的动态代理类,而newProxyInstance方法主要作用是产生一个myHander实例,但是这个实例必须继承Proxy,并且实现代理对象的所有接口(target.getClass().getInterfaces()),这里可以看一下JDK源码,
public static Object newProxyInstance(ClassLoader loader,
  Class<?>[] interfaces,
  InvocationHandler h)
throws IllegalArgumentException
    {
if (h == null) {
    throw new NullPointerException();
}

/*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces);

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
    Constructor cons = cl.getConstructor(constructorParams);
    return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
    throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
    throw new InternalError(e.toString());
} catch (InstantiationException e) {
    throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
    throw new InternalError(e.toString());
}
}
从这段源代码可知,这个方法确实通过构造方法返回了一个myHander这个实例,通过DEBUG一下Test,发现调用代理工厂之后,那个dog已经不是狗了,而已经成为MyInvokationHander(myHander的对象)了,但是为何它的名字叫“$Proxy0”呢,通过继续查看JDK的Proxy类,发现了如下逻辑:
try {
    String proxyPkg = null; // package to define proxy class in

    /*
     * Record the package of a non-public proxy interface so that the
     * proxy class will be defined in the same package.  Verify that
     * all non-public proxy interfaces are in the same package.
     */
    for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
    String name = interfaces[i].getName();
    int n = name.lastIndexOf('.');
    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
    if (proxyPkg == null) {
proxyPkg = pkg;
    } else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
    "non-public interfaces from different packages");
    }
}
    }

    if (proxyPkg == null) { // if no non-public proxy interfaces,
proxyPkg = ""; // use the unnamed package
    }

    {
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
    num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
这段逻辑就是产生代理类名称的逻辑,proxyClassNamePrefix是Proxy的一个静待常量
  private final static String proxyClassNamePrefix = "$Proxy";由于此处,我们所有类都在一个package中,所以proxyPkg为空,这又是第一个代理类,所以num为0,所以我们产生这个代理类就就叫做“$Proxy0”。
下面又有一个疑问,既然代理工厂产生的是一个叫做$Proxy0的MyInvokationHander实例,那它为什么又回有GunDog的info()和run()方法呢??
上面已经说了,Proxy 的newProxyInstance方法会要求产生一个实现继承Proxy,实现被代理类实现的所有接口的实例,比如下面:
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), myHander)
它产生的就是一个myHander实例,该实例继承Proxy,实现了target实现的所有接口(target.getClass().getInterfaces()),而通过DEBUG代码发现,每次执行代dog.info();时,代码都会走到MyInvokationHander.invoke(Object proxy, Method method, Object[] args)方法中。所以我们可以猜测,这个方法Proxy.newProxyInstance其实在内存中产生了这样一个代理类:该代理类有被代理类的所有方法(被代理类肯定已经实现了被代理类实现的接口中的方法),这些方法内部再通过invoke方法调用被代理类的方法(有点绕口)。例如下面就是猜想的,在内存中的代理类:
package com.yf.designpattern.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvokationHander implements InvocationHandler {
	private Object target;

	public void setTarget(Object target) {
		this.target = target;
	}

	public void info() throws SecurityException, NoSuchMethodException,
			Throwable {
		this.invoke(target, this.target.getClass().getDeclaredMethod("info",
				null), null);
	}

	public void run() throws SecurityException, NoSuchMethodException,
			Throwable {
		this.invoke(target, this.target.getClass().getDeclaredMethod("run",
				null), null);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		TxUtil tu = new TxUtil();
		tu.beginTx();
		Object result = method.invoke(target, args);
		tu.endTx();
		return result;
	}

} 

当然这是本人猜测的一个类,实际在内存中并不一定就是上面这个样子。
从上面可以看出,采用动态代理可以将代理类和进行解耦,代理类已经不再关心被代理类时什么,它只关心需要给被代理的类增强那些功能。
总结:使用动态代理的一般步骤如下:
1、 创建代理类,代理类需要实现InvocationHandler接口;
2、 创建代理类工厂(也可以不创建),该工厂通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException方法创建代理对象(也可以通过Proxy.getProxyClass(loader, interfaces).getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });)方法创建);
3、 将被代理对象传给代理类和代理类工厂即可。
附件中是JDK的部分源码和例子的中源代码
分享到:
评论

相关推荐

    设计模式之动态代理与spring的aop编程

    动态代理和Spring AOP(面向切面编程)是两个关键的设计概念,它们在实现高效、模块化的系统中起着至关重要的作用。本文将深入探讨这两个概念,并结合实际应用进行解析。 首先,让我们理解什么是动态代理。动态代理...

    动态代理设计模式 日志和源码

    动态代理设计模式是一种在运行时创建代理对象的技术,它允许我们为现有的对象提供额外的功能,如日志记录、性能监控、事务管理等,而无需修改原对象的代码。这种模式在Java和许多其他编程语言中都有应用,尤其在...

    Java设计模式——代理设计模式(静态代理和动态代理)

    代理设计模式分为静态代理和动态代理两种类型。 ### 静态代理 静态代理是在编译时就已经确定了代理关系,代理类和真实类的关系是硬编码在代理类中的。下面我们将详细介绍静态代理的实现方式: 1. **定义接口**:...

    动态代理设计模式

    详细而又简单的讲述了java动态代理设计模式

    代理模式之动态代理

    代理模式是一种设计模式,它允许在不修改原有对象的情况下,为对象添加新的功能或行为。在Java编程中,代理模式有静态代理和动态代理两种实现方式。本文将主要探讨动态代理,这是一种更灵活且适用于多种场景的技术。...

    动态代理设计模式-源码

    动态代理设计模式是一种在运行时创建对象代理的技术,它允许我们为现有的对象提供额外的功能,而无需修改原对象的代码。这种模式的核心是接口和代理类,通过接口,代理类可以与原对象进行通信,同时在代理类中添加...

    动态代理设计模式详解.pdf

    动态代理设计模式是一种编程技术,它允许在运行时创建具有相同接口的新对象,该对象能够对原对象的行为进行增强或扩展。在这个模式中,代理对象作为客户端与目标对象之间的中介,代理可以添加额外的功能,如日志记录...

    java静态代理、动态代理、装饰设计模式

    在Java编程中,代理模式是一种常见的设计模式,它允许我们为一个对象提供一个代理以控制对这个对象的访问。代理模式通常用于增加额外的功能或在访问原对象时进行额外的操作,比如日志记录、安全控制、性能统计等。...

    优秀的设计模式示例-动态代理模式

    动态代理模式是一种在运行时创建代理对象以控制或扩展原有对象行为的设计模式。它允许我们为现有的对象提供一种代理以增强其功能,而无需修改原对象的代码。动态代理模式通常用于实现对目标对象的额外操作,如日志...

    java设计模式(工厂模式,动态代理,责任链模式……)实例源码

    这个压缩包中包含的实例源码涵盖了多种设计模式,包括工厂模式、动态代理和责任链模式。让我们深入探讨这些设计模式的核心概念、用途以及它们在实际编程中的应用。 1. 工厂模式:这是最基础的设计模式之一,属于...

    设计模式之代理模式demo

    代理模式是一种常用的设计模式,它在软件开发中起到了中介或者代表的作用。代理模式的主要目的是为其他对象提供一种代理以控制对这个对象的访问。通过引入代理,我们可以增加新的功能,如缓存、日志记录、访问控制等...

    设计模式之_代理模式

    **代理模式**是一种常用的设计模式,它在软件开发中扮演着重要的角色,特别是在对象的访问控制、性能优化、安全策略等方面。代理模式的核心思想是为一个对象提供一个替身,这个替身即代理对象,代理对象可以控制对原...

    java常用设计模式及JDK与CGLIB实现动态代理区别(源码)

    java常用设计模式及JDK与CGLIB实现动态代理区别(源码) /** * 使用cglib动态代理 * @author * */ public class BookFacadeProxy implements MethodInterceptor{ private Object target; @Override public...

    设计模式之代理模式

    **设计模式之代理模式** 代理模式是软件设计模式中的一种结构型模式,它在对象交互过程中引入一个代理,代理对象负责控制对原对象的访问。这种模式的主要目的是为了解耦,提供一种替代真实对象的方式,使得客户端...

    java模式设计-代理模式之动态代理.ppt

    代理模式是一种设计模式,它允许我们在不修改原有对象的情况下,为现有对象添加额外的功能或控制。在Java中,动态代理是代理模式的一种实现方式,它允许我们在运行时创建代理对象,这种方式比静态代理更加灵活。动态...

    .net实现设计模式之代理模式

    代理模式是一种常用的设计模式,它在软件工程中扮演着重要的角色,特别是在.NET框架下。代理模式的核心思想是为一个对象提供一个替代品或代表,这个替代品能够控制对原对象的访问,使得客户端代码可以通过代理对象与...

    java 设计模式之代理模式(Proxy Pattern)实现代码及设计详解:动态代理模式、静态代理模式

    Java设计模式是软件开发中的重要概念,它提供了一套通用的解决方案模板,使得代码更加可复用、可扩展。在这些模式中,代理模式(Proxy Pattern)是...对于Java开发者来说,掌握设计模式是提升专业技能的关键步骤之一。

    JAVA设计模式之代理模式实例

    代理模式是设计模式的一种,它提供了一种对目标对象进行增强或者控制访问的方式。在本实例中,我们将深入探讨Java中的代理模式及其应用。 代理模式的核心思想是为一个对象创建一个代理对象,这个代理对象在客户端和...

Global site tag (gtag.js) - Google Analytics