Spring IOC 源码学习 环境准备

lookup-method

lookup-method 是 Spring Framework 中用于实现依赖查找方法注入的一种方式。

它主要用于解决以下问题:在单例 bean 中,如果需要某个方法返回一个原型(prototype)类型的 bean,而该方法被多次调用,希望每次调用都能得到一个新的实例,而不是同一个实例。lookup-method 就可以帮助实现这样的需求。

in short 解决在单例Bean 中需要引用原型Bean的问题

public class PrototypeBean {
    private String name;
 
    public PrototypeBean() {
        this.name = "PrototypeBean-" + System.nanoTime();
    }
 
    public String getName() {
        return name;
    }
}
 
public abstract class SingletonBean {
 
    // 抽象方法,用于获取原型实例 (Spring 会基于cglib生成SingletonBean的子类, 实现这个方法)
    public abstract PrototypeBean getPrototypeInstance();
 
    public void displayPrototypeName() {
        // 调用被覆盖的方法来获取原型实例
        PrototypeBean prototypeBean = getPrototypeInstance();
        System.out.println("Prototype name: " + prototypeBean.getName());
    }
}
 
<!-- 配置 PrototypeBean -->
<bean id="prototypeBean" class="com.example.PrototypeBean" scope="prototype"/>
 
<!-- 配置 SingletonBean,使用 lookup-method 指定需要被子类覆盖的方法 (基于Cglib 生成一个代理对象) -->
<bean id="singletonBean" class="com.example.SingletonBean">
<!-- 将 getPrototypeInstance 方法 实现为返回容器的prototypeBean -->
    <lookup-method name="getPrototypeInstance" bean="prototypeBean"/>
</bean>

在这个例子中,每次调用 displayPrototypeName 方法都会得到一个新的 PrototypeBean 实例,因为 getPrototypeInstance 方法被代理类覆盖; 代理类是由 Spring 在运行时生成, 基于Cglib 生成一个代理对象 {@link org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy}

相关源码

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

 
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
 
		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;
 
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}
		// Prepare method overrides.
		try {
			/**
			 *  准备和验证 lookup-method 和 replace-method;
			 * 注意: 这里只是标记, 真正处理是在实例化时, 选择策略生成一个CgLib的代理对象 {@link org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy}
			 * 关于它们的作用见笔记 [[Spring lookup-method 和 replace-method.md]]
			 */
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}
 
....

看这个策略 org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy 的这个静态类 org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor

replace-method

replace-method 是Spring 动态借助CGLIB改变bean中的方法,通过改变方法逻辑注入对象,该方法的使用需要依赖Spring提供的MethodReplacer 接口实现。

in short 可以动态的 替换 任意bean的 任意方法, 类似jdk的方法拦截

public class Calculator {
	//被拦截的方法
    public String computeValue(String input) {
        // some real code...
    }
}

替换的bean需要实现MethodReplacer

//实现 MethodReplacer  方法拦截器
public class ReplacementComputeValue implements MethodReplacer {
    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

配置

<bean id="calculator" class="x.y.z.Calculator">
    <!-- 替换 computeValue 方法, 指向 replacementComputeValue 方法拦截器  -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<!-- 方法拦截器 -->
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

相关源码

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

 
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
 
		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;
 
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}
		// Prepare method overrides.
		try {
			/**
			 *  准备和验证 lookup-method 和 replace-method;
			 * 注意: 这里只是标记, 真正处理是在实例化时, 选择策略生成一个CgLib的代理对象 {@link org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy}
			 * 关于它们的作用见笔记 [[Spring lookup-method 和 replace-method.md]]
			 */
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}
 
....

看这个策略 org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy 的这个静态类 org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy.ReplaceOverrideMethodInterceptor