Sping源码(九)—— Bean的初始化(非懒加载)— lookupMethod标签

序言

在继续深入Spring的对象创建流程之前,这篇文章先简单介绍一下lookupMethod标签的用法及作用。

准备的xml
自定义名为methodOverride.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="apple" class="org.springframework.methodOverrides.lookup.Apple"></bean>

	<bean id="banana" class="org.springframework.methodOverrides.lookup.Banana"></bean>

	<bean id="fruitPlate1" class="org.springframework.methodOverrides.lookup.FruitPlate">
		<lookup-method name="getFruit" bean="apple"/>
	</bean>

	<bean id="fruitPlate2" class="org.springframework.methodOverrides.lookup.FruitPlate">
		<lookup-method name="getFruit" bean="banana"/>
	</bean>
</beans>

Fruit类

public class Fruit {
    public Fruit() {
        System.out.println("I got Fruit");
    }
}

Apple类

public class Apple extends Fruit {
    public Apple() {
        System.out.println("I got a fresh apple");
    }
}

Banana类

public class Banana extends Fruit {
    public Banana() {
        System.out.println("I got a  fresh bananer");
    }
}

FruitPlate

public abstract class FruitPlate{
    // 抽象方法获取新鲜水果
    public abstract Fruit getFruit();
}

测试类

public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
		FruitPlate fruitPlate1 = (FruitPlate) ac.getBean("fruitPlate1");
		fruitPlate1.getFruit();

		FruitPlate fruitPlate2 = (FruitPlate) ac.getBean("fruitPlate2");
		fruitPlate2.getFruit();
	}

运行结果
我们来看看配置的<lookup-method>标签是如何进行的区分和覆盖。
在这里插入图片描述

lookup-method标签

我们在FruitPlate类中调用getFruit方法会返回Fruit类,而我们在lookup-method标签中配置调用getFruit方法会返回Apple类。
所以我们这次从FruitPlate对象的创建入手,看看整个流程中做了什么。

FruitPlate的创建
依然是 getBean ——》 doGetBean ——》 createBean 的整体流程的逻辑,我们直接看createBean

createBean

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		//删除无用代码
		
		RootBeanDefinition mbdToUse = mbd;
		
		mbdToUse.prepareMethodOverrides();

		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		return beanInstance;
	}

prepareMethodOverrides
如果bean对象配置了lookup-method、replace-method标签,说明要进行方法覆盖操作,设置标志位。

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
		// Check that lookup methods exist and determine their overloaded status.
		//methodOverrides变量不为null,说明有方法覆盖
		if (hasMethodOverrides()) {
			//设置methodOverrides标记位
			getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
		}
	}
	protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
		int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
		if (count == 0) {
		}
		else if (count == 1) {
			// Mark override as not overloaded, to avoid the overhead of arg type checking.
			//标记overload为未重载,避免arg类型检查开销
			mo.setOverloaded(false);
		}
	}

doCreateBean

调用createBeanInstance方法取bean的策略实例。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		//去掉无用代码
		
		// Instantiate the bean.
		//这个beanWrapper是用来持有创建出来的bean对象的
		BeanWrapper instanceWrapper = null;

		//如果是单例对象,从factoryBeanInstanceCache缓存中移除该信息
		if (mbd.isSingleton()) {
			// 如果是单例对象,从factoryBean实例缓存中移除当前bean定义信息
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 没有就创建实例
		if (instanceWrapper == null) {
			// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		return exposedObject;
	}

createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// 去除无用代码
		
		// 使用默认无参构造函数创建对象,如果没有无参构造且存在多个有参构造且没有@AutoWired注解构造,会报错
		return instantiateBean(beanName, mbd);
	}

instantiateBean
忽略securityManagergetInstantiationStrategy中创建并返回了CglibSubclassingInstantiationStrategy对象。

	protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
		try {
			Object beanInstance;
			if (System.getSecurityManager() != null) {
				beanInstance = AccessController.doPrivileged(
						(PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
						getAccessControlContext());
			}
			else {
				// 获取实例化策略并且进行实例化操作
				beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
			}
			// 包装成BeanWrapper
			BeanWrapper bw = new BeanWrapperImpl(beanInstance);
			initBeanWrapper(bw);
			return bw;
		}
	}

getInstantiationStrategy
方法创建了一个CglibSubclassingInstantiationStrategy类型对象。使用Cglib形式的动态代理创建对象。

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

