在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到了中介作用,这不仅仅使用代理模式,还可以实现适配器模式、装饰模式等。
代理对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
传统式上Java代理对象分为静态代理和动态代理,其实本质上是一样只是两种不同的编码方式,后者更有通用性。
静态代理
先来看看静态代理,可以认为代理只是对真实类的一个包装,就想去办证大厅办证一样,你代办除了在大厅办事流程一样外,还需要做些额外的处理,比如还需要自己的一些复印件之类的。我们来代码示例一下:
package net.oseye; public class ProxyDemoApp { public static void main(String[] args) { //代理对象的使用 new HelloProxy(new Hello()).sayHello("oseye");; } } /** * 真实类 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ class Hello{ public void sayHello(String userName) { System.out.println("Hello,"+userName); } } /** * 代理类 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ class HelloProxy{ private Hello hello; public HelloProxy(Hello hello){ this.hello=hello; } public void sayHello(String userName) { System.out.println("代理模式开始...."); this.hello.sayHello(userName); System.out.println("代理模式结束...."); } }
但通常我们会先定义一个接口,真实类和代理类都实现这个接口:
package net.oseye; public class ProxyDemoApp { public static void main(String[] args) { //代理对象的使用 new HelloProxy(new Hello()).sayHello("oseye");; } } /** * 接口 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ interface IHello{ public void sayHello(String username); } /** * 真实类 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ class Hello implements IHello{ public void sayHello(String userName) { System.out.println("Hello,"+userName); } } /** * 代理类 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ class HelloProxy implements IHello{ private Hello hello; public HelloProxy(Hello hello){ this.hello=hello; } public void sayHello(String userName) { System.out.println("代理模式开始...."); this.hello.sayHello(userName); System.out.println("代理模式结束...."); } }
由以上可以看得出这种模式非常适合代理、适配、装饰等模式。然而每一个代理类只能为一个接口服务,如果非常多的代理不仅会让类快速膨胀而且造成很多重复代码,因此Java提供了动态代理接口来解决这个问题。
动态代理
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
- Interface InvocationHandler:该接口中仅定义了一个方法
Object:invoke(Object obj,Method method, Object[] args)
- Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:
Protected Proxy(InvocationHandler h):使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例;
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。该代理类将由指定的类加载器定义,并将实现提供的所有接口。如果类加载器已经定义了具有相同排列接口的代理类,那么现有的代理类将被返回;否则,类加载器将动态生成并定义这些接口的代理类;
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });
package net.oseye; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyDemoApp { public static void main(String[] args) { //代理对象的使用 IHello hello=(IHello)new ProxyDemo().bind(new Hello()); hello.sayHello("oseye"); } } /** * 接口 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ interface IHello{ public void sayHello(String username); } /** * 真实类 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ class Hello implements IHello{ public void sayHello(String userName) { System.out.println("Hello,"+userName); } } /** * 动态代理类 * ProxyDemoApp.java:TempTest * Jul 9, 2014 * @author kevin.zhai */ class ProxyDemo implements InvocationHandler{ private Object target; public Object bind(Object object){ this.target=object; return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("代理模式开始...."); result=method.invoke(this.target, args); System.out.println("代理模式结束...."); return result; } }
但JDK的代理API仍有一个缺陷:必须针对接口,如果没有接口就没办法了!这是可以考虑使用一个开源的框架cglib。