第十八章 Spring之假如让你来写AOP——Advice(通知)下篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇

第四部分——MVC篇

第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇

第五部分——Boot篇

第五十二章 Spring之再进一步——Boot
第五十三章 Spring之假如让你来写Boot——环境篇
第五十四章 Spring之假如让你来写Boot——注解篇(上)
第五十五章 Spring之假如让你来写Boot——注解篇(下)
第五十六章 Spring之假如让你来写Boot——SPI篇
第五十七章 Spring之假如让你来写Boot——配置文件篇(上)
第五十八章 Spring之假如让你来写Boot——配置文件篇(下)
第五十九章 Spring之假如让你来写Boot——番外篇:再谈Bean定义
第六十章 Spring之假如让你来写Boot——自动装配篇
第六十一章 Spring之假如让你来写Boot——番外篇:杂谈Starter
第六十二章 Spring之假如让你来写Boot——番外篇:重构BeanFactory
第六十三章 Spring之假如让你来写Boot——番外篇:再谈ApplicationContext
第六十四章 Spring之假如让你来写Boot——内嵌Web容器篇
第六十五章 Spring之假如让你来写Boot——Main方法启动篇
第六十六章 Spring之最终章——结语篇



前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上回,在上篇 第十七章 Spring之假如让你来写AOP——Advice(通知)上篇 中,A君 已经实现了 Advice(通知) 参数匹配器,只不过现在还没有整合到 Advice(通知) 里边去。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的 IOC容器

    前情提要: A君 已经实现了 Advice(通知) 参数匹配器,还没有整合到 Advice(通知) 里边去 。。。

第十九版(下) Advice(通知)

Joinpoint参数

    昨天 A君 才把参数匹配器折腾完,吃饭都不安生,满脑子都是后续要怎么实现。首先第一个出现的拦路虎就是:无论是Joinpoint、还是returning、throwing,这些参数从哪里来?返回值、异常不是所有通知都有的,可以由各自的通知。Joinpoint这个需要由框架提供,这个参数包含了方法运行的信息

    正当 A君 准备从Joinpoint参数入手时,忽然又发现一个问题:Joinpoint有两个。“@#¥%……&” 这让 A君 一阵骂街。 为什么会有两个?原因很简单 AspectJ 并没有按 AOP联盟 的来。没办法,只能适配了。可问题又来了:要用哪个?经过一番考量,A君 决定用 AspectJ 的,因为 AspectJ 提供的方法更全面,而 AOP 联盟把这些功能分的很细。设计原则之一:最少知道原则。嘿嘿。A君 定义 MethodInvocationProceedingJoinPoint 类,实现 AspectJ 相关接口,代码如下:

import com.hqd.ch03.v19.aop.ProxyMethodInvocation;
import com.hqd.ch03.v19.core.DefaultParameterNameDiscoverer;
import com.hqd.ch03.v19.core.ParameterNameDiscoverer;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.aspectj.runtime.internal.AroundClosure;

import java.lang.reflect.Method;

/**
 * 适配器,将aop联盟的joinpoint接口,包装成Aspectj接口
 */
