qiezijiajia

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

原文出处:http://blog.csdn.net/jiankunking/article/details/52143504

概述

主要从几个方面来讲解动态代理:

1.什么是代理?

2.动态代理类

3.怎么使用动态代理?

4.动态代理如何实现?

5.结论

什么是代理?

代理是常用的设计模式,主要用途是为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责委托类的预处理消息,过滤消息并转发消息,以及进行消息被委托类执行的后续处理。代理模式UML图:

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来,两者没有任何区别。通过代理类这一中间层,能有效控制对委托类对象的访问,也可以很好的隐藏和保护委托类,同时也为实施不同的控制策略预留了空间,从而在设计上获得更大的灵活性。Java动调代理机制以巧妙的方式接近完美的实践了代理模式的设计理念。

动态代理类

Java动态代理主要涉及两个类:

1. interface InvokeHanlder,该借口给中仅定义了一个方法invoke:

public Object invoke(Object proxy, Method method, Object[] args) 
该方法负责集中处理动态代理类上的所有方法调用。
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
* @param proxy //代理类实例
* @param method //被调用的方法
* @param args //方法参数

2.Proxy:该类即为动态代理类,主要包括以下方法:

  1.protected Proxy(InvocationHandler h) {}构造函数,主要给内部的h赋值;

  2.public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces): 获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

  3.public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):返回一个代理类实例,返回后的代理类可当做代理类使用,可直接调用真实类中在接口中声明的方法;

DynamicProxy是这样的一种class:它是在运行时生成的一种class,在生成它时,必须提供一组interface给它,然后该class就宣称自己实现了这些interface。当然这个DynamicProxy实际上就是一个Proxy,它不会做任何实质性的工作,在生成它的时候,必须提供一个InvocationHandler,由它来接管实际工作。

在使用动态代理类时,必须实现invocationHandler接口。

通过这种方式,被代理的对象(realSubject)可以在运行时动态改变,需要控制的接口(subject接口)也可以在运行时改变,控制的方式(DynamicProxy)也可以动态改变,从而实现了非常灵活的动态代理。

动态代理的步骤:

1.创建一个实现了invocationHandler接口的类,必须实现invoke方法;

2.创建被代理的类以及接口;

3.通过Proxy的静态方法newProxyInstance获取代理类实例;

4.通过代理类实例调用方法;

怎么使用动态代理?

1.需要动态代理的接口

public interface Subject {

    /**
     * 动态代理的接口
     */


    String sayHello(String name);

    String sayGoodBye();
}

2.动态代理的实际对象

public class RealSubject implements Subject {

    /**
     *实际对象
     */

    @Override
    public String sayHello(String name) {
        return "hello "+name;
    }

    @Override
    public String sayGoodBye() {
        return "good bye";
    }
}

3.实现了invocationHandler接口的类

/*调用处理器实现类
 * 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
 */

public class InvocationHandlerImpl implements InvocationHandler {

    private Subject subject;  //要代理的对象
    public InvocationHandlerImpl(Subject subject){

        this.subject=subject;
    }

    /**
     * 该方法负责集中处理动态代理类上的所有方法调用。
     * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
     * @param proxy //代理类实例
     * @param method  //被调用的方法
     * @param args  //方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("在调用之前,我要干点啥呢?");

        System.out.println("Method:" + method+",args:"+args.toString()); //需要调用的方法

        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object returnValue = method.invoke(subject, args); //invoke方法,并返回方法的返回值

        System.out.println("在调用之后,我要干点啥呢?");

        return returnValue;
    }
}

4.测试

public class DynamicProxyDemonstration {//测试类


    public static void main(String[] args){

        Subject realSubject=new RealSubject(); //实例化一个实际对象

        //InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发,
        //要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
        InvocationHandler invocationHandler=new InvocationHandlerImpl(realSubject); //处理
        ClassLoader classLoader=realSubject.getClass().getClassLoader();
        Class[] interfaces=realSubject.getClass().getInterfaces();

        //关键代码,生成Proxy实例.该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
        Subject subject=(Subject) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
        String hello=subject.sayHello("friend");
        System.out.println(hello);
    }
}

 输出结果如下:

在调用之前,我要干点啥呢?
Method:public abstract java.lang.String com.dynamicproxy.Subject.sayHello(java.lang.String),args:[Ljava.lang.Object;@4e50df2e
在调用之后,我要干点啥呢?
hello friend

可以看到在测试代码里面并没有直接调用invocationHandler的invoke方法,但是运行结果已经调用成功了。这是为什么?为什么通过动态代理类的实例,直接调用sayHello就会调用到invoke方法呢?下面来看一下动态代理怎么实现的。

动态代理如何实现?

在测试代码中,有段非常关键的代码,就是生成Proxy实例:

        Subject subject=(Subject) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);

当代理对象调用真实对象的方法时(String hello=subject.sayHello("friend");),会自动跳转到代理对象关联的handler对象的invoke方法来进行调用。为什么呢?下面来分析源码看下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs); //获取代理类,重点

        /*调用构造方法
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h}); //返回实例
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

newProxyInstance方法中有个获取代理类的方法getProxyClass0(loader, intfs):

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }


 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.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) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.重点,这次产生代理类,可以再跟踪进去看下是怎么生成.class文件的
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

这时,我们在测试类中单独把ProxyGenerator.generateProxyClass拿出来,用来产生.class文件,如下:


private
static void createProxyClassFile() { String name = "ProxySubject"; byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class}); FileOutputStream out =null; try { out = new FileOutputStream(name+".class"); System.out.println((new File("hello")).getAbsolutePath()); out.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(null!=out) try { out.close(); } catch (IOException e) { e.printStackTrace(); } } }

在main函数中调用该方法,执行完成后会生成.class文件,我们反编译一下这个.class文件,得到结果如下:

 

public final class ProxySubject extends Proxy implements Subject{


public
final String sayHello(String paramString) { try { return (String)this.h.invoke(this, m4, new Object[] { paramString }); //这里调用的实际上是invocationHanlder的invoke方法,这就明白了为什么通过生成的代理类直接调用sayHello从而调用到了invocationHandlerImpl的invoke()方法了 } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String sayGoodBye() { try { return (String)this.h.invoke(this, m3, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m4 = Class.forName("com.dynamicproxy.Subject").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.dynamicproxy.Subject").getMethod("sayGoodBye", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }

到这里,终于明白了为什么调用Subject的sayHello就自动调用到了invokeHandlerImpl的invoke方法:

因为JDK生成的真正代理类,实现了Subject接口,继承了Proxy类,在实现Subject接口时,通过反射调用了InvocationHandlerImpl的invoke方法。

总结

通过分析代码,可以看到动态代理的步骤:

1.实现invocationHandler接口,实现自己的调用处理器;

2.通过为Proxy类指定classLoader对象和一组接口来创建动态代理类;

3.通过反射机制获取动态代理类的构造函数,唯一参数类型就是调用处理器接口类型;

4.通过构造函数创建动态代理实例,构造时调用处理器对象作为参数传入。

posted on 2017-10-17 13:58  qiezijiajia  阅读(929)  评论(0编辑  收藏  举报