Java动态代理(Dynamic Proxy)

时间:2022-12-16 16:08:03

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