第十五章 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)!