Java与模式-动态代理模式

来源:互联网 发布:我国2016车祸死亡数据 编辑:程序博客网 时间:2024/05/21 07:57

 代理模式

代理模式:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。使用代理模式创建代理对象,让代理对象控制某个对象的访问,被代理的对象可以是远程的对象或需要安全控制的对象。我觉得,在C语言中,指针就是一个代理对象,真正存储数据的是内存中的另外一块区域。Java提供了API可以生成动态代理,所以把标准的代理模式称为静态代理模式。在静态代理模式中有三类角色,理解了这三类角色也就理解了代理模式:

a.抽象对象:定义了真实角色和抽象角色的公共接口(可以是类,也可以是接口)Subject;

b.代理角色:代理角色内部包含有对真实角色的引用,通过这个引用去执行真实角色想要完成的任务;除此之外,代理角色可以完成其他的一些功能;

c.真实角色:实际要完成任务的角色,是我们始终要引用的对象。

代理角色和真实角色均实现了(或继承了)抽象角色。

写一段代码来举例:

//抽象角色,定义公共部分interface Subject {     public void request();}//真实角色class RealSubject implements Subject {    public void request() {        do something;   } }//抽象角色,内部保存真实角色的引用class ProxySubject implements Subject {    private RealSubject sub;        public ProxySubject(RealSubject obj) {        this.sub = obj;//获得真实角色的实例引用    }   public void request() {       sub.request();//通过真实角色的引用去执行最终要完成的任务  }}public static void main(String[] args) {Subject proxy = new ProxySubject();proxy.request();}

代理模式有很多中变形,这个很值得研究一番,不过今天我的重点是Java中的动态代理。

动态代理

上面的代理模式存在一个问题,那就是需要为不同的接口单独提供了一个代理类。现在有这样一个问题,系统中有10个类的方法在调用之前,需要进行安全验证,这10个类分别实现了不同的接口。这种情况恰好是代理模式发挥作用的地方,在真正的方法调用之前,现在代理类里面进行安全验证,如果通过了就去调用真正的方法,否则返回。这样既达到了安全验证的目的,又不违反开闭原则。可是,安全验证都是一样的,难道我们真的要去实现十个代理类,然后去做同样的安全验证吗?重复代码就意味着坏的气息啊。Java的动态代理就可以帮助我们搞定这件事情。动态代理,顾名思义就是动态的生成代理类,去代理服务。在看Java的动态代理之前,还是先分析一下如果要实现动态代理,需要哪些条件:

(1)在代理模式中,客户端实际上是在跟代理角色打交道,它以为它操作的是真实对象,但其实是代理对象,但是这种代理对客户端是透明的,客户端其实是面向接口编程,它只知有抽象接口,而不知具体的实现类。所以我们要定义抽象接口,真实角色和代理角色全部的都实现抽象接口,这样就实现了对客户端的透明代理。

(2)动态代理也只是代理,代理无非就是在真正的方法执行之前或者之后添加一些逻辑,但是主要的业务逻辑还需要真实角色去实现,所以必须要有真实角色。这个和静态代理模式是一样。我们还是需要按照业务逻辑去定义RealSubject。

(3)动态代理同样需要代理类,它和静态代理的区别是:静态的代理类是由程序员生成的,而动态代理类是动态生成的。但是,动态代理类的性质和普通的代理类是一样的,它都需要:a.实现抽象角色接口,由于它是动态生成的,所以不知道要实现哪些接口,所以它应该实现真实角色实现的所有接口;b.保存角色的引用;

前面两个条件跟静态代理模式是一样的,我们不用关心,我们需要弄明白的问题是:如果给动态代理类实现所需的接口以及它是怎样代理方法调用的?

Proxy来生成动态代理类

为了实现动态代理,Java提供了Proxy类和InvocationHandler接口,Proxy类用于生成动态代理类,InvocationHandler接口是代理真正发生的地方,它只有一个方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable,代理类所有的方法调用都会转到invoke()方法里,后面我们在说这个方法,先看Proxy类是如何生成动态代理类的。Proxy提供了如下的方法:

