Spring 默认标签的解析的bean 标签的解析及注册之解析子元素 lookup-method和解析子元素 replaced-method

目录

一、解析子元素 lookup-method

(1) 首先我们创建一个父类

 (2)创建其子类并覆盖showMe方法

  (3)创建调用方法

  (4)创建测试方法

(5)创建lookupTest.xml

(6)启动测试类,看效果

二、解析子元素 replaced-method

(1)在changeMe 中完成某个业务逻辑。

(2)在运营一段时间后需要改变原有的业务逻辑。

(3)使替换后的类生效。

(4)测试

📢📢📢📣📣📣
哈喽!大家好,我是「Leen」。刚工作几年,想和大家一同进步🤝🤝
一位上进心十足的Java博主!😜😜😜
喜欢尝试一些新鲜的东西,平时比较喜欢研究一些新鲜技术和一些自己没有掌握的技术领域。能用程序解决的坚决不手动解决😜😜😜

目前已涉足Java、Python、数据库(MySQL、pgsql、MongoDB、Oracle...)、Linux、HTML、VUE、PHP、C(了解不多,主要是嵌入式编程方向做了一些)...(还在不断地学习,扩展自己的见识和技术领域中),希望可以和各位大佬们一起进步,共同学习🤝🤝

✨ 如果有对【Java】,或者喜欢看一些实操笔记感兴趣的【小可爱】,欢迎关注我

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️
————————————————

如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

前言

《Spring源码深度解析》相关博客

Spring官网

一、解析子元素 lookup-method

了解了部分Spring源码的同学应该知道,子元素lookup-method似乎并不是很常用,但是在某些时候它的确是非常有用的属性,通常我们称它为获取器注人。引用《Spring in Action》中的一句话:获取器注人是一种特殊的方法注人,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里面配置的,此方法可用在设计有些可插拔的功能上,解除程序依赖。我们看看具体的应用。 

(1) 首先我们创建一个父类

package com.www.test.test2024.test506.lookup.bean;

public class User {
    public void showMe(){
        System.out.println("I am User");
    }
}

 (2)创建其子类并覆盖showMe方法

package com.www.test.test2024.test506.lookup.bean;

public class It extends User {
    public void showMe() {
        System.out.println("I am It");
    }


}

  (3)创建调用方法

package com.www.test.test2024.test506.lookup.bean;

public abstract class GetBeanTest {
    public void showMe(){
        this.getBean().showMe();
    }
    public abstract User getBean();
}

  (4)创建测试方法

package com.www.test.test2024.test506.lookup.bean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    //解析子元素 lookup-method
    public static void main(String[] args) {
        ApplicationContext bf = new ClassPathXmlApplicationContext("classpath:lookup/lookupTest.xml");
        GetBeanTest test = (GetBeanTest) bf.getBean("getBeanTest");
        test.showMe();
    }
}

 到现在为止,除了配置文件外,整个测试方法就完成了,如果之前没有接触过获取器注人的同学们可能会有疑问:抽象方法还没有被实现,怎么可以直接调用呢?答案就在Spring为我们提供的获取器中,我们看看配置文件是怎么配置的。

(5)创建lookupTest.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="getBeanTest" class="com.www.test.test2024.test506.lookup.bean.GetBeanTest">
        <lookup-method name="getBean" bean="it"/>
    </bean>
    <bean id="it" class="com.www.test.test2024.test506.lookup.bean.It"/>
</beans>

(6)启动测试类,看效果

在配置文件中,我们看到了源码解析中提到的lookup-method 子元素,这个配置完成的功能是动态地将 teacher 所代表的 bean 作为 getBean 的返回值,运行测试方法我们会看到控制台上的输出:

I am It

当我们的业务变更或者在其他情况下,teacher里面的业务逻辑已经不再符合我们的业务要求,需要进行替换怎么办呢?这是我们需要增加新的逻辑类:

package com.www.test.test2024.test506.lookup.bean;

public class Student extends User {
    public void showMe() {
        System.out.println("I am Student");
    }
}

同时修改配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="getBeanTest" class="com.www.test.test2024.test506.lookup.bean.GetBeanTest">
<!--        <lookup-method name="getBean" bean="it"/>-->
        <lookup-method name="getBean" bean="student"/>
    </bean>
    <bean id="it" class="com.www.test.test2024.test506.lookup.bean.It"/>
    <bean id="student" class="com.www.test.test2024.test506.lookup.bean.Student"/>
