原 荐 Spring Aop之Jdk代理实现原理详解

栏目: Java · 发布时间: 7年前

内容简介:Jdk代理,也称为动态代理,其代理目标对象的方式是生成一个与目标对象实现同一个接口的类,该类的构造函数中会传入一个在讲解Spring Aop如何实现动态代理之前,建议读者先去阅读本人上一篇文章前面我们讲到,Spring Aop使用

Jdk代理,也称为动态代理,其代理目标对象的方式是生成一个与目标对象实现同一个接口的类,该类的构造函数中会传入一个 InvocationHandler 类型的对象。因为 InvocationHandler 对象是用户自定义的织入了切面逻辑的类,因而在需要使用目标对象的地方,只需要将生成的代理类的对象传入即可。又因为生成的代理类与目标类都实现了同一接口,因而从语法上其是没有任何问题的。另一方面,在传入代理类对象之后,代理类通过调用构造函数传入的 InvocationHandler.invoke() 方法,从而动态的调用目标类的方法,最终利用这种方式织入了代理逻辑。

在讲解Spring Aop如何实现动态代理之前,建议读者先去阅读本人上一篇文章 Spring Aop之Cglib实现原理详解 ,因为Cglib代理和Jdk代理织入切面逻辑的方式是类似的,只是生成代理类的方式不一样,并且本文中部分方法的实现原理在上一篇文章中已经进行了详细的讲解,这里就不再赘述。

1. 代理对象的构造

前面我们讲到,Spring Aop使用 AopProxy.getProxy() 方法获取代理对象的,而 JdkDynamicAopProxy 则已经实现了该接口,因而我们可以直接阅读其 getProxy() 方法的源码:

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " 
            + this.advised.getTargetSource());
    }
    // 完善代理对象需要实现的接口,主要是会默认增加三个需要实现的接口:SpringProxy,
    // Advised和DecoratingProxy。这三个接口的作用主要如下:
    // SpringProxy:该接口没有任何方法,主要用于标识当前对象是Spring生成的代理对象;
    // Advised:用于封装生成代理对象所需要的所有信息;
    // DecoratingProxy:其有一个getDecoratedClass()方法,用于返回当前代理对象的目标对象的Class类型
    Class<?>[] proxiedInterfaces = AopProxyUtils
        .completeProxiedInterfaces(this.advised, true);
    // 找到接口中是否包含有equals()和hashCode()方法,并进行标识
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // 使用动态代理生成代理对象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

上述方法逻辑其实比较简单,主要就是首先获取生成代理对象所需要实现的所有接口;然后判断接口中是否包含有equals()和hashCode()方法,因为这将用于判断生成的代理类是使用用于定义的equals()和hashCode()方法还是使用自动生成的方法;最后就是通过动态代理生成代理对象,这里需要注意的是 JdkDynamicAopProxy 不仅实现了 AopProxy 接口还实现了 InvocationHandler 接口,因而这里生成代理对象的时候传入的 InvocationHandler 对象是this。

2. 切面逻辑的织入

由于 JdkDynamicAopProxy 已经实现了 InvocationHandler 接口,因而代理逻辑的织入就是在 JdkDynamicAopProxy.invoke() 方法中,这里我们直接阅读器源码:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 如果当前方法是equals()方法,并且接口中并未定义该方法,就使用自动生成的equals()方法
            return equals(args[0]);
        } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 如果当前方法是hashCode()方法,并且接口中并未定义该方法,就使用自动生成的hashCode()方法
            return hashCode();
        } else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // 如果当前方法是Spring织入的DecoratingProxy接口中的方法,则返回目标对象的Class类型
            return AopProxyUtils.ultimateTargetClass(this.advised);
        } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 如果当前方法是Spring织入的Advised接口中的方法,
            // 则使用反射调用当前advised对象中的相关方法
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
        if (this.advised.exposeProxy) {
            // 如果设置了需要暴露代理对象,则将当前对象设置到AopContext中
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 获取当前方法需要织入的切面逻辑的调用链
        List<Object> chain = this.advised
            .getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            // 如果切面逻辑的调用链为空,则对方法参数进行类型转换处理,
            // 并且通过反射直接调用目标对象的方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        } else {
            // 获取目标对象的调用链逻辑,并且对该链进行调用
            invocation = new ReflectiveMethodInvocation(proxy,
                target, method, args, targetClass, chain);
            retVal = invocation.proceed();
        }

        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // 判断返回值如果为目标对象,并且当前方法的返回值类型是当前代理对象的类型,那么就将
            // 当前代理对象返回。这里的逻辑的实际意思简单的说就是,如果返回值是目标对象,那么
            // 就将当前代理对象返回
            retVal = proxy;
        } else if (retVal == null && returnType != Void.TYPE 
                   && returnType.isPrimitive()) {
            // 如果返回值满足其为空,不是Void类型,并且是基本数据类型,那么就抛出异常,
            // 因为基本数据类型的返回值必然不为空
            throw new AopInvocationException("Null return value from advice does not " 
                + " match primitive return type for: " + method);
        }
        return retVal;
    } finally {
        if (target != null && !targetSource.isStatic()) {
            // 如果TargetSource不是静态的,则调用其releaseTarget()方法将生成的目标对象释放
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // 处理AopContext中保存的当前代理对象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

可以看到,在进行代理逻辑的织入的时候,首先会进行如下判断:①当前方法是否为equals()或hashCode()方法,如果是,并且接口中并未要求子类实现这些方法,那么就会调用自动生成的方法;②当前方法是否为Spring织入的DecoratingProxy接口中的方法,如果是,则将目标对象的Class类型返回;③判断目标方法是否为Spring织入的Advised中的方法,如果是,则调用当前advised对象中相应的方法。然后就会获取当前方法需要织入的代理逻辑的调用链。接着就会将目标对象和调用链逻辑封装为ReflectiveMethodInvocation,并进行调用。最后对调用的返回值进行一些基本判断,并且返回。

3. 小结

本文首先对Jdk代理进行了简单的介绍,然后介绍了Spring Aop是如何调用将Jdk代理的Proxy引入并且生成代理对象的,最后对调用链的生成,以及切面逻辑的织入方式进行了讲解。关于调用链的生成以及切面逻辑的织入,由于使用的方式和Cglib是一样的,因而这里并没有对其细节进行过多描述。


以上所述就是小编给大家介绍的《原 荐 Spring Aop之Jdk代理实现原理详解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Growth Hacker Marketing

Growth Hacker Marketing

Ryan Holiday / Portfolio / 2013-9-3 / USD 10.31

Dropbox, Facebook, AirBnb, Twitter. A new generation of multibillion dollar brands built without spending a dime on “traditional marketing.” No press releases, no PR firms, and no billboards in Times ......一起来看看 《Growth Hacker Marketing》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具