Spring AOP源码02 - 代理的创建

Spring AOP 的实现

Spring AOP 不同于 AspectJ 的 AOP 实现,是在 runtime,通过代理原本的 object 来实现的。
由于 Spring IOC 的 container 的存在,编程过程中是通过container 间接地访问对象,Spring 可以将原本的object 替换为 proxy ,而不侵入原本的代码。
Proxy 有两种产生方式,通过 Java dynamic proxy 和 CGLIB 。本章只涉及 dyanmic proxy 的实现方法。 (dynamic proxy 的基础不在本章范围内)

ProxyFactory

ProxyFactory 提供了手动创建代理的方法。是比较好的一个分析 Proxy 生成过程的切入点。通过设置需要被代理的object 以及对应的advisors 就可以生成相应的代理。它和被代理的object 是一一对应的关系。

// 创建代理示例
ProxyFactory factory = new ProxyFactory();
// 设置target object
factory.setTarget(myBusinessInterfaceImpl);
// 设置interface 及 advice/advisor
factory.setInterface(MyBusinessInterface);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();

其结构也很简单,通过继承 ProxyCreatorSupport ,实现了通用的一些代理的配置以及对Advisor, Advice, interfaces 等的管理

ProxyFactory.png

Proxy 配置和管理

配置主要由 ProxyConfig 完成,Advise/Advisor的注册管理则定义在Advised 里。

ProxyConfig

定义了下列参数。

    private boolean proxyTargetClass = false;
    private boolean optimize = false;
    boolean opaque = false;
    boolean exposeProxy = false;
    private boolean frozen = false;
  • proxyTargateClass. 是否使用CGLIB

  • optimize. 是否采用CGLIB 的优化

  • opaque. 是否允许生成的proxy 被cast 成 Advised ——— 通过Advised 可以知道该proxy 的切面等信息

  • exposeProxy. 是否要把当前proxy 暴露在threadlocal 中。从而可以通过AopContext.currentProxy() 获取该proxy。一个使用场景是,如果希望在被proxy的class中调用proxy的方法,则需要开启此功能,从threadlocal 中获取proxy。

  • frozen. 是否允许继续修改配置,可以通过将其设置为 true 来避免被意外修改。

Advised

Advised 和被代理的object 是一一对应的关系,定义了一些列管理加于被代理object 上的proxy 的方法 —— 管理interfaces, advices, advisors 等

  • ProxyConfig 中的参数的获取
  • Proxied Interface 的管理
  • Targate Source 的管理
  • Advice List 的管理
  • Advisor List 的管理

TargateSource

我们并不直接访问被代理的对象,而是通过TargateSource来访问。TargateSource 封装了被proxy 的object, 提供了对proxied object 的间接访问。这让我们可以不透明地替换下面实际的proxied object。

public interface TargetSource extends TargetClassAware {

    Class<?> getTargetClass();

    boolean isStatic();

    Object getTarget() throws Exception;

    void releaseTarget(Object target) throws Exception;
}

通常用SingletonTargetSource 访问固定 proxied object 就可以了。

AdvisedSupport

上述两个配置由 AdvisedSupport 合起来提供一个convenience implementation

public class AdvisedSupport extends ProxyConfig implements Advised {}

实现细节上,AdvisedSupport 默认使用SingletonTargetSource

advice 添加的时候会被转化为 DefaultPointcutAdvisor,对所有的class/method 都生效。

另外会有对Introduction advice 的一些处理。例如会将 introduction interface 加到 proxied interfce 里。

生成 Proxy

ProxyFactory 将配置和管理的工作都交给了 AdvisedSupport ,而将具体的生成的代理的工作 delegate 给了 AopProxy
生成 proxy 的入口如下

public Object getProxy() {
    return createAopProxy().getProxy();
}

public interface AopProxy {

    Object getProxy();

    Object getProxy(ClassLoader classLoader);
}

AopProxy 两种实现,有基于Java dynamic proxy 的实现 ——JdkDynamicAopProxy 和基于CGLIB 的实现 —— CglibAopProxy

DefaultAopProxyFactory

具体AopProxy 实现会经由工厂类 DefaultAopProxyFactory 决定,通过判断 config 的 optimize, proxyTargetClass, 以及proxied interfaces 来判断用那个一 AopProxy

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

JdkDynamicAopProxy

JdkDynamicAopProxy 是实际生成proxy 的地方
JdkDynamicAopProxy 实现 InvocationHandler, 用Java dynamic proxy生成代理

Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

