【设计模式】代理模式
代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问,因为在某些情形下,我们不能直接或不方便直接访问某些类,或者我们需要对访问的类做一些特殊的处理,这时便可以采用代理模式了。
代理模式分为动态代理和静态代理,其中静态代理比较简单,它要求代理类和被代理类必须事先已经存在,代理类的接口和所代理方法都已明确指定。如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数。动态代理可以让系统能够根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法。
下面先来一个简单的静态代理的例子(这里把所有类放在同一个java文件中,下同):
interface Subject { public void fun(); } class SubjectImpl implements Subject { public void fun() { System.out.println("this is subject impl"); } } class Proxy implements Subject { private Subject subject = null;//将需要被代理的类设置为类属性 public void fun() { before();//do something before.. if(subject == null) { subject = new SubjectImpl();//初始化 subject.fun();//实际调用的是被代理类中的fun方法 } after();//do something after… } private void before() { System.out.println("before......."); } private void after() { System.out.println("after........"); } } class Main { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.fun(); } }
例子比较简单,就不分析了。
下面我们看下动态代理。
从JDK 1.3开始,Java语言提供了对动态代理的支持,Java语言实现动态代理时需要用到位于java.lang.reflect包中的一些类,现简要说明如下:
(1) Proxy类
Proxy类提供了用于创建动态代理类和实例对象的方法,它是所创建的动态代理类的父类,它最常用的方法如下:
· public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)。
· public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,方法中第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类。
(2) InvocationHandler接口
InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)。在该接口中声明了如下方法:
· public Object invoke(Objectproxy, Method method, Object[] args):该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。
动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时,调用请求会将请求自动转发给InvocationHandler对象的invoke()方法,由invoke()方法来实现对请求的统一处理。
具体由如下几种方式:
1. 常规使用:
class InvocationHandlerImpl implements InvocationHandler { private Object target = null; InvocationHandlerImpl(Object target) { this.target = target; } public Object invoke(Object proxy,Method method,Object[] args)throws Throwable { Object obj = null; if(method.getName().equals("fun")) { System.out.println("before..."); obj = method.invoke(target,args); System.out.println("after..."); } else { obj = method.invoke(target,args); } return obj; } } class Main//测试 { public static void main(String[] args) { SubjectImpl subimpl = new SubjectImpl(); InvocationHandlerImpl handler = new InvocationHandlerImpl(subimpl); Subject sub = (Subject)Proxy.newProxyInstance(subimpl.getClass().getClassLoader(), subimpl.getClass().getInterfaces(),handler); sub.fun(); } }
2. 通过匿名内部类方式使用:
class Main { public static void main(String[] args) { final SubjectImpl sub = new SubjectImpl(); Subject su = (Subject)Proxy.newProxyInstance(sub.getClass().getClassLoader(), sub.getClass().getInterfaces(),new InvocationHandler() { public Object invoke(Object proxy,Method method,Object[] args)throws Throwable { Object obj = null; if(method.getName().equals("func")) { System.out.println("before..."); obj = method.invoke(sub,args); System.out.println("after..."); } else { obj = method.invoke(sub,args); } return obj; } } ); su.fun(); } }
3.在InvocationHandler实现类内部创建一个bind方法,用来返回target类实例:
class InvocationHandlerImpl implements InvocationHandler { private Object target = null; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } public Object invoke(Object proxy, Method method,Object[] args) throws Throwable { Object obj = null; if(method.getName().equals("fun")) { before(); obj = method.invoke(target,args); after(); } else { obj = method.invoke(target,args); } return obj; //return null; } private void before() { System.out.println("before..."); } private void after() { System.out.println("after..."); } } class Main { public static void main(String[] args) { InvocationHandlerImpl proxy = new InvocationHandlerImpl(); Subject sub = (Subject)proxy.bind(new SubjectImpl()); sub.fun(); } }