protected InstantiationStrategy getInstantiationStrategy() {
		return this.instantiationStrategy;
	}

而我们的CglibSubclassingInstantiationStrategy中就有对应的变量用来进行标记。

/**
	 * 如果没有override方法覆盖的话,那么索引位置为0
	 *
	 * Index in the CGLIB callback array for passthrough behavior,
	 * in which case the subclass won't override the original class.
	 */
	private static final int PASSTHROUGH = 0;

	/**
	 * 如果有lookup-method的覆盖,那么索引位置为1
	 *
	 * Index in the CGLIB callback array for a method that should
	 * be overridden to provide <em>method lookup</em>.
	 */
	private static final int LOOKUP_OVERRIDE = 1;

	/**
	 * Index in the CGLIB callback array for a method that should
	 * be overridden using generic <em>method replacer</em> functionality.
	 *
	 * 如果有replace-method的覆盖,那么索引位置为2
	 */
	private static final int METHOD_REPLACER = 2;

instantiate
因为我们配置了lookup-method标签,所以会有方法覆盖,此时hasMethodOverrides()方法会返回true,取反之后走else。

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (!bd.hasMethodOverrides()) {
			//去除无用代码
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			// 必须生成CGLIB子类
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

此时我们将要加载的bean就是xml文件中配置的FruitPlate methodOverrides变量存放的就是我们想要进行覆盖的getFruit()

在这里插入图片描述

instantiateWithMethodInjection
而因为我们创建了CglibSubclassingInstantiationStrategy类型对象,所以此时的instantiateWithMethodInjection方法的实现是CglibSubclassingInstantiationStrategy的方法。

protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			@Nullable Constructor<?> ctor, Object... args) {

		// Must generate CGLIB subclass...
		// 必须生成一个cglib的子类
		return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
	}

封装beanDefinitionBeanFactoryCglibSubclassCreator对象中,类中包含3种方法的CALLBACK_TYPES

private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
				{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};

CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
			this.beanDefinition = beanDefinition;
			this.owner = owner;
		}

instantiate
此时,instance实例就是我们通过Cglib创建的FruitPlate对象,并设置callBacks属性值。

public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
			Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
			Object instance;
			if (ctor == null) {
				instance = BeanUtils.instantiateClass(subclass);
			}
			else {
				try {
					Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
					instance = enhancedSubclassConstructor.newInstance(args);
				}

			}

			Factory factory = (Factory) instance;
			factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
			return instance;
		}

此时要返回的instance就是通过Cglib创建的FruitPlate对象,并且callback属性中也设置了LookupOverrideMethodInterceptor用来进行方法的拦截。
在这里插入图片描述

createEnhancedSubclass
Cglib规定写法,创建Enhancer对象并设置callBack属性。

private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
			//cglib规定用法,对原始class进行增强,并设置callback
			Enhancer enhancer = new Enhancer();
			enhancer.setSuperclass(beanDefinition.getBeanClass());
			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
			if (this.owner instanceof ConfigurableBeanFactory) {
				ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
				enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
			}
			//过滤,自定义逻辑来指定调用的callback下标
			enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
			//只是产生class就直接指定callback类型,跟上面指定的callbackFilter对应
			enhancer.setCallbackTypes(CALLBACK_TYPES);
			return enhancer.createClass();
		}
	}

创建好了FruitPlate对象后,main方法中再次调用fruitPlate1.getFruit();方法。

public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");
		FruitPlate fruitPlate1 = (FruitPlate) ac.getBean("fruitPlate1");
		fruitPlate1.getFruit();

		FruitPlate fruitPlate2 = (FruitPlate) ac.getBean("fruitPlate2");
		fruitPlate2.getFruit();
	}

通过索引LOOKUP_OVERRIDE LookupOverrideMethodInterceptor<lookup-method>标签中配置的方法进行拦截。
获取 lo 所对应的 BeanName,调用getBean()进行Bean实例的创建,从而调用Apple类中的getFruit()方法,从而实现覆盖

public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
			// Cast is safe, as CallbackFilter filters are used selectively.
			LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
			Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
			if (StringUtils.hasText(lo.getBeanName())) {
				return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
						this.owner.getBean(lo.getBeanName()));
			}
			else {
				return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
						this.owner.getBean(method.getReturnType()));
			}
		}

