一、问题引出
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的博客-优快云博客