Java动态代理(Dynamic Proxy)

1.Proxy定义

Dynamic Proxy API是在Java1.3中引入的。Proxy强制对象方法通过代理对象(Proxy Object)调用。代理对象被声明客户端对象没有迹象表明他们有代理对象的实例。
一些常用的代理有:access proxy、facade、remote proxy和virtual proxy。
  • access proxy被用于在访问服务器或提供数据的对象时实施安全策略。
  • facade是连接多个底层对象的单一接口。
  • remote proxy被用于隐藏底层对象时远程的客户对象。
  • virtual proxy被用于延迟执行实际对象的实例化。

代理也是一个基础的设计模式,被经常用于编程中。但是,它的一个缺点是专一性或者代理和它的底层对象紧密耦合。


2.Dynamic Proxies

为了使用动态代理,你有两步要做:第一,必须有一个代理接口,这个代理接口是要被代理类实现的接口;第二,需要有一个代理类的实例。有意思地是,你可以有一个实现了多个接口的代理类。但是,当你创建动态代理时,有几个约束条件要记住。以下是对于你实现接口的几个约束条件:
  1. 代理接口必须是一个接口。换句话说,它不能是一个类或者一个抽象类。
  2. 传给代理构造方法的接口数组必须不包含同一个接口的副本,即你不能同时实现一个接口两次。例如,{IPerson.class, IPerson.class}是非法的,但是{IPerson.class, IEmployee.class}(IEmployee继承了IPerson)是合法的。调用构造方法的代码应该检查过滤掉副本。
  3. 在构造调用ClassLoader期间,所有的接口必须对指定的ClassLoader是可见的。另外,这个ClassLoader必须能够为该代理加载这些接口。
  4. 所有非公有接口必须在同一个包中。你不能有一个private接口在com.xyz中,而它的代理类在com.abc中。你也不能用一个普通类实现其他包中的非公用接口。
  5. 代理接口不能有冲突的方法。你不能有两个拥有相同参数但返回值类型不同的方法。例如,public void foo()和public String foo()不能定义在同一个类里,因为他们有相同的签名,只是返回值不同而已。
  6. 生成的代理类不能超过虚拟机的限制。比如说,被实现的接口的数量的限制。

3.示例

上面说了一大堆,现在看下代码。创建动态代理类,你需要实现 java.lang.reflect.InvocationHandler接口:
public class MyDynamicProxyClass implements InvocationHandler {
	Object obj;
	
	public MyDynamicProxyClass(Object obj) {
		this.obj = obj;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		try {
			String methodName = method.getName();
			if(methodName.startsWith("get")){
				String name = methodName.substring(methodName.indexOf("get") + 3);
				return method.invoke(obj, args);
			}
			else if(methodName.startsWith("set")){
				String name = methodName.substring(methodName.indexOf("set") + 3);
				method.invoke(obj, args);
				return null;
			}
			else if(methodName.startsWith("is")){
				String name = methodName.substring(methodName.indexOf("is") + 2);
				return null;
			}
				
			return null;
		} catch (Exception e) {
			throw e;
		}
	}
}

假设我们已经有了一个IPerson接口和它的实现类Person。那么,我们来下如何使用动态代理:
IPerson person = new Person();
IPerson proxy = (IPerson) Proxy.newProxyInstance(person.getClass().getClassLoader(), 
				new Class[] {IPerson.class},
				new MyDynamicProxyClass(person));

没错,这样就创建了动态代理。代码看上去很丑陋对吗?如果每次都这样写很揪心不是吗?实际上,每次都这样写着实很丑陋。那么,我们换一种方式,把它给包装起来:
public class ViewProxy implements InvocationHandler{

	private Map map = new HashMap();
	private Object obj;
	
	public ViewProxy(Object obj) {
		this.obj = obj;
	}
	
	public static Object newInstance(Object obj, Class[] interfaces){
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
										interfaces, 
										new ViewProxy(obj));
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		String methodName = method.getName();
		if(methodName.startsWith("get")){
			String name = methodName.substring(methodName.indexOf("get") + 3);
//			return map.get(name);
			return method.invoke(obj, args);
		}
		else if(methodName.startsWith("set")){
			String name = methodName.substring(methodName.indexOf("set") + 3);
//			map.put(name, args[0]);
			method.invoke(obj, args);
			return null;
		}
		else if(methodName.startsWith("is")){
			String name = methodName.substring(methodName.indexOf("is") + 2);
			return map.get(name);
		}
			
		return null;
	}

}

IPerson:
public interface IPerson {
	
	public String getName();
	public String getAddress();
	public void setName(String name);
	public void setAddress(String address);
}

Person:
public class Person implements IPerson {
	
	private String name;
	private String address;

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return name;
	}

	@Override
	public String getAddress() {
		// TODO Auto-generated method stub
		return address;
	}

	@Override
	public void setName(String name) {
		// TODO Auto-generated method stub
		this.name = name;
	}

	@Override
	public void setAddress(String address) {
		// TODO Auto-generated method stub
		this.address = address;
	}

}

main方法:
public static void main(String[] args) {
        IPerson person = new Person();
	IPerson proxy = (IPerson) ViewProxy.newInstance(person, new Class[] {IPerson.class});
	proxy.setName("Bob Jones");
	proxy.setAddress("Chicago");;
		
	System.out.println(proxy.getName() + ", " + proxy.getAddress());
}


你可能感兴趣的:(java,动态代理,proxy,dynamic)