JDK dynamic proxy implementation

JDK dynamic proxy implementation

Although popular Java framework (Spring, MyBaits, etc.), it is often seen using JDK dynamic proxies, but also know how to write a JDK dynamic proxy Demo, but it is not clear realization of the principle.

Mr. Cheng is a little known before a proxy object, and then call the proxy object external interface methods, then the agent will call the original method object, nothing more. Now I want to throw a little deeper, to understand part of the implementation.

Java programs are run on the JVM, the source code is first compiled into byte code and then loaded into the virtual machine, the virtual machine is parsed and then executed (Note the wording here is not necessarily rigorous and accurate, its meaning can be).

JDK dynamic proxy is actually running code that is generated dynamically in the realization of the specified interface (the interface is implemented in the proxy object) of the class bytecode, i.e. corresponding to the proxy class, and then create objects of this class of agents, also It is the proxy object. About this part, see this blog better, written in great detail. In fact, the point of this article is actually concerned about is how to create a JDK proxy class, and then find ways how to call the proxy object, will be able to enhance the original object.

First the simple Demo Code:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * @author xi
 * @date 2018/08/23 12:04
 */
public class DynamicProxyTest {
    interface IHello {
        void sayHello();
    }

    static class Hello implements IHello {
        @Override
        public void sayHello() {
            System.out.println("hello world");
        }
    }

    static class DynamicProxy implements InvocationHandler {

        Object originalObj;

        Object bind(Object originalObj) {
            this.originalObj = originalObj;
            return Proxy.newProxyInstance(
                    originalObj.getClass().getClassLoader(),
                    originalObj.getClass().getInterfaces(), this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("welcome");
            return method.invoke(originalObj, args);
        }
    }

    public static void main(String[] args) {
        IHello hello = (IHello) new DynamicProxy().bind(new Hello());
        hello.sayHello();
    }
}

This example is the "in-depth understanding of the Java Virtual Machine," the book's examples, feeling the example of DynamicProxythe name is not very good, easy to mislead the beginner like me, easy to see it as a proxy class, but in reality not, it is just InvocationHandleran interface implementation class, do not be confused.

In fact, the focus of this concern is how you want to proxy class and proxy objects are generated, so to look at java.lang.reflect.Proxy#newProxyInstance, because the machine is used on JDK 1.8, previously code a little difference, but not a big problem.

    @CallerSensitive// 这个注解不懂是啥,Google 一下,还是没懂,先看看文末的参考吧,有懂的同学,麻烦指点一二
    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) {
            //...省略部分不看的内容
        }
    }

Mainly want to see is java.lang.reflect.Proxy#getProxyClass0how the generated proxy class:

    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    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);
    }

proxyClassCache It is a static variable, the caching proxy class:

    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

It should be noted about the creation of two-argument constructor of the cache class two parameters are implemented BiFunctioninterfaces, the new features Java8: functional interface. Simple understanding is a method defined in the interface, accepts two parameters, meal after the operation returns a result. Look at the constructor cache:

    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

Remember subKeyFactoryand valueFactorythese two variables is valid and will be used immediately.

Look caching java.lang.reflect.WeakCache#getmethod, the method is relatively long, do not all look the place to focus on a few comments, details can be self Debug:

    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 生成 key,subKeyFactory 就是前面提到的函数对象
        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
                // 上面的注释都写明白了,获取出来的对象的类都实现了 Supplier 接口
                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 {//  用 factory 替换 supplier
                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);
                }
            }
        }
    }

Generate key not read, look directly at V value = supplier.get();this line of code, the first call, certainly not cached, so look at java.lang.reflect.WeakCache.Factory#get:

        @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {// 这个地方的 valueFactory 就是前面说到的 ProxyClassFactory 对象
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // put into reverseMap
            reverseMap.put(cacheValue, Boolean.TRUE);

            // try replacing us with CacheValue (this should always succeed)
            if (!valuesMap.replace(subKey, this, cacheValue)) {// 添加缓存
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            return value;
        }

The next is to look at the java.lang.reflect.Proxy.ProxyClassFactory#applyway the main line:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

This is a tool to generate proxy class provided by the JDK, decompile the code is as follows:

未完待续,先睡觉....

reference:

Guess you like

Origin www.cnblogs.com/magexi/p/11762577.html