美文网首页
JDK动态代理

JDK动态代理

作者: 雨夏_ | 来源:发表于2019-02-22 15:23 被阅读1次

在看这篇文章前,可以先去了解RTTI和反射机制

1.代理模式

什么是代理模式?例如找房子,我们可以自己找,也可以找中介代理去找,如果是自己找,首先要找到房源,然后看房、买房、签协议。如果是代理模式,房源就叫给中介代理去找,我们只需负责看房、买房、签协议就好。可以省去找房源这个麻烦的事情。
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
代理模式可以分为静态代理和动态代理。这篇文章仅仅针对动态代理进行源码的分析。

2.JDK动态代理实现

话不多说,先上代码:

public interface IDoSometing {
    void doSometing();
}
public class DoSometing implements IDoSometing {
    @Override
    public void doSometing() {
        System.out.println("做有意义的事情,买个房吧~!");
    }
}
public class MyInvocationHandler implements InvocationHandler {
    //被代理对象
    private Object target;
 
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 被代理对象执行前
        System.out.println("中介找房子~!");
        // 被代理对象执行
        Object result = method.invoke(target, args);
        // 被代理对象执行后
        System.out.println("中介处理后续的事情");
        return result;
    }
    public IDoSometing getProxy(){
        // 返回代理对象
        return (IDoSometing) Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}
public static void main(String[] args) {
    IDoSometing doSometing = new MyInvocationHandler(new DoSometing()).getProxy();
    doSometing.doSometing();
}

测试结果:


3.Proxy源码分析

通过上面的代码我们可以知道JDK动态代理怎么去实现,那么它的底层原理是怎样的呢。接下来我们对相关的源码进行分析。在上面的代码中,首先是通过Proxy.newProxyInstance()这个静态方法来生成代理对象,我们首先从这个方法开始。

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    // 检测InvocationHandler是否为空,为空抛NullPointerException
    Objects.requireNonNull(h);
    // 将接口类对象数组复制一份
    final Class<?>[] intfs = interfaces.clone();
    // 执行权限检查
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    /**
     * 这里是通过查询一个WeakCache缓存,获取代理对象
     * 如果代理对象不存在,则通过静态内部类ProxyClassFactory来生成代理对象
     * 这个WeakCache<K , P ,V>,K是ClassLoader , P是被代理对象所有的接口,V就是生成代理对象
     */
    Class<?> cl = getProxyClass0(loader, intfs);
    //调用指定的构造方法生成代理对象
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        /**
         * 生成的代理类会有参数为InvocationHandler的构造方法
         * 这里获取参数为InvocationHandler的构造方法
         */
        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) {
        //...省略以下catch部分的代码
    }
}

从上面的代码分析可以知道,代理类主要是通过getProxyClass0()这个方法获取的,getProxyClass0()这个方法主要是调用WeakCache的get()方法,WeakCache查询缓存的逻辑是查询缓存,如果不存在则通过valueFactory去生成一个值。这里valueFactory其实就是Proxy静态内部类ProxyClassFactory,通过apply()这个方法来生成代理类:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            // 以下都是对接口的校验
            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");
            }
            //校验是否为一个接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            //校验是否重复
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }
        //生成代理类的包名
        String proxyPkg = null;
        //指定代理类访问标志,默认public final
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
 
        for (Class<?> intf : interfaces) {
            // 获取接口访问标志
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                //设置代理类访问标志位final
                accessFlags = Modifier.FINAL;
                //获取接口的全限定名,例如:java.lang.reflect.Proxy
                String name = intf.getName();
                //裁剪,例如java.lang.reflect
                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) {
            //如果全部都是public的接口,则生成的代理类放置默认的包下:com.sun.proxy
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }
 
        // 生成代理类全限定名,包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
        // num就是末尾的0,是AtomicLong,保证线程安全
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
        /**
         *  这里是核心,用来生成代理类的字节码
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // 本地方法,根据二进制文件生成相应的Class实例
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}

其中ProxyGenerator.generateProxyClass()来生成代理类的字节码,该方法主要是通过调用generateProxyClass()来生成的,我们直接看这个方法。

private byte[] generateClassFile() {
 
    /* ============================================================
     * 第一步:将所有方法组装成ProxyMethod对象以生产代理调度代码
     */
 
 
     //为代理对象生成toString, hashCode, equals等代理方法
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);
 
    // 遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
    for (Class<?> intf : interfaces) {
        for (Method m : intf.getMethods()) {
            addProxyMethod(m, intf);
        }
    }
 
     // 对于具有相同签名的每组代理方法,验证方法的返回类型是否兼容
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }
 
    /* ============================================================
     * 第二步:组装要生成的class文件的所有的字段信息和方法信息
     */
    try {
        /**
         * 生成构造方法,注意,这里会生成带有InvocationHandler参数的构造方法
         */
        methods.add(generateConstructor());
 
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
 
                // 添加静态属性
                fields.add(new FieldInfo(pm.methodFieldName,
                    "Ljava/lang/reflect/Method;",
                     ACC_PRIVATE | ACC_STATIC));
 
               // 添加代理类的代理方法
                methods.add(pm.generateMethod());
            }
        }
        // 添加代理类的静态字段初始化方法
        methods.add(generateStaticInitializer());
 
    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }
 
    if (methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    }
    if (fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    }
 
    /* ============================================================
     * 第三步:写入class文件
     */
    //省略以下写入class文件代码,不过其中有一个地方可以注意下,写入的时候写入了父类索引为"java/lang/reflect/Proxy",所以所有生成的代理类都继承于Proxy
    ...
     
}

JDK动态代理的源码分析就到这里了,大概的了解了一下实现原理,以后用到JDK动态代理也能知道它是怎么实现的了,这次分析的代码中,其实还有一个很关键的东西WeakCache,没有具体分析,下次有机会可以单独写一篇文章进行分析。

4.JDK动态代理使用场景

分析了这么多东西,总得要知道学到的东西该用在哪里吧~!这次分析JDK动态代理源码,其实就是为了更好的去了解Spring框架的AOP的实现,Spring框架AOP的实现就是通过JDK动态代理或者字节码增强来实现,JDK动态代理是生产代理类来实现,必须要实现接口才行,字节码增强是生成子类,无需继承接口。Spring框架的AOP能够非常有效的解决业务类中与业务逻辑无关代码的解耦合,能让我们不用去关心与业务逻辑无关的代码,只需注重于业务逻辑的实现,提高代码质量,也为后期维护代码提供便利。
SpringAOP只是JDK动态代理的一个使用场景,当然还有其它的使用场景,我就不一一介绍了。

相关文章

网友评论

      本文标题:JDK动态代理

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