public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
   
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    private final ProxyMethodInvocation methodInvocation;
    private Object[] args;

    private Signature signature;


    private SourceLocation sourceLocation;

    public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
   
        this.methodInvocation = methodInvocation;
    }

    @Override
    public int getId() {
   
        return 0;
    }

    @Override
    public void set$AroundClosure(AroundClosure arc) {
   
        throw new UnsupportedOperationException();
    }

    @Override
    public Object proceed() throws Throwable {
   
        return this.methodInvocation.invocableClone().proceed();
    }

    @Override
    public Object proceed(Object[] args) throws Throwable {
   
        this.methodInvocation.setArguments(args);
        return this.methodInvocation.invocableClone(args).proceed();
    }

    @Override
    public String toShortString() {
   
        return "execution(" + getSignature().toShortString() + ")";
    }

    @Override
    public String toLongString() {
   
        return "execution(" + getSignature().toLongString() + ")";
    }

    @Override
    public String toString() {
   
        return "execution(" + getSignature().toString() + ")";
    }

    /**
     * 此处返回代理类
     *
     * @return
     */
    @Override
    public Object getThis() {
   
        return this.methodInvocation.getProxy();
    }

    @Override
    public Object getTarget() {
   
        return this.methodInvocation.getThis();
    }

    @Override
    public Object[] getArgs() {
   
        if (args == null) {
   
            this.args = this.methodInvocation.getArguments().clone();
        }
        return this.args;
    }

    @Override
    public Signature getSignature() {
   
        if (this.signature == null) {
   
            this.signature = new MethodSignatureImpl();
        }
        return this.signature;
    }

    @Override
    public SourceLocation getSourceLocation() {
   
        if (this.sourceLocation == null) {
   
            this.sourceLocation = new SourceLocationImpl();
        }
        return this.sourceLocation;
    }

    @Override
    public String getKind() {
   
        return ProceedingJoinPoint.METHOD_EXECUTION;
    }

    @Override
    public StaticPart getStaticPart() {
   
        return this;
    }


    private class SourceLocationImpl implements SourceLocation {
   

        @Override
        public Class getWithinType() {
   
            return methodInvocation.getThis().getClass();
        }

        @Override
        public String getFileName() {
   
            throw new UnsupportedOperationException();
        }

        @Override
        public int getLine() {
   
            throw new UnsupportedOperationException();
        }

        @Override
        public int getColumn() {
   
            throw new UnsupportedOperationException();
        }
    }

    private class MethodSignatureImpl implements MethodSignature {
   
        private String[] parameterNames;
        private Method method = methodInvocation.getMethod();

        @Override
        public Class getReturnType() {
   
            return method.getReturnType();
        }

        @Override
        public Method getMethod() {
   
            return method;
        }

        @Override
        public Class[] getParameterTypes() {
   
            return method.getParameterTypes();
        }

        @Override
        public String[] getParameterNames() {
   
            if (parameterNames == null) {
   
                this.parameterNames = parameterNameDiscoverer.getParameterNames(method);
            }
            return this.parameterNames;
        }

        @Override
        public Class[] getExceptionTypes() {
   
            return method.getExceptionTypes();
        }

        @Override
        public String toShortString() {
   
            return "";
        }

        @Override
        public String toLongString() {
   
            return "";
        }

        @Override
        public String getName() {
   
            return method.getName();
        }

        @Override
        public int getModifiers() {
   
            return method.getModifiers();
        }

        @Override
        public Class getDeclaringType() {
   
            return method.getDeclaringClass();
        }

        @Override
        public String getDeclaringTypeName() {
   
            return getDeclaringType().getTypeName();
        }
    }
}

整个类没有什么出彩的地方,实际上调用都是由 ProxyMethodInvocation 提供。解决完Joinpoint参数适配问题,接下来的问题就是怎么获取到这个参数?直接new吗?如果同一个目标方法,多个通知,每个通知都new一个吗?想到这里,就算是 A君 也察觉出来不妥之处,同一个目标方法,无论有多少个通知,他们的Joinpoint应该是一样的,这就涉及到共享变量了。A君 想起了之前的 ThreadLocal,这不正好可以解决共享变量的问题了吗?共享变量问题解决了,但是要放哪里才能保证每个通知都能获得到?问题还真是一个接着一个呢。A君 不经一阵挠头,想要整个调用链都能获取的到,只能放在最开始的地方,也就是链表头部,并且优先级最高,只有这样才能保证后续的节点能访问的到。那问题就简单,A君 定一个拦截器,让他的优先级最高,ExposeInvocationInterceptor 代码如下:

import com.hqd.ch03.v19.aopalliance.intercept.MethodInterceptor;
import com.hqd.ch03.v19.aopalliance.intercept.MethodInvocation;
import com.hqd.ch03.v19.core.Ordered;
import com.hqd.ch03.v19.thread.NamedThreadLocal;

/**
 * 默认拦截器
 * 优先级最高
 */
public class ExposeInvocationInterceptor implements MethodInterceptor, Ordered {
   
    public static final 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穷儒公羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值