//返回动态代理类proxy关联的InvocationHandlerstatic InvocationHandler getInvocationHandler(Object proxy) //返回由loader加载,实现了interfaces接口的代理类static Class<?>getProxyClass(ClassLoader loader, Class<?>... interfaces)//判断cl是否是代理类static booleanisProxyClass(Class<?> cl) //返回由loader加载,实现了interfaces接口的动态类的实例,其关联的InvocationHandler是hstatic ObjectnewProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
可以看出,Proxy提供了的4个方法全是static,它其实应嘎叫做ProxyFactory。关于以上方法具体的内容可以看JavaDoc,不过为了看清楚动态代理的实现,我决定研究一下Proxy的源码。处于篇幅的考虑,只是节选了部分源码:
 class Proxy implements java.io.Serializable {/*所有生成的代理类的名称是:$Proxy紧接一个数字,这个数字就是nextUniqueNumber++*/private static long nextUniqueNumber = 0;    private final static String proxyClassNamePrefix = "$Proxy";    /**动态代理类构造方法的参数 */    private final static Class[] constructorParams = { InvocationHandler.class };    /*映射每个ClassLoader所生成的代理类*/    private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache        = new WeakHashMap<>();    /**标记一个动态代理类正在生成 */    private static Object pendingGenerationMarker = new Object();    /** next number to use for generation of unique proxy class names */    private static Object nextUniqueNumberLock = new Object();    /** 生成的代理类,在查询一个类是否是代理类时就是通过判断是否在proxyClasses中 */    private static Map<Class<?>, Void> proxyClasses =        Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());    /*关联的InvocationHandler*/    protected InvocationHandler h;   /*留给子类扩展的构造方法*/    protected Proxy(InvocationHandler h) {        doNewInstanceCheck();        this.h = h;    }   /*生成代理类,这个代理类由loader加载,实现了interfaces中所有的接口,主要逻辑由getProxyClass0()实现*/    public static Class<?> getProxyClass(ClassLoader loader,                                         Class<?>... interfaces)        throws IllegalArgumentException    {        SecurityManager sm = System.getSecurityManager();        if (sm != null) {            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);        }        return getProxyClass0(loader, interfaces);    }    /**     * 真正生成代理类的方法:     * 1.首先检查需要继承的接口是否大于65535,这是Java中能实现接口的上限     * 2.遍历interfaces(需要实现的接口),获得每个接口的名称interfaceNames,如果:     *   使用反射加载interfaceNames所对应的Class对象,如果与接口不一致,抛异常     *   如果当前遍历的Class对象不是接口,抛异常     *   如果其中有重复的接口,抛异常(interfaceSet就是用来保存已经遍历的接口,通过它可以判断是否包含当前接口)     * 3.使用loader去loaderToCache中获得有该类加载器生成的所有代理类的map,如果没有,则创造一个空的Map放进去     * 4.将所有需要实现的接口转换成List,去第3步中的map中查找:     *   如果已经生成了由loader加载的且实现了interfaces所有接口的代理类,直接把该代理类返回     *   如果正在生成有loader加载且实现了interface接口的代理类,等待,直到代理类生成//point1     *   如果还没有生成符合条件的代理类,执行第5吧     *   注意上面是放在一个while(true)循环中,所有point1最终都会从这里返回     * 5.将该类加入包里,如果interfaces中由non-public的接口,则该代理类就就放在该non-public所在的包中,     *   所以interfaces中所有的non-public接口应该在同一包中,否则就无法生成代理类了;     *   如果全部都是public接口,则代理类默认放在com.sun.proxy包里     * 6.代理类的名称是:$Proxy加序号     * 7.调用本地方法生成代理类的字节码,同时将生成的代理类放入proxyClasses,方便isProxy()方法调用     * 8.将生成的代理类放入第3步的map中,以便point1可以顺利返回,同时也把该代理类保存起来     * @param loader     * @param interfaces     * @return     */    private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {    //检查需要继承的接口是否大于65535,这是Java中能实现接口的上限    if (interfaces.length > 65535) {            throw new IllegalArgumentException("interface limit exceeded");        }    //需要生成的代理类        Class<?> proxyClass = null;        /* 所有需要实现的接口名称的数组 */        String[] interfaceNames = new String[interfaces.length];        // 用于检查是否有重复的接口        Set<Class<?>> interfaceSet = new HashSet<>();        //遍历每个接口,对应注释中的第2步        for (int i = 0; i < interfaces.length; i++) {        //判断传递进来的接口与通过反射生成的接口是否相同            String interfaceName = interfaces[i].getName();            Class<?> interfaceClass = null;            try {                interfaceClass = Class.forName(interfaceName, false, loader);            } catch (ClassNotFoundException e) {            }            if (interfaceClass != interfaces[i]) {                throw new IllegalArgumentException(                    interfaces[i] + " is not visible from class loader");            }           /*判断传递进来的是否是进口*/            if (!interfaceClass.isInterface()) {                throw new IllegalArgumentException(                    interfaceClass.getName() + " is not an interface");            }            /*判断是否有重复的接口*/            if (interfaceSet.contains(interfaceClass)) {                throw new IllegalArgumentException(                    "repeated interface: " + interfaceClass.getName());            }            interfaceSet.add(interfaceClass);            interfaceNames[i] = interfaceName;        }        /*将要实现的接口组装成List,到时候作为key去map中查找,判断该动态代理类是否已经生成*/        List<String> key = Arrays.asList(interfaceNames);        /*获得由loader类加载器生成所有代理类的map*/        Map<List<String>, Object> cache;        synchronized (loaderToCache) {            cache = loaderToCache.get(loader);            if (cache == null) {                cache = new HashMap<>();                loaderToCache.put(loader, cache);            }        }        synchronized (cache) {        /**         * 下面的代码是在while(true)循环中:         * (1)如果已经生成了复合条件的动态代理类,直接把该类返回,循环结束         * (2)如果需要生成的动态代理类正在创建中,等待,直到创建完成,则会跳到(1),循环会结束         * (3)否则,去创建该动态代理类,并标记正在创建中,创建完成后,也会跳到(1)         */            do {                Object value = cache.get(key);                if (value instanceof Reference) {                    proxyClass = (Class<?>) ((Reference) value).get();                }                if (proxyClass != null) {                    return proxyClass;                } else if (value == pendingGenerationMarker) {                    try {                        cache.wait();                    } catch (InterruptedException e) {                    }                    continue;                } else {                    cache.put(key, pendingGenerationMarker);                    break;                }            } while (true);        }        try {        //代理类的包            String proxyPkg = null;                /*所有要实现的接口有non-public的,则该代理类就和这个non-public接口在同一个包里,否而就在com.sun.proxy*/            for (int i = 0; i < interfaces.length; i++) {                int flags = interfaces[i].getModifiers();                if (!Modifier.isPublic(flags)) {                    String name = interfaces[i].getName();                    int n = name.lastIndexOf('.');                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));                    if (proxyPkg == null) {                        proxyPkg = pkg;                    } else if (!pkg.equals(proxyPkg)) {                        throw new IllegalArgumentException(                            "non-public interfaces from different packages");                    }                }            }            if (proxyPkg == null) {                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";            }            {               /*获得一个序号,加到$Proxy后面的序号*/                long num;                synchronized (nextUniqueNumberLock) {                    num = nextUniqueNumber++;                }                String proxyName = proxyPkg + proxyClassNamePrefix + num;                /*生成代理类*/                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                    proxyName, interfaces);                try {                    proxyClass = defineClass0(loader, proxyName,                        proxyClassFile, 0, proxyClassFile.length);                } catch (ClassFormatError e) {                    throw new IllegalArgumentException(e.toString());                }            }            // 加到proxyClasses中,isProxy()方法就是靠它判断            proxyClasses.put(proxyClass, null);        } finally {           /*将生成的代理类加入缓存中*/            synchronized (cache) {                if (proxyClass != null) {                    cache.put(key, new WeakReference<Class<?>>(proxyClass));                } else {                    cache.remove(key);                }                cache.notifyAll();            }        }        return proxyClass;    }        /**     * 生成代理类的实例     * 1.首先调用getProxyClass0()获得代理类的Class对象     * 2.获取动态东来类的构造方法的Constructor对象     * 3.调用Constructor.newInstance()     * 它需要InvocationHandler参数     */    public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException    {        //获取由loader家在,且实现了interfaces接口的动态代理类        Class<?> cl = getProxyClass0(loader, interfaces);        final Constructor<?> cons = cl.getConstructor(constructorParams);        final InvocationHandler ih = h;        return newInstance(cons, ih);    }    //创建代理类的实例,代理对象,调用代理类的构造方法    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {              return cons.newInstance(new Object[] {h} );    }        /*判断cl是否是代理类,如果在proxyClasses中就是,否则就不是*/    public static boolean isProxyClass(Class<?> cl) {        if (cl == null) {            throw new NullPointerException();        }        return proxyClasses.containsKey(cl);    }        /*获得与proxy关联的InvocationHandler,就是获得proxy.h,然后返回*/    public static InvocationHandler getInvocationHandler(Object proxy)            throws IllegalArgumentException        {            final Proxy p = (Proxy) proxy;            final InvocationHandler ih = p.h;            return ih;        }}
Proxy在生成代理类的时候主要的工作就是让代理类实现所需的接口,生成字节码的工作是ProxyGenerator做的。在生成动态代理类时,我们需要传递给它类加载器,需要实现的接口,以及与之关联的InvocationHandler对象,InvocationHandler对象要做的就是我们希望实现的代理逻辑,比如,我们希望在每个方法调用之前做安全检查,那就在InvocationHandler的invoke()方法里调用安全检查的逻辑,invoke()是代理发生的地方。我们看一下invoke()方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

其中proxy是代理类,不用去管它。method是要真实对象要执行的方法,也就是我们要代理的方法,args是method的参数,所以如果代理需要做安全检查的话,可以这么写:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    if(安全检查通过)        method.invoke(obj,args);   else       return null;}

