在Spring中,默認創建的對象是單例的,Spring會在一級緩存中持有該對象,方便下次直接獲取,如果創建的是多例對象,Spring每次則會創建新的對象,不會進行緩存;
如果想在一個單例bean下引用一個多例bean,此時需要使用LookUp來解決;
測試如下:
ObjectA的getObjectC方法用@Lookup注解修飾或在xml配置<lookup-method>屬性,而ObjectB和ObjectC對象都是通過@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)配置成了多例;
@Component
public class ObjectA {
private final static Log LOG = LogFactory.getLog(ObjectA.class);
@Autowired
private ObjectB objectB;
@Autowired
private ObjectC objectC;
public ObjectA() {
LOG.info("ObjectA constructor");
}
public ObjectB getObjectB() {
return objectB;
}
@Lookup
public ObjectC getObjectC() {
return objectC;
}
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ObjectB {
private final static Log LOG = LogFactory.getLog(ObjectB.class);
public ObjectB() {
LOG.info("ObjectB constructor");
}
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ObjectC {
private final static Log LOG = LogFactory.getLog(ObjectC.class);
public ObjectC() {
LOG.info("ObjectC constructor");
}
}
@Test
public void lookUpMethodTest() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MethodOverrideConfig.class);
context.refresh();
ObjectA objectA = context.getBean(ObjectA.class);
ObjectB objectB1 = objectA.getObjectB();
ObjectB objectB2 = objectA.getObjectB();
System.out.println(objectB1);
System.out.println(objectB2);
ObjectC objectC1 = objectA.getObjectC();
ObjectC objectC2 = objectA.getObjectC();
System.out.println(objectC1);
System.out.println(objectC2);
}
結果如下:
ObjectB和ObjectC雖然是配置成多例,但是通過getBean多次獲取ObjectB和ObjectC對象的效果不同,每次獲取ObjectB的效果還是跟單例一樣,而每次獲取ObjectC的效果才是多例的;

分析如下:
AbstractAutowireCapableBeanFactory#createBeanInstance方法是使用合適的實例化策略來創建新的實例,創建的過程是會調用instantiateBean方法;
AbstractAutowireCapableBeanFactory#instantiateBean
instantiateBean方法是使用無參構造器進行實例化對象;通過getInstantiationStrategy方法獲取實例化策略,根據獲取的策略處理instantiate方法實例化對象的操作;

bean的生成策略,默認是cglib,而CglibSubclassingInstantiationStrategy是繼承SimpleInstantiationStrategy,如下圖;

bean實例化策略為CglibSubclassingInstantiationStrategy時,調用instantiate方法是SimpleInstantiationStrategy的;
SimpleInstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory)

上面的測試代碼中ObjectA#getObjectC有@Lookup注解修飾,因此對應的methodOverrides不為空,執行instantiateWithMethodInjection方法的邏輯,如下圖;

CglibSubclassingInstantiationStrategy.CglibSubclassCreator#instantiate

對於有設置了methodOverride屬性的bean會設置對應的方法攔截器;
當調用有@Lookup注解修飾或xml配置<lookup-method>的方法時,調用對應的方法會先進入攔截器CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept;

首先通過beanDefinition獲取到對應的LookupOverride的屬性,通過屬性獲取到對應方法的返回值,調用getBean實例化對象;
參考:
