美文网首页
Spring 源码分析(三)Core

Spring 源码分析(三)Core

作者: sschrodinger | 来源:发表于2019-05-28 14:45 被阅读0次

Spring源码分析(三)Core

sschrodienger

2019/05/27


代理


代理的主要作用是使用一个代理类代理普通类,使得对普通类的功能进行扩展,参见代理模式(runoob.com)

按照代理类生成的时间,可以分为静态代理和动态代理,静态代理特指代理类和委托类在代码运行前关系就确定了,也就是说在代理类的代码一开始就已经存在了;动态代理特指在运行时才生成代理类。

Java 静态代理

静态代理医用聚合的方式组成,UML 类图如下:

spring core

假如需要编写一个数据库操作类,实现数据库的连接和添加操作,类的接口和实现定义如下:

public interface DataOp {
    public void connect() throws SQLException;
    public void addData(Data data) throws SQLException;
}

public class DataOpImpl implements DataOp {
    public void connect() throws SQLException {}
    public void addData(Data data) throws SQLException {}
}

使用静态代理对 DataOpImpl 增加一个日志功能,在运行成功时打印成功、失败时打印失败,如下:

public class DataOpProxy implements DataOp {
    private DataOp dataOp = new DataOpImpl();
    public void connect() throws SQLException {
        try {
            dataOp.connect();
            System.out.println("connect success");
        } catch (SQLException e) {
            System.out.println("connect failed");
            throw new SQLException(e);
        }
    }
    public void addData(Data data) throws SQLException {
        try {
            dataOp.addData(Data data);
            System.out.println("add success");
        } catch (SQLException e) {
            System.out.println("add failed");
            throw new SQLException(e);
        }
    }
}

note

  • 代理模式和装饰器模式比较相似:代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
  • 即:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。

手工写代理类麻烦且易出错,AspectJ 库可以通过注解的方式自动帮我们完成代理类的生成。

动态代理

动态代理主要有两种实现方式:JDK 代理CGLIB 代理

JDK 代理

JDK 代理主要依赖于类库 java.lang.reflect.ProxyJDK 代理要求委托类必须实现接口,JDK 代理只会代理接口之下的方法。

对于如下的接口和接口实现:

interface Service {
    void doSomething();
}

class ServiceImpl implements Service {

    public void doSomething() {
        System.out.println("class[" + this.getClass().getCanonicalName() + "] do something");
    }
}

可以使用以下的方式获得代理类:

public class DynamicProxy {

    public Object getProxyInstance(Service target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("before...");
                    Object returnValue = method.invoke(target, args);
                    System.out.println("after...");
                    return null;
                });
    }

    public static void main(String[] args) {
        DynamicProxy dynamicProxy = new DynamicProxy();
        Service service = (Service) dynamicProxy.getProxyInstance(new ServiceImpl());
        System.out.println(service.getClass());
        service.doSomething();
    }

}
源码分析

JDK 代理最重要的一个方法是 Proxy.newProxyInstance,在这个方法中,玩成了最主要的三步工作,代码简写如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException {
    // step 1
    Class<?> cl = getProxyClass0(loader, intfs);
    // step 2
    // constructorParams = { InvocationHandler.class }
    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;
            }
        });
    }
    // step 3
    return cons.newInstance(new Object[]{h});
}

其中第一步主要是完成代理类的创建,第二步找到代理类的构造函数、第三部将 InvocationHandler 作为构造函数的参数传入并返回。

 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.get() 中,首先看该缓存中有没有当前代理类,有,则直接返回,没有的话进行创建。proxyClassCache 的值为 ProxyClassFactory,该类是 Proxy 的内部类,如果没有缓存,则调用 ProxyClassFactory 类的 apply 方法创建新的代理类。apply 函数的关键函数如下:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
try {
    return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
}

动态的生成继承了接口的 Proxy 的子类

在如上 main 函数之前加入如下语句,可以将生成的代理类保存在 classPath 中,如下:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

利用反编译查看生成的代理类如下:

package other;

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

final class $Proxy0 extends Proxy implements Service {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("other.Service").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

由此可知,对于每一个代理类继承的函数,都是使用 InvocationHandler,利用反射机制实现代理。

note

  • JDK 代理只能代理接口,因为生成的代理类自动继承 Proxy,当继承类时,因为 Java 不允许多继承,所以会报错。
cglib 代理

与 JDK 代理相比,cglib 直接在内存中根据委托类创建该委托类的子类,并且在子类中增加功能。

与 JDK 代理相比,cglib 可以实现类的代理而不仅仅是接口的代理。

由于是继承方式,对于标注了 static 方法,private 方法,final 方法等描述的方法是不能被代理的。

相关文章

网友评论

      本文标题:Spring 源码分析(三)Core

      本文链接:https://www.haomeiwen.com/subject/bckdtctx.html