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源码阅读目录
- 第一部分——IOC篇
- 第二部分——AOP篇
- 前言
- 尝试动手写IOC容器
- 第二十版 Aspect(切面)篇
- 切点、通知的良媒——Advisor
- 切面真正的管理者——Advised
- 总结
前言
对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》
书接上上回,在上上篇 第十八章 Spring之假如让你来写AOP——Advice(通知)下篇 中,A君 废了老大的劲,终于实现了 Advice(通知) 的内容,虽然道路曲折,但是前途是光明的。接下来看看 A君 会有什么骚操作吧
尝试动手写IOC容器
出场人物:A君(苦逼的开发)、老大(项目经理)
背景:老大要求A君在一周内开发个简单的 IOC容器
前情提要: A君 已经完成 Advice(通知) 的内容 。。。
第二十版 Aspect(切面)篇
“可以啊,A君!Advice(通知) 弄的出人意表,比我想象中的还好” 老大 不吝夸赞道
“开玩笑,拼了老命才弄出来的。” A君 心道
“接下来,我们来讨论下 Aspect(切面),Aspect(切面) 说到底,就是用来管理 Advice(通知) 的。这部分内容你自由发挥把,别忘了,面向接口编程,还有抽象类。” 老大 今天难得没说什么,只是这“自由发挥”的要求也太抽象了
切点、通知的良媒——Advisor
开完会,A君 独自坐在位置上思考:Aspect(切面) 要如何实现?直接定义一个接口,让它拥有CRUD的方法吗?A君 随手写了个例子,如下:
import com.hqd.test.aop.Advice;
import com.hqd.test.aop.Pointcut;import java.util.List;public class Advisor {private List<Advice> adviceList;private List<Pointcut> pointcuts;public void addAdvice(Advice advice) {this.adviceList.add(advice);}public void addPointcut(Pointcut pointcut) {this.pointcuts.add(pointcut);}
}
额,不太对头,这无法知道 Advice(通知) 和 Pointcut(切点) 的对应关系。通过Map封装?这又不好管理了。算了,A君 索性把 Advice(通知) 和 Pointcut(切点) 包装成一个类,这样方便管理。A君 定个 Advisor 接口,用以维护 Advice(通知) 和 Pointcut(切点) 的对应关系。代码如下:
import com.hqd.ch03.v20.aopalliance.aop.Advice;/*** 维护切点和通知的映射关系接口*/
public interface Advisor {Advice EMPTY_ADVICE = new Advice() {};Advice getAdvice();
}
Advisor 是个顶级接口,本身只有最通用的功能。还需要子接口去拓展其功能,例如:切点信息。额,那为什么不直接在 Advisor 接口中一起定义。嘿,因为还有个 引介增强,它是以类为维度进行增强的,并不需要切点。PointcutAdvisor 接口如下:
/*** 切点和通知对应关系*/
public interface PointcutAdvisor extends Advisor {Pointcut getPointcut();
}
引介增强 也需要维护映射关系。它是的切点比较特殊,是 ClassFilter,而不是 Pointcut。接口如下:
/*** 引介增强映射关系*/
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {ClassFilter getClassFilter();/*** 检验接口** @throws IllegalArgumentException*/void validateInterfaces() throws IllegalArgumentException;
}
Advisor 本身的定义并不复杂,所以其实现也没有什么奇特的地方。虽然简单,可是 老大 言犹在耳。A君 稍微思索了下:Advisor 的实现不就是存个通知和切点就完事了,还有哪里需要提取抽象类的?A君 转念一想:现在框架切点匹配有一大堆实现,让用户一顿 new Advisor(new Advice(),new Pointcut())
或者xml中配置一堆bean,再ref,好像也不是事。说好的,简单呢?说好的,开箱即用呢?切点的问题解决了。那么通知也需要这么处理吗?应该没必要,如果是通过实现接口的通知,那么压根就没法处理,总不能特地定义个子类,只是为了换个类型吧。至于 AspectJ 的通知,它另有去处。嘿嘿。那,这些不同实现的 Advisor,有什么公共点?要说公共,倒是也有一点,那就是顺序。要知道 Advisor 是单个切点和通知的管理者,通知是有顺序的,那么管理者也应该是有序的,而且这个顺序还不能乱来,得用通知的才行。这么一想,整个 Advisor 的轮廓就出来了。“果然,还是得深思熟虑呀!不然又得挨叼了。” A君 感叹
思量完毕,A君 先提取一个抽象类,不干别的,把排序提取出来就行了。AbstractPointcutAdvisor 代码如下:
import com.hqd.ch03.v20.aop.PointcutAdvisor;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import com.hqd.ch03.v20.core.Ordered;public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered {private Integer order;@Overridepublic int getOrder() {if (this.order != null) {return this.order;}Advice advice = getAdvice();if (advice instanceof Ordered) {return ((Ordered) advice).getOrder();}return Ordered.LOWEST_PRECEDENCE;}public void setOrder(int order) {this.order = order;}}
都做到这一步了,那索性把通知也提取成抽象类算了。A君 在定义个 AbstractGenericPointcutAdvisor 类。代码如下:
import com.hqd.ch03.v20.aopalliance.aop.Advice;public abstract class AbstractGenericPointcutAdvisor extends AbstractPointcutAdvisor {private Advice advice = EMPTY_ADVICE;@Overridepublic Advice getAdvice() {return this.advice;}public void setAdvice(Advice advice) {this.advice = advice;}
}
好啦,接口和抽象类都搞定了,接下来要弄的事,就是把不同切点匹配映射到对应的 Advisor 实现就行了。俗话说:柿子拣软的捏。A君 先拿 NameMatchMethodPointcut 开刀。只需要把 NameMatchMethodPointcut 创建出来就行了。** NameMatchMethodPointcutAdvisor** 代码如下:
import com.hqd.ch03.v20.aop.ClassFilter;
import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aopalliance.aop.Advice;public class NameMatchMethodPointcutAdvisor extends AbstractGenericPointcutAdvisor {private final NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();public NameMatchMethodPointcutAdvisor() {}public NameMatchMethodPointcutAdvisor(Advice advice) {setAdvice(advice);}public void setClassFilter(ClassFilter classFilter) {this.pointcut.setClassFilter(classFilter);}public NameMatchMethodPointcut addMethodName(String name) {return this.pointcut.addMethodName(name);}public Pointcut getPointcut() {return this.pointcut;}
}
再来个切点表达式的,AspectJExpressionPointcutAdvisor 代码如下:
import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aop.support.AbstractGenericPointcutAdvisor;public class AspectJExpressionPointcutAdvisor extends AbstractGenericPointcutAdvisor {private final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();public String getExpression() {return this.pointcut.getExpression();}public void setExpression(String expression) {this.pointcut.setExpression(expression);}@Overridepublic Pointcut getPointcut() {return this.pointcut;}
}
其他切点匹配都是类似的,这里 A君 就不再一一赘述了。NameMatchMethodPointcutAdvisor 这一类的 Advisor 都是提供给用户,方便用户使用的。但也不因此而忘记本心——自由搭配。A君 再定义一个 DefaultPointcutAdvisor 类,这个类不做任何限制,只要实现了 Advice 和 Pointcut 接口就行。代码如下:
import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aopalliance.aop.Advice;public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor {private Pointcut pointcut = Pointcut.TRUE;public DefaultPointcutAdvisor() {}public DefaultPointcutAdvisor(Advice advice) {this(Pointcut.TRUE, advice);}public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {this.pointcut = pointcut;setAdvice(advice);}@Overridepublic Pointcut getPointcut() {return this.pointcut;}public void setPointcut(Pointcut pointcut) {this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);}
}
上诉提到的 Advisor 实现,都是配菜。A君 再次把目光转移的 AspectJ 上,啥,你说 AspectJExpressionPointcutAdvisor?别闹,它不是,它只有切点表达式,还差通知呢。之前,A君 就把除 引介通知 外的其他通知,都归到了 AbstractAspectJAdvice 类下了。现在 Advice 的类图可是这样子的:
既然和 AspectJ 强绑定了,那就是给他个单独的实现吧。说是怎么说,实现起来也没啥东西。只是不把它归为通用实现罢了。A君 定义 AspectJPointcutAdvisor 类,代码如下:
import com.hqd.ch03.v20.aop.Pointcut;
import com.hqd.ch03.v20.aop.PointcutAdvisor;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import com.hqd.ch03.v20.core.Ordered;public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {/*** 通知*/private final AbstractAspectJAdvice advice;/*** 切点*/private final Pointcut pointcut;private Integer order;public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {this.advice = advice;this.pointcut = advice.getPointcut();}@Overridepublic int getOrder() {if (this.order != null) {return this.order;} else {return this.advice.getOrder();}}public void setOrder(int order) {this.order = order;}@Overridepublic Advice getAdvice() {return this.advice;}@Overridepublic Pointcut getPointcut() {return this.pointcut;}public String getAspectName() {return this.advice.getAspectName();}
}
还有 引介通知,只是实现了不同接口,其他地方都是类似的。DefaultIntroductionAdvisor 代码如下:
import com.hqd.ch03.v20.aop.ClassFilter;
import com.hqd.ch03.v20.aop.DynamicIntroductionAdvice;
import com.hqd.ch03.v20.aop.IntroductionAdvisor;
import com.hqd.ch03.v20.aop.IntroductionInfo;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import com.hqd.ch03.v20.core.Ordered;import java.util.LinkedHashSet;
import java.util.Set;public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFilter, Ordered {private final Advice advice;private final Set<Class<?>> interfaces = new LinkedHashSet<>();private int order = Ordered.LOWEST_PRECEDENCE;public DefaultIntroductionAdvisor(Advice advice) {this(advice, (advice instanceof IntroductionInfo ? (IntroductionInfo) advice : null));}public DefaultIntroductionAdvisor(Advice advice, IntroductionInfo introductionInfo) {this.advice = advice;if (introductionInfo != null) {Class<?>[] introducedInterfaces = introductionInfo.getInterfaces();if (introducedInterfaces.length == 0) {throw new IllegalArgumentException("IntroductionInfo defines no interfaces to introduce: " + introductionInfo);}for (Class<?> ifc : introducedInterfaces) {addInterface(ifc);}}}public void addInterface(Class<?> ifc) {if (!ifc.isInterface()) {throw new IllegalArgumentException("Specified class [" + ifc.getName() + "] must be an interface");}this.interfaces.add(ifc);}@Overridepublic ClassFilter getClassFilter() {return this;}@Overridepublic void validateInterfaces() throws IllegalArgumentException {for (Class<?> ifc : this.interfaces) {if (this.advice instanceof DynamicIntroductionAdvice &&!((DynamicIntroductionAdvice) this.advice).implementsInterface(ifc)) {throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +"does not implement interface [" + ifc.getName() + "] specified for introduction");}}}@Overridepublic Advice getAdvice() {return this.advice;}@Overridepublic Class<?>[] getInterfaces() {return this.interfaces.toArray(new Class[]{});}@Overridepublic boolean matches(Class<?> clazz) {return true;}@Overridepublic int getOrder() {return order;}public void setOrder(int order) {this.order = order;}
}
切面真正的管理者——Advised
Advisor 只能表示单个切点和通知的关系,要是现在开发,通知往往不止一个,想要管理多个通知,需要再进行一次抽象。所谓管理者,那CRUD是基本功能。A君 之前因为没定义接口,不知道被 老大 骂了多少次,现在 A君 学聪明了,干啥都先定义个接口再说。A君 定义 Advised 作为管理 Advisor 的接口。代码如下:
import com.hqd.ch03.v20.aopalliance.aop.Advice;public interface ![Advised](https://i-blog.csdnimg.cn/direct/999c9f2cc48743e99c926ce85d3c5aa6.png)extends TargetClassAware {Advisor[] getAdvisors();default int getAdvisorCount() {return getAdvisors().length;}void addAdvisor(Advisor advisor);void addAdvisor(int pos, Advisor advisor);boolean removeAdvisor(Advisor advisor);void removeAdvisor(int index);int indexOf(Advisor advisor);boolean replaceAdvisor(Advisor a, Advisor b);void addAdvice(Advice advice);void addAdvice(int pos, Advice advice);boolean removeAdvice(Advice advice);int indexOf(Advice advice);}
这个接口没啥东西,就是基础CRUD。当然了,管理 Advisor 只是它的一部分功能,其他配置应该归它管理,这是后话了。照目前来说,Advised 极其简单。 AdvisedSupport 不在是之前的泥腿子了,它作为 Advised 标准实现,A君 对AdvisedSupport 类进行改造,让其实现 Advised 。改造如下:
import com.hqd.ch03.v20.aop.Advised;
import com.hqd.ch03.v20.aop.Advisor;
import com.hqd.ch03.v20.aop.DynamicIntroductionAdvice;
import com.hqd.ch03.v20.aop.IntroductionInfo;
import com.hqd.ch03.v20.aop.support.DefaultIntroductionAdvisor;
import com.hqd.ch03.v20.aop.support.DefaultPointcutAdvisor;
import com.hqd.ch03.v20.aopalliance.aop.Advice;
import org.apache.commons.collections4.CollectionUtils;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;/*** advice管理类*/
public class AdvisedSupport implements Advised {AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();private List<Advisor> advisors = new ArrayList<>();private List<Class<?>> interfaces = new ArrayList<>();private Object target;public AdvisedSupport(Object target) {this.target = target;}public void addAdvisors(Collection<Advisor> advisors) {if (CollectionUtils.isNotEmpty(advisors)) {this.advisors.addAll(advisors);}}public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {return this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);}public Advisor[] getAdvisors() {return advisors.toArray(new Advisor[]{});}@Overridepublic void addAdvisor(Advisor advisor) {if (advisor != null) {this.advisors.add(advisor);}}@Overridepublic void addAdvisor(int pos, Advisor advisor) {this.advisors.add(pos, advisor);}@Overridepublic boolean removeAdvisor(Advisor advisor) {return this.advisors.remove(advisor);}@Overridepublic void removeAdvisor(int index) {this.advisors.remove(index);}@Overridepublic int indexOf(Advisor advisor) {return this.advisors.indexOf(advisor);}@Overridepublic boolean replaceAdvisor(Advisor a, Advisor b) {int index = indexOf(a);if (index == -1) {return false;}removeAdvisor(index);addAdvisor(index, b);return true;}@Overridepublic void addAdvice(Advice advice) {addAdvice(this.advisors.size(), advice);}@Overridepublic void addAdvice(int pos, Advice advice) {/*** 根据通知类型,创建对于的Advisor*/if (advice instanceof IntroductionInfo) {addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));} else if (advice instanceof DynamicIntroductionAdvice) {throw new RuntimeException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");} else {addAdvisor(pos, new DefaultPointcutAdvisor(advice));}}@Overridepublic boolean removeAdvice(Advice advice) {int index = indexOf(advice);if (index == -1) {return false;} else {removeAdvisor(index);return true;}}@Overridepublic int indexOf(Advice advice) {for (int i = 0; i < this.advisors.size(); i++) {Advisor advisor = this.advisors.get(i);if (advisor.getAdvice() == advice) {return i;}}return -1;}public List<Class<?>> getInterfaces() {return interfaces;}public void addInterfaces(Class<?>... classes) {this.interfaces.addAll(Arrays.asList(classes));}public Object getTarget() {return target;}@Overridepublic Class<?> getTargetClass() {return this.target.getClass();}
}
到这里,切面基本就完事了。A君 开始着手写测试代码,以 前置通知 为例。代码如下:
@Testpublic void v20() throws Throwable {System.out.println("############# 第二十版 Aspect(切面)篇 #############");com.hqd.ch03.v20.aop.framework.aspectj.AspectJExpressionPointcut pointcut =new com.hqd.ch03.v20.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");Object ap = new AopBean();//前置通知AopTest aop = new AopTest();Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);List list = new ArrayList<>();//前置通知com.hqd.ch03.v20.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =new com.hqd.ch03.v20.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);Advisor advisor = new AspectJPointcutAdvisor(beforeAdvice);list.add(com.hqd.ch03.v20.aop.interceptor.ExposeInvocationInterceptor.ADVISOR);list.add(advisor);com.hqd.ch03.v20.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v20.aop.framework.ProxyFactory(ap);proxyFactory.addAdvisors(list);AopBean proxy = (AopBean) proxyFactory.getProxy();proxy.test1("11");}
测试结果如下:
整体来说,Aspect(切面) 还是比较简单的,并没有复杂的逻辑。如果没有历史包袱(详情见 第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计),相信会更为简单。不过为了广大使用者,兼容是必然的。“Aspect(切面) 也搞定了啦!” A君 欢呼。距离 AOP 实现又近了一步
总结
正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)