Java之动态代理

静态代理

先看一个例子,有个汽车记录功能,我们既要记录行驶的时间,又要记录其它日志,如果这些事全部交给Car这个对象来做,那么它要处理的事情就太多了,既要跑还有写,所以为了给Car减轻负担,代理类就诞生了,代码如下:

//接口类
public interface MoveAble {
    void move();
}

//Car类
public class Car implements MoveAble {
    @Override
    public void move() {
        System.out.println("car move中");
    }
}

//记录时间代理类
public class TimeProxy implements MoveAble{
    private MoveAble m;
    public TimeProxy(MoveAble m){
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("time开始");
        m.move();
        System.out.println("time结束");
    }
}

//记录日志代理类
public class LogProxy implements MoveAble {
    private MoveAble m;
    public LogProxy(MoveAble m) {
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("log开始");
        m.move();
        System.out.println("log结束");
    }
}

从以上的代码中可以看出我们创造了两个新的类去继承和Car一样的接口,这样做本质上来说它们三个就都有跑的方法了。那么我们可以像下面的代码那样调用它们:

public static void main(String[] args) {
        Car car = new Car();
        LogProxy logProxy = new LogProxy(car);
        TimeProxy timeProxy = new TimeProxy(logProxy);
        timeProxy.move();
}

上面这段代码中把Car先放进了LogProxy里,又把LogProxy放进了TimeProxy里,接着调用了TimeProxy对象的move方法,这时候会一层一层的调用move方法,从TimeProxy到LogProxy再到Car,这样子就把记录时间和日志的功能给完成了。
那么这种代理模式有什么好处呢,可以让不同的功能做不同的事,也就是单一职责,拆分了代码逻辑,使逻辑更加的清晰了。
但是这种静态的代理有个问题,就是我们需要自己去写代理类,如果功能少还好说,要是功能多的话就会很麻烦,所以,动态代理就开始登场了。

动态代理

jdk中为我们提供了创建动态代理对象的方式,即Proxy类。
Proxy类位于java.lang.reflect包中,包含有一个静态的创建方法newProxyInstance,代码如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException{
       ......//暂时可以忽略的逻辑
}

在上面的创建Proxy类代码中需要接口三个参数:

  • ClassLoader loader
    类加载器
  • Class[] interfaces
    类实现的接口
  • InvocationHandler h 处理器

我们重点看一下第三个参数InvocationHandler,这个类是个接口,里面只有一个invoke方法,我们所要做的处理逻辑都会在这个方法里执行。代码如下:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

invoke方法里也有三个参数:

  • Object proxy 调用invoke方法的对象实例
  • Method method 对象的方法
  • Object[] args 参数

那么我们应该怎么使用动态代理创建我们的代理类呢,基于上面Car记录时间和日志的例子,我们可以这样写:

public static void main(String[] args) {
        Car car = new Car();
        MoveAble m = (MoveAble) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("log开始");
                System.out.println("time开始");

                method.invoke(car);

                System.out.println("time结束");
                System.out.println("log结束");

                return null;
            }
        });
        m.move();
}

是不是很简单,有个动态代理,我们就不用自己去创建LogProxy,TimeProxy等等代理类了。

动态代理原理

知道了通过动态代理的方式可以帮我们自动的生成一些代理类,那么Proxy究竟是怎么办到的呢,下面就开始分析一下Proxy的内部原理。首先,看一下newProxyInstance这个静态方法,代码如下:

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

        final Class[] intfs = interfaces.clone();
        
        Class cl = getProxyClass0(loader, intfs); //1

        try {
            final Constructor cons = cl.getConstructor(constructorParams);//2
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);
                AccessController.doPrivileged call.
            }
            return cons.newInstance(new Object[]{h});//3
        } 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);
        }
    }

为了逻辑清晰我删除了注释,这段代码的主要逻辑就是在1,2,3处,这三步分别是:

  • 1.得到Proxy的Class对象
  • 2.通过Class对象得到Constructor对象
  • 3.通过Constructor对象的newInstance方法获得实例对象

下面我们主要分析Class cl = getProxyClass0(loader, intfs); //1这行代码,来看看这个方法是怎么生成的Class类对象的。
这个方法的代码很简单:

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);
}

主要就是看最后一行代码,返回的就是Class对象

public V get(K key, P parameter) {

       ......

        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue instance
                V value = supplier.get();
                if (value != null) {
                    return value;//1
                }
            }

            // 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);
                }
            }
        }
    }

这段代码我删除了一些注释和与主要逻辑关系不大的代码。
首先来看几个比较的对象:

  1. Supplier supplier
  2. Factory factory

先看最后返回的value是通过supplier.get()的,也就是代码中1处的逻辑,那么这个supplier是什么呢,它是个接口,下面的factory是它的实现类,下面的while循环中就是给supplier赋值的过程。也就是说value的最终获取是通过factory里的get方法取得,那么我们再看一下这个get方法逻辑:

public synchronized V get() { // serialize access
            ......

            // create new value
            V value = null;
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));//1
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }

            return value;
}

这里最重要的逻辑是代码1处,从valueFactory的apply里得到了value,也就是我们最终要得到的Class对象。那么我们再来看看这个valueFactory是什么。

 public WeakCache(BiFunction subKeyFactory,
                     BiFunction valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

valueFactory是BiFunction接口的实现类对象,WeakCache初始化的时候被传进来

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class[] constructorParams =
        { InvocationHandler.class };

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

        ......
}

可以看出来是在Proxy类中被初始化的,也就是ProxyClassFactory类对象。这个ProxyClassFactory也实现了BiFunction,那么上面的apply方法最终的实现逻辑就是在这个类中:

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

           ......
                
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

                return generateProxy(proxyName, interfaces, loader, methodsArray,
                                     exceptionsArray);
            }
}

这个方法代码很长,我们只需要关注这几行就行了,可以看出,这个方法设定了一个规则,生成一个字符串proxyName也就是我们的代理类的类名,最后的generateProxy方法实际上是调用了本地的一个方法,就是生成Class对象的方法:

@FastNative
    private static native Class generateProxy(String name, Class[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class[][] exceptions);

那么到现在为止就把动态代理怎么生成代理对象的机制分析完了。

你可能感兴趣的:(Java之动态代理)