在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實例化對象;
參考: