@Lookup应用场景
在一个单例bean UserService
中,注入一个原型bean属性 OrderService
,每次调用UserService时,拿到的OrderService是同一个,不符合原型的特性(每次获取都是新创建的bean)
UserService的作用域是单例,即每次都是从单例池中获取,获取不到则创建后放入单例池,每次都是获取的同一个
OrderService的作用域是原型,即每次获取都是新创建bean。
以上代码运行结果是每次调用userService.test()
获取到的orderService是同一个,不符合原型特征。
使用@Lookup
注解寻找beanName=orderService的bean,每次获取都是新的。
Lookup源码流程
在bean的生命周期“实例化”步骤中,会推断出合适的构造方法,在类AutowiredAnnotationBeanPostProcessor
中会处理@Autowired
以及@Lookup
注解。
AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors()
遍历类中的方法,找到加了@Lookup
注解的方法,放到LookupOverride
对象中,存入到合并后的RootBeanDefinition
对象中methodOverrides集合属性中
存入到RootBeanDefinition
对象的methodOverrides
集合属性后,在bean的实例化流程AbstractAutowireCapableBeanFactory.doCreateBean()#createBeanInstance()
中,会调用instantiateBean()
方法来实例化生成bean。
实例化策略方法getInstantiationStrategy()
默认返回CglibSubclassingInstantiationStrategy
在spring容器启动时,设置了默认的实例化策略是CglibSubclassingInstantiationStrategy
CglibSubclassingInstantiationStrategy
的父类是SimpleInstantiationStrategy
先执行父类SimpleInstantiationStrategy.instantiate()
方法
如果判断methodOverrides属性有值,会调用子类CglibSubclassingInstantiationStrategy.instantiateWithMethodInjection()
方法,最终生成了cglib代理对象。
执行cglib代理对象的instantiate()
方法
生成LookupOverrideMethodInterceptor
对象,并执行intercept()
方法,从beanDefinition拿到methodOverrides
集合属性,根据beanName直接从beanFactory即spring容器获取bean返回了,没有执行method.invoke(),意味着userService.xxx()方法不会执行,而是根据它方法上的@Lookup的值找到bean并返回。
总结
- 在bean实例化的过程中,执行推断构造方法时,会找到类方法上的
@Lookup
信息,并存到合并后的RootBeanDefinition
的methodOverrides
属性中。 - 推断构造方法得到具体的构造方法后,进入真正的实例化流程,根据获取的实例化策略
CglibSubclassingInstantiationStrategy
,实例化时,先执行它父类SimpleInstantiationStrategy.instantiate()
方法 - 判断methodOverrides有值的情况下,调用
CglibSubclassingInstantiationStrategy.instantiateWithMethodInjection()
方法,生成Cglib代理对象调用instantiate()
方法,生成LookupOverrideMethodInterceptor
- 执行
LookupOverrideMethodInterceptor.intercept()
,从beanDefinition拿到methodOverrides集合属性,拿到beanName从beanFactory即spring容器获取bean并返回。