</beans>

再次运行测试类,你会发现不一样的结果:

i am Student


至此,我们已经初步了解了 lookup-method 子元素所提供的大致功能,相信这时再次去看它的属性提取源码会觉得更有针对性。

	/**
	 * Parse lookup-override sub-elements of the given bean element.
	 */
	public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
		NodeList nl = beanEle.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
            //仅当在 Spring 默认 bean 的子元素下且为<lookup-method 时有效
			if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
				Element ele = (Element) node;
                //获取要修饰的方法
				String methodName = ele.getAttribute(NAME_ATTRIBUTE);
                //获取配置返回的bean
				String beanRef = ele.getAttribute(BEAN_ELEMENT);
				LookupOverride override = new LookupOverride(methodName, beanRef);
				override.setSource(extractSource(ele));
				overrides.addOverride(override);
			}
		}
	}

上面的代码很眼熟,似乎与 parseMetaElements的代码大同小异,最大的区别就是在if判断中的节点名称在这里被修改为LOOKUPMETHODELEMENT。还有,在数据存储上面通过使用 LookupOverride 类型的实体类来进行数据承载并记录在AbstractBeanDefinition中的methodOverrides属性中。

二、解析子元素 replaced-method

这个方法主要是对 bean 中replaced-method 子元素的提取,在开始提取分析之前我们还是预先介绍下这个元素的用法。
方法替换:可以在运行时用新的方法替换现有的方法。与之前的look-up不同的是,replaced-method不但可以动态地替换返回实体 bean,而且还能动态地更改原有方法的逻辑。我们来看看使用示例。

(1)在changeMe 中完成某个业务逻辑。

package com.www.test.test2024.test506.replaced.bean;

public class TestChangeMethod {
    public void changeMe() {
        System.out.println("changeMe");
    }
}

(2)在运营一段时间后需要改变原有的业务逻辑。

package com.www.test.test2024.test506.replaced.bean;

import org.springframework.beans.factory.support.MethodReplacer;

import java.lang.reflect.Method;

public class TestMethodReplacer implements MethodReplacer {
    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("我替换了原有的方法");
        return null;
    }
}

(3)使替换后的类生效。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="testChangeMethod" class="com.www.test.test2024.test506.replaced.bean.TestChangeMethod">
        <replaced-method name="changeMe" replacer="replacer"/>
    </bean>
    <bean id="replacer" class="com.www.test.test2024.test506.replaced.bean.TestMethodReplacer"></bean>
</beans>

(4)测试

package com.www.test.test2024.test506.replaced.bean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    //解析子元素 replaced-method
    public static void main(String[] args) {
        ApplicationContext bf = new ClassPathXmlApplicationContext("classpath:replacerMethod/replacerMethodTest.xml");
        TestChangeMethod test = (TestChangeMethod) bf.getBean("testChangeMethod");
        test.changeMe();
    }
}

好了,运行测试类就可以看到预期的结果了,控制台成功打印出“我替换了原有的方法”也就是说我们做到了动态替换原有方法,知道了这个元素的用法,我们再次来看元素的提取过程:


    /**
     * Parse replaced-method sub-elements of the given bean element.
     */
    public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            //仅当在 Spring 默认bean 的子元素下且为 <replaced-method 时有效
            if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
                Element replacedMethodEle = (Element) node;
                //提取要替换的旧的方法
                String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
                //提取对应的新的替换方法
                String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
                // Look for arg-type match elements.
                List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
                for (Element argTypeEle : argTypeEles) {
                    String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
                    //记录参数
                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
                    if (StringUtils.hasText(match)) {
                        replaceOverride.addTypeIdentifier(match);
                    }
                }
                replaceOverride.setSource(extractSource(replacedMethodEle));
                overrides.addOverride(replaceOverride);
            }
        }
    }

我们可以看到无论是look-up还是replaced-method 都是构造了一个 MethodOverride,并最终记录在了 AbstractBeanDefinition中的 methodOverrides 属性中。而这个属性如何使用以完成它所提供的功能可以往后深度研究一下Spring的代码,后续我会整理一下出一期这方面的笔记。

欢迎大家在评论区讨论,今天的干货分享就到此结束了,如果觉得对您有帮助,麻烦给个三连!

以上内容为本人的经验总结和平时操作的笔记。若有错误和重复请联系作者删除!!感谢支持!!

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leen@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值