spring--aop_2_源码分析之MethodInterceptor

前提:

https://blog.csdn.net/convict_eva/article/details/81084833

https://blog.csdn.net/convict_eva/article/details/81101432

前两篇分析了aop 两种方式实现的大致流程和方式,在这两种实现方式中都有一个很重要的方法获取拦截器链

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

所有的aop增强方法都封装在一个个拦截器中,然后根据这个拦截器链的调用进行增强。

此方法的实现是在advised 对象实现的(advised 是 AdvisedSupport 对象的实例,ProxyFactoryBean 是AdvisedSupport 子类)

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

MethodCacheKey cacheKey = new MethodCacheKey(method);

//这里使用了缓存来提高效率,第一次获取还是要创建的。

//使用了 advisorChainFactory 生成拦截器链,advisorChainFactory 是 DefaultAdvisorChainFactory 的实例

List<Object> cached = this.methodCache.get(cacheKey);

if (cached == null) {

cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(

this, method, targetClass);

this.methodCache.put(cacheKey, cached);

}

return cached;

}

advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()源码:

@Override

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(

Advised config, Method method, @Nullable Class<?> targetClass) {

// This is somewhat tricky... We have to process introductions first,

// but we need to preserve order in the ultimate list.

//初始化拦截器链,大小是通知器个数。这个配置就是 ProxyFactoryBean 的 interceptNames 的属性配置。

List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);

Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());

boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);

//获取 AdvisorAdapterRegistry 实例,AdvisorAdapterRegistry被称为“注册器”

//利用它来对从ProxyFactoryBean 配置中得到的通知器进行适配,从而获得相应的拦截器,再把拦截器加入到拦截器链中

AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

for (Advisor advisor : config.getAdvisors()) {

if (advisor instanceof PointcutAdvisor) {

// Add it conditionally.

PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;

if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {

//通过  AdvisorAdapterRegistry 获取拦截器,这个方法封装了advice 织入的实现入口

MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

//获取 MethodMatcher ,用来匹配目标方法,如果匹配就放入到拦截器链中。pointcut 就是在这里使用的

MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();

if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {

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));

}

}

}

}

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));

}

}

else {

Interceptor[] interceptors = registry.getInterceptors(advisor);

interceptorList.addAll(Arrays.asList(interceptors));

}

}

return interceptorList;

}

通过GlobalAdvisorAdapterRegistry 获取一个单例的 AdvisorAdapterRegistry 实例

AdvisorAdapterRegistry 的实现是 DefaultAdvisorAdapterRegistry

DefaultAdvisorAdapterRegistry.getInterceptors() 方法封装了advice 织入实现的入口

注:在ProxyFactoryBean 中已经把配置的advice封装成了 Advisor,第一篇有介绍。

DefaultAdvisorAdapterRegistry 类源码分析:

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

//advisor 适配器列表

private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

/**

* Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.

定义了3个适配器, 就是这3个适配器为aop提供编织能力。

这3个适配器和spring aop提供的advice增强功能相对就的

这3个是spring aop advice的封装实现

*/

public DefaultAdvisorAdapterRegistry() {

registerAdvisorAdapter(new MethodBeforeAdviceAdapter());

registerAdvisorAdapter(new AfterReturningAdviceAdapter());

registerAdvisorAdapter(new ThrowsAdviceAdapter());

}

//省略方法......

@Override

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {

List<MethodInterceptor> interceptors = new ArrayList<>(3);

//从advisor通知器中获取advice通知

Advice advice = advisor.getAdvice();

if (advice instanceof MethodInterceptor) {

//如果是MethodInterceptor 直接加入到MethodInterceptor list 中不需要适配

interceptors.add((MethodInterceptor) advice);

}

//对通知器进行适配,使用构造时已经配置好的adapter(上面3种adapter)

//然后从对应的adapter 中取出封装好的aop编织功能的拦截器

//通过adapter.getInterceptor() 方法返回对应的是3种 MethodInterceptor。

//这里就是第一篇分析的invoke获取拦截器链调用的 MethodInterceptor

for (AdvisorAdapter adapter : this.adapters) {

if (adapter.supportsAdvice(advice)) {

interceptors.add(adapter.getInterceptor(advisor));

}

}

if (interceptors.isEmpty()) {

throw new UnknownAdviceTypeException(advisor.getAdvice());

}

return interceptors.toArray(new MethodInterceptor[0]);

}

//可以自己定义adapter

@Override

public void registerAdvisorAdapter(AdvisorAdapter adapter) {

this.adapters.add(adapter);

}

}

adapter 有三种实现方式

这里使用MethodBeforeAdviceAdapter说明:就是判断一下advice类型,然后把advice 封装成MethodInterceptor

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

//这里验证是否是 MethodBeforeAdvice 类型

@Override

public boolean supportsAdvice(Advice advice) {

return (advice instanceof MethodBeforeAdvice);

}

@Override

public MethodInterceptor getInterceptor(Advisor advisor) {

//获取advice,创建 MethodBeforeAdviceInterceptor 对象并返回

MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();

return new MethodBeforeAdviceInterceptor(advice);

}

}

这里获取了advisor 对应的 MethodInterceptor之后,就要对这MethodInterceptor 进行匹配了

匹配使用的是通过 advisor.getPointcut().getMethodMatcher() 获取到 MethodMatcher 对象进行匹配验证的。

总结:

    通过配置 ProxyFactoryBean interceptorNames 属性来配置advice,ProxyFactoryBean 调用 initializeAdvisorChain() 方法把这些配置解析成 advisor链(advisor 包含了advice 和 pointcut)。

    在代理对象调用目标方法时,通过 DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice() 方法获取到拦截器链,这个方法又是调用 DefaultAdvisorAdapterRegistry.getInterceptors() 方法生成的拦截器链。

    spring aop 就是通过拦截器模式调用这些生成的拦截器实现的aop功能

————————————————

版权声明:本文为CSDN博主「Jamin_Ma」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/convict_eva/article/details/81105144

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

推荐阅读更多精彩内容