Spring源码分析-Bean创建流程二

文章介绍了如何在Spring中让单例模式的bean引用原型模式的bean,通过lookup-method标签实现动态代理,每次获取对象时都会创建新的原型bean实例,而非使用缓存。详细解释了lookup-method的工作原理,包括XML配置、方法覆盖以及CGLIB子类创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、问题引出

spring中默认的对象都是单例的,spring会在一级缓存中持有该对象,方便下次直接获取,

如果想在一个单例模式的bean下引用一个原型模式的bean,怎么办?

在此时就需要引用lookup-method标签来解决此问题

通过拦截器的方式每次需要的时候都去创建最新的对象,而不会把原型对象缓存起来,

二、lookup-method使用及原理

1、程序入口

测试类

public class TestMethodOverride {
    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("fruitplate1");
        fruitplate2.getFruit();

        FruitPlate fruitplate3 = (FruitPlate) ac.getBean("fruitplate2");
		fruitplate3.getFruit();
		FruitPlate fruitplate4 = (FruitPlate) ac.getBean("fruitplate2");
		fruitplate4.getFruit();
    }
}

运行结果:说明单例对象可以引用多例也可以引用单例

2、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="com.yang.test.methodOverrides.lookup.Apple" ></bean>
	<bean id="banana" class="com.yang.test.methodOverrides.lookup.Banana" scope="prototype"></bean>

    <bean id="fruitplate1" class="com.yang.test.methodOverrides.lookup.FruitPlate">
        <lookup-method name="getFruit" bean="apple"></lookup-method>
    </bean>
    <bean id="fruitplate2" class="com.yang.test.methodOverrides.lookup.FruitPlate">
        <lookup-method name="getFruit" bean="banana"></lookup-method>
    </bean>
</beans>

3、在createBean方法中设置lookup-method标签

验证及准备覆盖的方法,lookup-method  replace-method,当需要创建的bean对象中包含了lookup-method和replace-method标签的时候,会产生覆盖操作

	public void prepareMethodOverrides() throws BeanDefinitionValidationException {
		// Check that lookup methods exist and determine their overloaded status.
		if (hasMethodOverrides()) {
			getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
		}
	}

4、在CglibSubclassingInstantiationStrategy中生成代理类

		public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
			// 根据beanDefinition来创建一个cglib的子类
			Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
			Object instance;
			// 如果构造器等于空,那么直接通过反射来实例化对象
			if (ctor == null) {
				instance = BeanUtils.instantiateClass(subclass);
			}
			else {
				try {
					// 通过cglib对象来根据参数类型获取对应的构造器
					Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
					// 通过构造器来获取对象
					instance = enhancedSubclassConstructor.newInstance(args);
				}
				catch (Exception ex) {
					throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
							"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
				}
			}
			// SPR-10785: set callbacks directly on the instance instead of in the
			// enhanced class (via the Enhancer) in order to avoid memory leaks.
			Factory factory = (Factory) instance;
			factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
			return instance;
		}

5、 fruitplate1.getFruit();时 会调用代理对象,然后走->getBean->doGetBean->走多例的分支

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

备注:动态代理学习可到:动态代理原理_GL-Yang的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值