例子

说了这么多,我们还是实现一个动态代理的例子,逻辑是这样的:有三个模块,分别是添加,删除,修改,这些代码已经完成;在做实际的工作之前,需要进行安全验证,如安全验证通过,就去调用相应的方法,否则本次操作失败。首先定义各个接口以及实现:
//添加模块interface Add {public void add(String s);}class AddImp implements Add {public void add(String s) {System.out.println(s + "已经被添加进系统");}}//删除模块interface Delete {public void delete(String s);}class DeleteImp implements Delete {public void delete(String s) {System.out.println(s + "已经被删除");}}//修改模块interface Update {public void update(String s);}class UpdateImp implements Update {public void update(String s) {System.out.println(s + "已经被修改");}}
安全检查模块
/** * 安全检查模块,随机的返回检查通过或者不通过 */class SafeCheck {static Random rand = new Random(25);public static boolean check(Object obj) {if(rand.nextInt(20) > 10)return false;return true;}}
生成动态代理,并代理执行:
/** * 可以把它理解为代理角色,内部保存真实对象的引用 * 在调用真实对象的方法之前或之后执行相应的控制逻辑 * 虽然真正的动态代理类是由Proxy生成的,但是代理的逻辑却是在这里实现的 */class DynamicProxy implements InvocationHandler {//真实对象,只能是Objectprivate Object originalObj;/** * 为传递进来的对象生成代理类,并实现originalObj的接口 */public Object bind(Object originalObj) {this.originalObj = originalObj;//originalObj对应的Class对象Class<?> clazz = originalObj.getClass();//自身现在就是处理器,所以把this传递给代理类return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}/** * 方法的返回值是method的返回值,如果没有则返回null * 代理真正发生的地方,可以在这里添加控制逻辑 * 注意,proxy是代理类,method是在真实对象上执行的,也就是originalObj */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//如果通过了安全验证,就去执行真正的逻辑if(SafeCheck.check(originalObj))return method.invoke(originalObj, args);else System.out.println(originalObj + "不符合安全要求!");//否则就什么也不执行return null;}}
测试代码:
public class Client {public static void main(String[] args) {String name = "cxy";DynamicProxy handler = new DynamicProxy();//真实对象Add add = new AddImp();Update update = new UpdateImp();Delete delete = new DeleteImp();//生成Add的代理类Add pa = (Add) handler.bind(add);//代理执行pa.add(name);//生成update的代理类Update pu = (Update)handler.bind(update);pu.update(name);//生成Delete的代理类Delete pd = (Delete)handler.bind(delete);pd.delete(name);}}
输出结果:
----正在给com.understanding.loaderandengine.AddImp@f0a3e8做安全检查-----cxy已经被添加进系统----正在给com.understanding.loaderandengine.UpdateImp@a22e0c做安全检查-----com.understanding.loaderandengine.UpdateImp@a22e0c不符合安全要求!----正在给com.understanding.loaderandengine.DeleteImp@1b56bda做安全检查-----com.understanding.loaderandengine.DeleteImp@1b56bda不符合安全要求!

我们可以看到,虽然系统中有三个模块,而且各自实现不同的接口,只要他们需要代理的逻辑是相同的,那么只需要给出一个处理程序,就可以动态代理全部的模块了,这是动态代理的强大之处。这个例子已经有点AOP的味道了,安全检查以不改变侵入原来模块的方式做到了为每个模块服务,Spring中的AOP有一大部分是通过动态代理实现的。

动态代理的执行

前面已经介绍了动态代理类是如何生成的,也演示了动态代理的例子,其实我们还想看看动态代理类是如何代理服务的,这就需要看动态代理类的代码了,可以通过下面这个工具类来生成动态代理类:

import java.io.FileOutputStream;import java.io.IOException;import java.lang.reflect.Proxy;import sun.misc.ProxyGenerator;public class ProxyUtils {/* * 将根据类信息 动态生成的二进制字节码保存到硬盘中, * 默认的是clazz目录下         * params :clazz 需要生成动态代理类的类         * proxyName : 为动态生成的代理类的名称         */public static void generateClassFile(Class clazz,String proxyName){  //根据类信息和提供的代理类名称,生成字节码  byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces()); String paths = clazz.getResource(".").getPath();System.out.println(paths);FileOutputStream out = null;                  try {            //保留到硬盘中        out = new FileOutputStream(paths+proxyName+".class");              out.write(classFile);              out.flush();          } catch (Exception e) {              e.printStackTrace();          } finally {              try {                  out.close();              } catch (IOException e) {                  e.printStackTrace();              }          }  }}
或者在main()中加入一句来生成代理类:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
通过反编译,得到代理类的源码,有了源码那么一切都暴露在我们面前了,先看看为AddImp生成的代理类:
/** * 动态代理类,它继承了Proxy类,同时实现了Add接口 */public final class AddProxy extends Proxyimplements Add{private static Method m1;private static Method m3;private static Method m0;private static Method m2;//接收一个InvocationHandler做参数,它是代理真正发生的地方public AddProxy(InvocationHandler paramInvocationHandler)  throws {  super(paramInvocationHandler);}/** * 实现Add接口而生成的add方法 */public final void add(String paramString)  throws {  try  {//它其实就是去执行InvocationHandler的invoke()方法了    this.h.invoke(this, m3, new Object[] { paramString });    return;  }  catch (Error|RuntimeException localError)  {    throw localError;  }  catch (Throwable localThrowable)  {    throw new UndeclaredThrowableException(localThrowable);  }}static{  try  {   //通过反射的反射获得add对应的Method对象,在InvocationHandler的invoke()方法中,当代理逻辑完成后,就是调用m3去执行真正的业务逻辑    m3 = Class.forName("com.understanding.loaderandengine.Add").getMethod("add", new Class[] { Class.forName("java.lang.String") });    m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);    return;  }  catch (NoSuchMethodException localNoSuchMethodException)  {    throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  }  catch (ClassNotFoundException localClassNotFoundException)  {    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  }}}
以上代码删除了为继承Object而生成的hashCode(),toString(),equals()方法,接着再看看Delete代理类的源码,节选:
//为实现delete()代理而实现的方法public final void delete(String paramString)    throws   {    try    {      this.h.invoke(this, m3, new Object[] { paramString });      return;    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }//通过反射获得delte()的Method对象m3 = Class.forName("com.understanding.loaderandengine.Delete").getMethod("delete", new Class[] { Class.forName("java.lang.String") });
Update的代码就不贴了,从上面的代码中,我们可以发现动态代理的执行过程:

(1)动态代理类需要保存一个InvocationHandler的引用;

(2)动态代理类实现被代理类实现的全部接口,并获得相应方法的Method对象,生成相应的方法

(3)在执行代理的时候,动态代理类其实转到InvocationHandler.invoke()方法中去了,这也是为什么我说InvocationHandler是代理角色的原因。

到此,我们已经知道了动态代理类是如何生成的,动态代理是如何实现的,并且举了一个小例子,这个小例子很有AOP的思想。但是Java中动态代理已经很完美了吗?如果回过来头来去看Proxy.getProxyclass)()方法,我们发现Java的动态代理是根据被代理类所实现的接口来代理的,可以说Java的动态代理是“面向接口的代理”,如果一个类没有实现接口,那么就无法生成动态代理类,这是它的缺陷,但是不得不说,Java的动态代理还是很强大的。

转载请注明:喻红叶《Java与模式-代理模式》

0 0