Proxied Interfaces

而proxiedInterfaces 由AopProxyUtils.completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) 生成,除了我们添加到ProxyFactory 的interface 之外,还会添加三个特殊的 interface, SpringProxy, Advised, DecoratingProxy

SpringProxy - 空interface, 用来标记该object 是spring 生成的 proxy

Advised - 令proxy 也能访问到创建它的proxy 信息,如果 ProxyConfig.opaque = true 则不添加这个 interface。

DecoratingProxy - 令AOP 外部的 package 也能检查 proxy 上的 interface

AdvisedDecoratingProxy 上的方法的调用会被拦截下来,具体实现视AopProxy 的实现而定。

invoke()

invoke()JdkDynamicAopProxy 的核心方法,调用的拦截,转发都是在这里完成。

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

    try {
        ...

        /*
        拦截对 DecoratingProxy 和 Advised 方法的调用
         */
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        /*
        在threadlocal 中暴露当前proxy
         */
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        /*
        从TargetSource 中获取object
         */
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }

        /*
        创建interceptor 链并调用
         */
        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();
        }

        /*
        如果返回值为该object, 则尝试返回 proxy。
        以及 null 检查
         */
        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()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    /*
    最后恢复threadlocal 中之前的proxy
     */
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

拦截链

不管是 dynamic proxy 还是 cglib 都只是实现代码“织入“的手段。而真正的逻辑还是定义在拦截链上。

AdvisorChainFactory

getInterceptorsAndDynamicInterceptionAdvice 返回条件可能符合当前调用的所有Advisor。

基本逻辑是遍历所有注册的advisor,能用pointcut 过滤掉的都先过滤掉,然后用 Advice adapter 转化成MethodInterceptor

注意由于ProxyFactory 并不会在生成proxy 之前,对注册的advisor 先做一次过滤,所以实际每次调用都会都会进行这样的遍历,所以在注册advisor 的时候需要留意,不要将对当前对象无效的advisor 注册进来。

public interface AdvisorChainFactory {

    List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass);
}

AdvisorChainFactory 只有 DefaultAdvisorChainFactory 一个实现。

public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, Class<?> targetClass) {

        List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

        for (Advisor advisor : config.getAdvisors()) {
            if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                /*
                PointcutAdvisor 的情况下,先用其 ClassFilter 筛一下
                 */
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    /*
                    使用 Advice Adapter 转化 PointcutAdvisor 中的 advice
                     */
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    /*
                    用 MethodFilter 再筛一遍
                     */
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        /*
                        static 的 advisor 的interceptor 现在已经可以直接放入拦截链里了
                        但如果还需要 runtime 的检查 (检查调用时传入的 arguments), 
                        则需要将 InterceptorAndDynamicMethodMatcher 将interceptor 和 method matcher wrap 起来供runtime 检查用
                        这也是runtime 的 pointcut 性能较差的原因
                         */
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            /*
            IntroductionAdvisor 则简单取出 interceptor 即可
             */
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            /*
            其他(用户自定义的) advisor
             */
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
    }
    ...
}

ReflectiveMethodInvocation

ReflectiveMethodInvocation 将一次调用和上面的拦截链组合起来

public Object proceed() throws Throwable {
    /*
    拦截链遍历完,调用原本的方法
     */
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    /*
    获取并调用拦截器的 invoke() 方法,传入自身
    而拦截器中,执行了自身逻辑之后由会调用 MethodInvocation.proceed() 方法,
    从而递归地实现对拦截链的遍历
     */
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        /*
        做runtime 的 MethodMatcher 检查
         */
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            return proceed();
        }
    }
    else {
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

至此整个拦截调用,用了一种很巧妙的方法,将advice代码的执行时间交给了拦截器自己决定。这种方法灵活度更高也更容易实现。而不是通过维护拦截链顺序,比如将before advice 放在原方法调用前,after returing 放在调用链后。

总结

  1. Spring AOP runtime 的代码织入有两种方式,Java dynamic proxy 和 cglib。代码织入方式和 AOP 的实现调用无关。

  2. AOP 后的方法调用每次都会遍历过滤所有的advisor,所以不要注册无效的advisor。

  3. 调用链中的 Interceptor 通过改变 MethodInvocation.invoke() 的位置来改变切入时机。ReflectiveMethodInvocation 封装方法调用,递归地调用拦截器。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容