Java的动态代理实现

1. 问题

在许多情况下,我们需要使用代理模式来解决问题。如下为代理模式的类图。


 

 

 

如下为使用 Java 实现代理模式的一个例子:

 

package com.demo.dynamicproxy;

public interface IHello {

	public void sayHello() ;
}

 

package com.demo.dynamicproxy;

public class HelloProxy implements IHello {

	private IHello speaker = new HelloSpeaker();
	
	public HelloProxy(IHello hello) {
		this.speaker = hello;
	}
	
	private void preRequest() {
		System.out.println("prepare");
	}
	
	private void postRequest() {
		System.out.println("applaud");
	}
	
	@Override
	public void sayHello() {
		preRequest();
		this.speaker.sayHello();
		postRequest();

	}

}

 

package com.demo.dynamicproxy;

/**
 * @author Xiechangming
 * 
 */
public class HelloSpeaker implements IHello {

	@Override
	public void sayHello() {
		System.out.println("Hello,world");

	}

}

 

package com.demo.dynamicproxy;

public class ProxyTest {

	public static void main(String...args){
		
		//static proxy
		HelloProxy proxy = new HelloProxy(new HelloSpeaker());
		
		proxy.sayHello();
		
		//dynamic proxy
		HelloHandler handler = new HelloHandler();
		
		IHello dynamicProxy = (IHello)handler.bind(new HelloSpeaker());
		
		dynamicProxy.sayHello();
	}
}

 

 

正如你所见,需要显示地实现一个代理类,这就是静态代理模式。 如果需要被代理的方法很多,且代理的逻辑相同,势必要为每种方法实现一个代理类,在程序规模稍大是就会产生很多代理类。该如何解决呢?

2. 解决方案

JDK1.3 中,引入了动态代理类,用来解决相同的代理逻辑可以为多种被代理类重复使用。 JDK 动态代理中包含一个类和一个接口:  
InvocationHandler
接口:
 
public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明:
 
Object proxy
:指被代理的对象。
 
Method method
:要调用的方法
 
Object[] args
:方法调用时所需要的参数 

该方法就是实现代理逻辑的地方,类似于 ProxySubject 

Proxy 类:  
Proxy
类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
 
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
                               throws IllegalArgumentException 
参数说明:
 
ClassLoader loader
:类加载器
 
Class<?>[] interfaces
:得到全部的接口
 
InvocationHandler h
:得到 InvocationHandler 接口的子类实例  

该方法返回真实的代理类的一个对象,该代理类是 JDK 动态生成的。如下是一个动态代理模式的实例:

package com.demo.dynamicproxy;

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

public class HelloHandler implements InvocationHandler {

	private Object delegate;
	
	public Object bind(Object delegate) {
		this.delegate = delegate;
		
		return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
	}
	private void preRequest() {
		System.out.println("prepare");
	}
	
	private void postRequest() {
		System.out.println("applaud");
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		preRequest();
		method.invoke(this.delegate, args);
		postRequest();
		return null;
	}

}

 

 

 

 

package com.demo.dynamicproxy;

public class ProxyTest {

	public static void main(String...args){
		
		//static proxy
		HelloProxy proxy = new HelloProxy(new HelloSpeaker());
		
		proxy.sayHello();
		
		//dynamic proxy
		HelloHandler handler = new HelloHandler();
		
		IHello dynamicProxy = (IHello)handler.bind(new HelloSpeaker());
		
		dynamicProxy.sayHello();
	}
}

 

 

3.  实现原理

通过查看 Proxy 的源代码,可以对动态代理模式的实现原理有一个大致的了解 :

下面是 newProxyInstance 的实现,从中可以看出关键是调用 getProxyClass 方法 获得最终的代理类,该代理类是动态生成

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());
	}
}

 

 

 

 

 

 下面是 getProxyClass 的主要实现

public static Class<?> getProxyClass(ClassLoader loader, 
                                         Class<?>... interfaces)
	throws IllegalArgumentException
    {
	…

	Class proxyClass = null;//声明要动态生成的类
…
String proxyName = proxyPkg + proxyClassNamePrefix + num; //定义动态生成的类的名字
		/*
		 * Verify that the class loader hasn't already
		 * defined a class with the chosen name.
		 */

		/*
		 * Generate the specified proxy class.
		 */
		byte[] proxyClassFile =	ProxyGenerator.generateProxyClass(
		    proxyName, interfaces);//调用generateProxyClass方法生成动态类的.class文件的二进制,Sun的内部实现
		try {
		    proxyClass = defineClass0(loader, proxyName,
			proxyClassFile, 0, proxyClassFile.length);//.class文件的二进制导入到JVM,获得类的实例。这是一个本地方法。
		} catch (ClassFormatError e) {
		    /*
		     * A ClassFormatError here means that (barring bugs in the
		     * proxy class generation code) there was some other
		     * invalid aspect of the arguments supplied to the proxy
		     * class creation (such as virtual machine limitations
		     * exceeded).
		     */
		    throw new IllegalArgumentException(e.toString());
		}
	    }
	    // add to set of all generated proxy classes, for isProxyClass
	    proxyClasses.put(proxyClass, null);

	}
 …
	return proxyClass;//返回生成的类。
    }


 

 

 

 

4. 不足

该代理模式要求被代理的类必须要实现接口,如果被代理的类没有实现接口,则不能使用动态代理模式。那么有什么更好的方式来代理没有实现接口的类的方法呢?这就要用到开源软件 CBLib 了。下面是是使用 CGLib 实现的例子:

package com.demo.dynamicproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

	private void preRequest() {
		System.out.println("prepare");
	}

	private void postRequest() {
		System.out.println("applaud");
	}

	public Object newProxyInstance(Object target) {
		Enhancer enhancer = new Enhancer();
		
		enhancer.setSuperclass(target.getClass());
		
		enhancer.setCallback(this);
		
		return enhancer.create();
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		preRequest();
		proxy.invokeSuper(obj, args);
		postRequest();

		return null;
	}

}
 
package com.demo.dynamicproxy;

public class ProxyTest {

	public static void main(String... args) {

//		// static proxy
//		HelloProxy proxy = new HelloProxy(new HelloSpeaker());
//
//		proxy.sayHello();
//
//		// dynamic proxy
//		HelloHandler handler = new HelloHandler();
//
//		IHello dynamicProxy = (IHello) handler.bind(new HelloSpeaker());
//
//		dynamicProxy.sayHello();

		// Cglib
		CglibProxy cglibProxy = new CglibProxy();
		
		IHello helloProxy = (IHello)cglibProxy.newProxyInstance(new HelloSpeaker());
		
		helloProxy.sayHello();

	}
}
 

CGLib 本质上是动态生成了一个被代理类的子类,并 Override 了被代理的方法,因此被代理的类中方法不能将方法定义为 final ,否则就类似于直接对被代理类的调用,就如实例中,只会输出“Hello,world"。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值