`

Java动态代理

阅读更多
Java提供了动态代理,可以完成AOP和装饰模式的功能,主要的Proxy类和InvocationHandler接口:
1、Proxy类
public class Proxy implements java.io.Serializable {
//...
    public static Class getProxyClass( ClassLoader loader,
            Class[] interfaces )throws IllegalArgumentException;

    public static Object newProxyInstance( ClassLoader loader,
       Class[] interfaces,InvocationHandler h )throws IllegalArgumentException;
    public static boolean isProxyClass( Class cl );
    public static InvocationHandler getInvocationHandler( Object proxy )
throws IllegalArgumentException;
}

Proxy类提供了一些工具方法:
getProxyClass得到代理类,如果不存在则动态创建。
newProxyInstance创建一个代理实例。
isProxyClass判断是否是一个代理类。
getInvocationHandler得到InvocationHandler。
一般先调用isProxyClass,判断是,然后再使用getInvocationHandler
参数:loader,创建对象需要classLoader,创建proxy也需要,一般被代理
真实对象得到:realObj.getClass().getClassLoader();
interfaces,代理和真实对象实现的接口。
创建一个代理一般使用以下两种方式:
方法一:
Proxy cl = getProxyClass( SomeInterface.getClassLoader(),
Class[]{SomeInterface.class} );
Constructor cons = cl.getConstructor( new Class[]{InvocationHandler.class} );
Object proxy = cons.newInstance( new Object[] { new SomeIH( obj ) } );

方法二:
Object proxy = Proxy.newProxyInstance( SomeInterface.getClassLoader(),
Class[]{SomeInterface.class},
new SomeIH( obj ) );

2、InvocationHandler接口:
public interface InvocationHandler {
   public Object invoke( Object proxy, Method method, Object[] args )
 throws Throwable;
}

代理对象通过这个接口来将真实的对象联系起来。过程是:
代理对象直接调用InvocationHandler 的invoke方法,把自己作为proxy参数,调用的函数
作为method参数,调用函数的参数作为args传递过来,我们只需要在invoke方法中做一些事情,然后再调用被代理对象的方法就可以了。
我们以一个例子说明:
跟踪函数的调用过程:
函数执行之前和之后打印出跟踪信息:
实现InvocationHandler接口:
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class MethodTracker implements InvocationHandler{;
	private Object target;
	private PrintWriter out;
	
	private MethodTracker(Object obj, PrintWriter out){
		this.target = obj;
		this.out = out;
	}
	
	public static Object createProxy(Object obj, PrintWriter out){
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new MethodTracker(obj,out));
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result = null;
		try{
			out.println(method.getName() + "(...) called");
			result = method.invoke(target, args);
		}catch(InvocationTargetException e){
			out.println(method.getName() + " throws " + e.getCause());
			throw e.getCause();
		}
		out.println(method.getName() + " returns");
		return result;
	}
	
}

我们测试一下:
import java.io.PrintWriter;

interface IHelloWorld{
	void hello();
}

class HelloWorld implements IHelloWorld{
	private String name = "world";
	
	public void hello(){
		System.out.println("Hello," + name);
	}
	
	public void setName(String name){
		this.name = name;
	}
}

public class TestTracker {
	public static void main(String[] args) {
		IHelloWorld tc =  (IHelloWorld) MethodTracker.createProxy(new HelloWorld(),new PrintWriter(System.out,true));
		tc.hello();
	}
}

Java动态代理的缺点是只能对接口进行代理,无法代理class。
3.高级进阶:
如果我们想象拦截器那样一层一层的拦截,也就是形成一个代理链,上面的实现可能会出现问题,比如我们想有个同步调用方法的代理,如果按照上面的实现
IHelloWorld tc =  (IHelloWorld) MethodSynchronizer.createProxy(
MethodTracker.createProxy(new   HelloWorld(),new PrintWriter(System.out,true))
);
将是错误的,因为他使用的同步对象是内层代理,而不是真实要被代理的对象。
要完成上面的功能,我们可以通过下面实现来达到:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public abstract class  InvocationHandlerBase implements InvocationHandler{
	protected Object nextTarget;
	protected Object realTarget = null;
	
	public InvocationHandlerBase(Object target){
		nextTarget = target;
		if(nextTarget != null){
			realTarget = findRealTarget(nextTarget);
			if(realTarget == null){
				throw new RuntimeException("findRealTarget failure");
			}
		}
	}
	
	protected final Object getRealTarget(){
		return realTarget;
	}
	
	protected static final Object findRealTarget(Object t){
		if(! Proxy.isProxyClass(t.getClass()))
			return t;
		InvocationHandler ih = Proxy.getInvocationHandler(t);
		if(InvocationHandlerBase.class.isInstance(ih)){
			return ((InvocationHandlerBase)ih).getRealTarget();
		}else{
			try{
				Field f = findField(ih.getClass(), "target");
				if(Object.class.isAssignableFrom(f.getType())&&
						! f.getType().isArray()){
					f.setAccessible(true);
					Object innerTarget = f.get(ih);
					return findRealTarget(innerTarget);
				}
				return null;
			}catch(Exception e){
				return null;
			}
		}
	}
	
	public static Field findField(Class<?> cls, String name) throws NoSuchFieldException{
		if(cls != null){
			try{
				return cls.getDeclaredField(name);
			}catch(NoSuchFieldException e){
				return findField(cls.getSuperclass(),name);
			}
		}else{
			throw new NoSuchFieldException();
		}
	}
}


注意,这里我们约定了实现InvocationHandler接口的类使用target字段来保存被代理的对象,通过继承InvocationHandlerBase便可以达到所说的效果:
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class MethodSynchronizer extends InvocationHandlerBase{
	public static Object createProxy(Object obj){
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
				obj.getClass().getInterfaces(), new MethodSynchronizer(obj));
	}
	private MethodSynchronizer(Object obj){
		super(obj);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		Object result = null;
		synchronized( this.getRealTarget() ){
			result = method.invoke(nextTarget, args);
		}
		return result;
	}
}

测试代码:
import java.io.PrintWriter;

interface IHelloWorld{
	void hello();
}

class HelloWorld implements IHelloWorld{
	private String name = "world";
	
	public void hello(){
		System.out.println("Hello," + name);
	}
	
	public void setName(String name){
		this.name = name;
	}
}

public class TestTracker {
	public static void main(String[] args) {
		IHelloWorld tc =  (IHelloWorld) MethodSynchronizer.createProxy(
				MethodTracker.createProxy(new HelloWorld(),new PrintWriter(System.out,true))
		);
		tc.hello();
	}
}
分享到:
评论
1 楼 kuangfengkuang 2012-06-26  
zzzzz

相关推荐

Global site tag (gtag.js) - Google Analytics