然而,lookup-method的作用和效果远非如此,我们此时稍稍改动一下配置文件。将Apple的scope改为prototype。

<?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="apple" class="org.springframework.methodOverrides.lookup.Apple" scope="prototype"></bean>
	
	<bean id="fruitPlate1" class="org.springframework.methodOverrides.lookup.FruitPlate">
		<lookup-method name="getFruit" bean="apple"/>
	</bean>

</beans>
public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("methodOverride.xml");

		FruitPlate fruitPlate1 = (FruitPlate) ac.getBean("fruitPlate1");
		Apple a1 =(Apple) fruitPlate1.getFruit();
		FruitPlate fruitPlate2 = (FruitPlate) ac.getBean("fruitPlate1");
		Apple a2 = (Apple) fruitPlate2.getFruit();
		System.out.println(a1 == a2);
	}

修改完配置文件和测试类后,此时的 a1 != a2,会输出 false。

原因在于,当lookup-method标签生效调用getFruit()进行拦截时走getBean()方法创建对象时,因为此时Apple的scope = prototype,所以singletonObjects缓存中不会缓存创建好的实例,每次都会重新创建,所以每次获取的都是新的对象。

所以lookup-method最重要的作用是利用了索引(LOOKUP_OVERRIDE)和拦截器(LookupOverrideMethodInterceptor)解决了单例引用原型的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/708738.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【星海随笔】云解决方案学习日志篇(三) 工作原理篇

Filebeat工作原理 Filebeat 是使用 Golang 实现的轻量型日志采集器,也是 Elasticsearch stack 里面的一员。本质上是一个 agent ,可以安装在各个节点上,根据配置读取对应位置的日志,并上报到相应的地方去。 使用了背压敏感协议,因此不会使管道过载。当Logstash数据处理繁忙时,…

张艺兴step新专开启自由驾驶新纪元

张艺兴《Step》新专&#xff0c;开启自由驾驶新纪元&#xff01;当音乐与驾驶相遇&#xff0c;会碰撞出怎样的火花&#xff1f;当实力派艺人张艺兴遇上全新英文专辑《Step》&#xff0c;便为我们解锁了一种前所未有的出行体验&#xff01;这不仅仅是一张音乐专辑&#xff0c;更…

南方cass专业测绘软件下载,南方cass功能强大的cad辅助测绘软件获取!

在测绘领域&#xff0c;南方CASS测绘软件无疑是一颗璀璨的明星&#xff0c;被誉为“全能选手”。这款软件在功能方面表现出了令人赞叹的多样性和专业性&#xff0c;为测绘工作提供了极大的便利。 ​ 首先&#xff0c;南方CASS测绘软件具备强大的数据兼容性&#xff0c;支持多种…

万相台的功能是什么?如何使用万相台?

1.特点&#xff1a; 万相台是一个智能渠道&#xff0c;可控性弱&#xff0c;高转化&#xff0c;人群&关键词是黑盒&#xff1b; 2.场景多&#xff1a; 有拉新快、活动加速、上新快、货品加速、活动加速、多目标直投、全站推等&#xff1b; 3.扣费逻辑&#xff1a;cpc付…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第41课-动态添加3D对象

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第41课-动态添加3D对象 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎…

填报志愿选大学专业,文科生如何选专业?

读文科的同学接触的专业知识相对广泛&#xff0c;往往被认为是“万金油”&#xff0c;他们仿佛什么都能做&#xff0c;但是和专业技能类知识不同&#xff0c;缺乏技术支持&#xff0c;从而使得文科专业的就业方向和前景远远比不上理科专业那么明朗&#xff0c;对于众多文科生而…

机器学习:回顾总结

学了什么 进阶内容 接下来如何学习 找个项目自己练习多读前沿paper 学员分布

【linux网络(三)】HTTP协议详解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. 序列化和…

使用QT绘制简单的动态数据折线图

两个核心类时QChart和QLineSeries 下面这个示例代码中&#xff0c;定时器每隔一段时间将曲线图中的数据点向右移动 一个单位&#xff0c;同时调整横坐标轴的范围&#xff0c;实现了一次滚动对应移动一个数据点的效果。 QLineSeries最多容纳40961024个点 #include <QtWidg…

boot整合solr

