Loading

Spring中的lookup-method的作用

在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

posted @ 2022-01-03 00:56  街头卖艺的肖邦  阅读(976)  评论(0编辑  收藏  举报