自学内容网 自学内容网

第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇

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

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


    书接上回,在上篇 第十四章 Spring之假如让你来写AOP——雏形篇 中,A君 已经完成了初稿,先让 AOP 跑起来。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

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

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

    前情提要: A君 完成了初稿,让 AOP 先跑起来了 。。。

第十七版 Joinpoint(连接点)

    “不错,A君,能在这么短的时间内整出个初稿了。” 老大 看着 A君 提交上来的代码说到

    “哪里,哪里?” A君 嘴上谦虚,暗道:“别有但是就行了”

    “但是,Joinpoint(连接点) 需要更细致化的区分,比如说:你这个链式调用,在整个调用链中,可能会有一些共享变量,要将他放在哪里?而且我看你并未实现环绕通知,环绕通知是很特殊的一个通知,它可以甚至修改入参,涉及到修改的时候,不得不谨慎一点了,假如有人在参数数组中添加元素,这时候就会导致整个调用链失败,这也是不得不考虑的部分。” 老大 说到 “拿回去改改吧”

    A君 默然,老大 说的确是有道理,让人没法反驳。不过对于 老大 说的这几个点,也好解决,在执行通知调用链的时候,用个Map保存共享数据即可,至于入参数组与有可能被意外修改。这个就更简单了,clone一份就行了

    思量完毕,A君 定义 ProxyMethodInvocation 接口,代码如下:

public interface ProxyMethodInvocation extends MethodInvocation {
    /**
     * 获取代理对象
     *
     * @return
     */
    Object getProxy();

    /**
     * 复制对象
     *
     * @return
     */
    MethodInvocation invocableClone();

    /**
     * 复制参数
     *
     * @param arguments
     * @return
     */
    MethodInvocation invocableClone(Object... arguments);

    /**
     * 设置参数
     *
     * @param arguments
     */
    void setArguments(Object... arguments);

    /**
     * 设置调用链共享数据
     *
     * @param key
     * @param value
     */
    void setUserAttribute(String key, @Nullable Object value);

    @Nullable
    Object getUserAttribute(String key);
}

接着,让 ReflectiveMethodInvocation 做对应的实现即可,修改如下:

在这里插入图片描述

完整代码如下:

import com.hqd.ch03.v17.aop.ProxyMethodInvocation;
import com.hqd.ch03.v17.aopalliance.intercept.MethodInterceptor;
import com.hqd.ch03.v17.aopalliance.intercept.MethodInvocation;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 连接点类,用以执行通知及目标方法
 */
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    private Object proxy;
    private Object target;
    private Method method;
    private Object[] args;
    private List<? extends MethodInterceptor> mis;
    private Map<String, Object> userAttributes = new HashMap<>();
    private int currentInterceptorIndex = -1;

    public ReflectiveMethodInvocation(Object target, Object proxy, Method method,
                                      Object[] args, List<MethodInterceptor> mis) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.args = args;
        this.mis = mis;
    }

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

    @Nonnull
    @Override
    public Object[] getArguments() {
        return args;
    }

    @Override
    public void setArguments(Object... arguments) {
        this.args = arguments;
    }

    @Override
    public MethodInvocation invocableClone() {
        Object[] args = this.args.clone();
        return invocableClone(args);
    }

    @Override
    public MethodInvocation invocableClone(Object... arguments) {
        try {
            ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
            clone.args = arguments;
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void setUserAttribute(String key, @Nullable Object value) {
        this.userAttributes.put(key, value);
    }

    @Nullable
    @Override
    public Object getUserAttribute(String key) {
        return this.userAttributes.get(key);
    }

    @Nullable
    @Override
    public Object proceed() throws Throwable {
        /**
         * 链式调用通知
         */
        if (mis.size() - 1 == currentInterceptorIndex) {
            return getMethod().invoke(target, args);
        }
        MethodInterceptor methodInterceptor = this.mis.get(++currentInterceptorIndex);

        return methodInterceptor.invoke(this);
    }

    @Nullable
    @Override
    public Object getThis() {
        return target;
    }

    @Nonnull
    @Override
    public AccessibleObject getStaticPart() {
        return method;
    }

    @Override
    public Object getProxy() {
        return proxy;
    }


    public Map<String, Object> getUserAttributes() {
        return userAttributes;
    }
}

“没想到 Joinpoint(连接点) 这么简单。” A君 窃喜,今天又能准时下班了。A君 做个简单的测试意思一下,测试代码如下:

  @Test
    public void v17() throws Throwable {

        System.out.println("############# 第十七版  Joinpoint(连接点) #############");

        v16();
    }

在这里插入图片描述

    “搞定,今天真是愉快的一天”

tips:Joinpoint(连接点) 核心在于通知的调用链,这一点整明白了,其他就没什么了

请添加图片描述

总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)


原文地址:https://blog.csdn.net/weixin_42789334/article/details/143117310

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!