代理模式
java设计模式中有一个设计模式很重要,就是代理模式(proxy),代理模式一般涉及到的角色有
抽象角色:声明真实对象和代理对象的共同接口。
真实角色:用户要访问的最终对象,是代理角色所代表的真实对象。
代理角色:用户可通过代理角色访问真实角色,代理角色中含有真实角色的引用,可通过该引用操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时候都能代替真实对象,代理对象可以在执行真实对象操作时,附加其他操作,相当于在实现真实对象的功能额外附加功能。
代理角色在生活中随处可见,通过代理角色可实现客户与真实角色的分离,就好像学生在生活中要找兼职,要通过中介来找,真正提供工作的不是中介,而是那些你做兼职的公司,中介相当于代理角色,而找兼职的你是客户,你兼职的公司是真实角色。
下面是一个简单的静态代理的例子
1 package proxy; 2 //定义的抽象类,代理角色跟真实角色都要继承它 3 public abstract class Subject 4 { 5 public abstract void output(); 6 }
1 package proxy; 2 //真实角色,实现Subject抽象类的功能 3 public class RealSubject extends Subject 4 { 5 public void output() 6 { 7 System.out.println("from real subject"); 8 } 9 }
1 package proxy; 2 //代理角色 3 public class ProxySubject extends Subject 4 { 5 private RealSubject realSubject;//含有真实角色的引用 6 7 @Override 8 public void output() 9 { 10 this.preOutput();//调用真实角色的方法前执行的方法 11 if(null == realSubject) 12 { 13 realSubject = new RealSubject(); 14 } 15 realSubject.output();//调用真实角色的方法 16 this.postOutput();//调用真实角色后执行的方法 17 } 18 19 public void preOutput() 20 { 21 System.out.println("pre output"); 22 } 23 public void postOutput() 24 { 25 System.out.println("post output"); 26 } 27 }
1 package proxy; 2 //访问的客户 3 public class Client 4 { 5 public static void main(String[] args) 6 { 7 Subject subject = new ProxySubject();//生成代理实例 8 subject.output();//调用真实角色的方法 9 } 10 }
通过代理角色实现了各个角色的各自完成自身的功能,真实角色通过代理角色与客户联系在一起,但该代理模式有其缺点,就是真实角色必须是事先已经存在的,并将其作为代理对象的内部属性,但实际应用时,一个真实角色必须对应一个代理角色,就是说每次定义一个真实角色就要定义一个代理角色,如果大量使用会导致类的急剧增长,此外,如果事先不知道真实角色,就不能使用代理了。不过这个问题可以通过java的动态代理(DynamicProxy)来解决。
动态代理类也是位于java.lang.reflect包下,主要涉及到以下两个类
1、Interface InvocationHandler:该接口中只定义了一个方法public object invoke(Object obj,Method method,Object[] args) 在实际使用中第一个参数一般是指代理类,method是被代理的方法,args是该方法的参数数组,这个抽象方法会在代理类中动态实现。每一个代理类实例会跟一个InvocationHandler相关,这是jdk里面写的
2、Proxy:该类是动态代理类,作用的话跟ProxySubject类似,主要内容有
1)protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
2)static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
3)staticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口法)
动态代理就是在运行时生成class,在生成它时要给它提供一组interface,你可以实现任何一个接口,生成动态代理实例时要提供一个handler(实现了InvocationHandler接口),由它接管实际的工作,所以在使用动态代理类时,我们必须要实现InvocationHandler接口。创建动态代理的步骤如下
1、创建被代理类以及接口
2、创建一个实现接口InvocationHandler的类,并实现invoke方法
3、通过Proxy的静态方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler handler)创建一个代理
4、通过创建的代理来调用方法
下面通过一个实例来说明如何创建动态代理
1 package dynamicProxy; 2 //定义被代理类要实现的接口 3 public interface Subject { 4 public void output(String str); 5 6 }
1 package dynamicProxy; 2 //实现接口的真实对象 3 public class RealSubject implements Subject { 4 public void output(String str) 5 { 6 System.out.println(str+" from real"); 7 } 8 }
1 package dynamicProxy; 2 //该类实现了InvocationHandler接口,并重写了了invoke方法 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class DynamicSubject implements InvocationHandler{ 7 private Object object = new Object();//用Object可以传递任何要代理的类 8 public DynamicSubject(Object obj) 9 { 10 this.object = obj; 11 } 12 13 @Override 14 public Object invoke(Object proxy, Method method, Object[] args) 15 throws Throwable { 16 System.out.println(method); 17 method.invoke(object, args);//等价于realsubject的output方法 object为要代理的真实对象,args为要传递的参数 18 return null; 19 } 20 21 }
1 package dynamicProxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Proxy; 5 6 public class Client { 7 public static void main(String[] args) { 8 9 Subject sub = new RealSubject(); 10 InvocationHandler handler = new DynamicSubject(sub); 11 12 Class<?> classType = handler.getClass(); 13 //生成代理,并把方法调用转向InvocationHandler,这里生成的subject类宣称实现了sub.getClass().getInterfaces的所有接口 14 Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(), sub.getClass().getInterfaces(),handler); 15 subject.output("CIACs");//从这里传递参数,和方法到DynamicSubject的invoke 16 17 System.out.println(subject.getClass().getName());//打印输出动态生成代理类的名字 18 } 19 }
输出结果:
动态代理是可用在调试和远程方法调用中,客户是通过代理类来调用真实对象的方法。一个代理类可生成很多代理实例,实现了一对多的效果。