换了新项目组&#xff0c;技术相对老些&#xff0c;于是用boot框架简单记录下&#xff01; 安装 下载路径&#xff1a;https://solr.apache.org/downloads.html Windows环境 下载solr-8.2.0.zip包并解压缩&#xff0c;以管理员身份打开cmd&#xff0c;执行 solr cmd 命令启…

【开源项目】重庆智慧城市案例~实景三维数字孪生城市CIM/BIM

飞渡科技数字孪生重庆管理平台&#xff0c;以实景三维平台为支撑&#xff0c;以城市数据库对接为核心&#xff0c;利用数字孪生技术&#xff0c;结合云计算、物联网IOT等技术&#xff0c;对接城市规划、智能交通、和公共安全等系统。 利用平台强大的国产自研渲染引擎&#xff0…

你的职业规划就是面向贫穷的规划

如果你觉得作者的文章还有点用,请记得点赞 + 关注 说一个扎心的事实,就是我们绝大多数人的职业规划基本上都是错误的,都是面向贫穷的规划。 因为绝大多数人的职业规划都是打工人的职业规划,这种规划除了很少部分人最终能成为企业高管,实现层级跃迁外,绝大多数人在大多数…

AI大模型探索之路-实战篇:智能化IT领域搜索引擎之知乎网站数据获取(初步实践)

系列篇章&#x1f4a5; No.文章1AI大模型探索之路-实战篇&#xff1a;智能化IT领域搜索引擎的构建与初步实践2AI大模型探索之路-实战篇&#xff1a;智能化IT领域搜索引擎之GLM-4大模型技术的实践探索3AI大模型探索之路-实战篇&#xff1a;智能化IT领域搜索引擎之知乎网站数据获…

Spring Boot:Java 应用开发高效之道

Spring Boot 是一种革命性的框架&#xff0c;旨在简化 Java 应用的创建和部署过程。通过自动化配置和简化项目搭建流程&#xff0c;Spring Boot 大大加速了开发周期&#xff0c;让 Java 应用开发变得更加高效和便捷。 核心优势&#xff1a; 快速启动和简化配置&#xff1a;Spr…

wordpress轻量免费主题

WordPress建站公司 适合提供WordPress建站服务的公司或个体(个人)工作室使用的WordPress建站公司主题模板。 https://www.jianzhanpress.com/?p545 首屏大图红色简洁wordpress主题 首屏大图红色简洁wordpress主题&#xff0c;非常地高端大气上档次&#xff0c;可用于多个行…

感受光子芯片中试线,如何点亮未来计算与通信的革命之路(2024青岛智能装备与通信技术展)

光子芯片中试线&#xff1a;点亮未来计算与通信的革命之路 在新一代信息技术的浪潮中&#xff0c;光子芯片以其低能耗、高速度的特点备受瞩目。首条光子芯片中试线的建立&#xff0c;标志着我国在光电子领域的重大突破&#xff0c;同时也为即将到来的量子计算时代奠定了坚实基…

机器学习python实践——数据“相关性“的一些补充性个人思考

在上一篇“数据白化”的文章中&#xff0c;说到了数据“相关性”的概念&#xff0c;但是在统计学中&#xff0c;不仅存在“相关性”还存在“独立性”等等&#xff0c;所以&#xff0c;本文主要对数据“相关性”进行一些补充。当然&#xff0c;如果这篇文章还能入得了各位“看官…

(Javascript)AI数字人mp4转canvas播放并去除背景绿幕

1、需求介绍 H5页面嵌入AI数字人播报&#xff0c;但生成的数字人是mp4格式且有绿幕背景&#xff0c;需要转成canvas并去除背景&#xff1b; 2、效果&#xff1a; 去除前&#xff1a; 去除后&#xff1a; 3、代码 <!DOCTYPE html> <html lang"en"><…

泉州职业技术大学2024Java期末题库【基础题】

1.根据输入的表示星期几的数字&#xff0c;对应输出它的英文名称。 考察内容:Switch语句的掌握 public class test1 {public static void main(String[] args) {//switch语句复习//创建对象java.util.Scanner input new java.util.Scanner(System.in);//提示输入语句System.ou…

本地Zabbix开源监控系统安装内网穿透实现远程访问详细教程

文章目录 前言1. Linux 局域网访问Zabbix2. Linux 安装cpolar3. 配置Zabbix公网访问地址4. 公网远程访问Zabbix5. 固定Zabbix公网地址 &#x1f4a1;推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【…