在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实例化对象;
参考:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-method-injection