Spring扩展点-MethodOverride(运行时方法重写)

1. 了解什么是Spring当中的MethodOverride

Spring框架当中,为我们提供了一个机制,称为MethodOverride,我们称之为运行时方法重写。

1.1 XML版本的IOC容器中的相关实现

其实运行时方法重写,在SpringXML版本当中就已经提供了实现,可以通过配置lookup-methodreplace-method这两个标签去进行的相关配置,就会使用到运行时方法重写这个玩意。

image.png

XML版IOC容器中,我们来看看它的XmlBeanDefinitionReader组件对于bean标签的解析过程中,其中就包含了对这两个标签的解析

image.png

这个类所在的类和方法是BeanDefinitionParserDelegate.parseBeanDefinitionElement

我们来看关键的对ReplaceOverrideLookupOverride的处理工作

image.png
image.png

我们可以看到,针对于replace-method标签,被解析成为一个ReplaceMethodOverride对象,加入到BeanDefinition当中,而对于lookup-method标签,则是被解析成为一个LookupMethodOvrride对象。

我们来看MethodOverride和这些组件有什么关系?

image.png

我们可以看到,MethodOverride的两个实现就是LookupOverrideReplaceOverride

MethodOverride对象被维护在哪里?实际上在BeanDefinition当中就有维护这样的一个列表,用来存放需要进行运行时方法重写的方法信息。

image.png

1.2 注解版IOC容器中的实现

在注解版中,并未提供ReplaceMethodOverride的实现,但是对于LookupOverride,是有提供相关的实现的。

在注解版的IOC容器中,提供了@Lookup这样一个注解,专门用来提供实现LookupMethodOverride,对应的就是XML版本当中对于lookup-method的实现。

1.3 ReplaceMethodOverrideLookupOverride如何使用?

LookupMethodOverride的功能,是提供让方法能够返回自定义的对象,而不是调用目标方法去return对象,也就是实现的是方法返回值的替换功能。

比如,可以通过如下这样的注解版的配置方式去使用到LookupOverride

    @Lookup
    public User getUser() {
        return null;
    }

在xml当中,对应的等价代码是

image.png

它实现的功能是:

  • 1.如果@Lookup注解配置了value属性,那么,将会从容器中按照beanName去获取Bean作为方法的返回值去进行返回;如果没配置value属性,那么将会按照方法的返回值作为beanType,从容器中去获取Bean
  • 2.lookup-method指定的方法本身可以是抽象方法(比如接口当中的方法),因为并不会执行到真实的方法的真正逻辑,而是走的代理逻辑去从容器中获取要返回的对象。
  • 3.因为lookup-method实际上就是调用getBean从容器中拿对象,因此它可以实现原型Bean的获取功能。

ReplaceMethodOverride实现的功能是什么?它其实更有方法重写这句话的韵味,在运行时去替换目标方法的逻辑。

我们首先编写一个自定义的Replacer,实现MethodReplacer接口,并实现它的reimplement方法,在这个方法当中执行的逻辑,实际上就你要替换的方法逻辑。

    public static class Replacer implements MethodReplacer {
        @Override
        public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
            return null;
        }
    }

下面要做的,就是在XML当中去进行配置(注解版似乎并未提供ReplaceMethodOverride的相关实现)。

image.png

这样,在运行时,目标方法,就会被替换成为我们自定义的Replacer组件当中实现的方法。

1.4 @Lookup注解的扫描

既然注解版容器也有提供@Lookup的支持,那么必然会存在扫描这个注解的逻辑,那么这个注解究竟是在何处被扫描的呢?

这个注解的扫描其实隐藏的比较深,在AutowiredAnnotationBeanPostProcessor这个组件的推断构造器的同时对@Lookup注解去进行处理。

而这个组件本身的作用是什么?其实就是处理@Autowired/@Resource/@Inject这些注解的自动注入逻辑,另外一方面是使用一大段的逻辑去进行推断即将要进行创建Bean的构造器,最终根据候选构造器选出一个合适的构造器去创建目标对象。

我们可以看到它是遍历beanClass以及它的所有父类当中的所有标注了@Lookup注解的方法,将其封装成为LookupOverride对象,加入到BeanDefinition当中。

image.png

2. Spring当中如何实现MethodOverride

2.1 对MethodOverride的实现

其实实现运行时方法重写很简单,就是创建代理嘛,代理的目的不就是运行时方法重写嘛,因此很显然是创建代理去实现的逻辑。我们从Spring源码当中找到如何创建代理的逻辑?

doGetBean当中,有如下的代码

image.png

很显然,我们可以知道,创建Bean的真正逻辑,一定是在createBeanInstance这个方法当中的。

image.png

在这个方法当中,首先有两个逻辑的判断,就是使用Supplier去创建Bean(这个一般使用的少,但是其实有在使用,如果有了解过SpringCloud-OpenFeign组件的源码的朋友,相信会见过这个Supplier的使用),以及使用FactoryMethod(@Bean标注的方法,这个用的就比较多了)去创建逻辑。这部分我们都暂时不管,pass掉。

image.png

接下来,就是根据自动注入的类型(XML版本当中配置的自动注入的情况),以及推断出来的构造器的类型,选用instantiateBean或者是autowireConstructor这两个方法去创建对象。

其实最终都会走到SimpleInstantiationStrategy中的下面这样的一个方法,这里才是最终的逻辑。

image.png

如果没有MethodOverride的情况,直接使用构造去创建对象就行了,如果有MethodOverride的情况,那么需要走到下面的逻辑,我们从注释当中已经看清楚了,使用CGLIB去生成目标子类。

image.png

我们来看真正进行实例化的逻辑,我们发现已经差不多到底了,已经到了设置Callbacks的层面了,设置了一个LookupOverrideMethodInterceptor和一个ReplaceOverrideMethodInterceptor这两个关键的回调。

image.png

为什么设置了多个回调呢?怎么确定运行时要使用哪个回调?其实这就涉及到CGLIB代理当中的CallbackFilter,当accept方法返回了Callbacks数组中的哪个位置的索引index,运行时将会采用的目标回调就是Callbacks[index]所在的Callback

image.png

2.2 ReplaceMethodOverrideLookupMethodOverride对应的回调

对于LookupMethodOverride的实现,我们可以看到,就是如果配置了name,将会采用按名去进行注入,如果没配置name,那么将会按照方法的返回值作为type去进行注入,蛮简单的。

image.png

对于ReplaceMethodOverride的实现,其实相当简单,从容器中按照beabName去获取配置的Replacer对象,然后调用Replacer对象的reimplement方法就行。

image.png

我们可以总结一个点:运行时方法重写,其实就是使用JDK/CGLIB去创建代理,去拦截掉目标方法的逻辑,去指定要替换的逻辑,搞不定,用代理总没错,在Spring当中对于这句话的体现的淋漓尽致!

个人博客:http://wanna1314y.top:8090/

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