@Lazy
一、基本信息
?? 作者 - Lex ?? 博客 - 我的CSDN ?? 文章目錄 - 所有文章 ?? 源碼地址 - @Lazy源碼
二典格、注解描述
@Lazy
注解岛宦,它的主要用途是延遲依賴注入的初始化。通常情況下钝计,當(dāng) ApplicationContext 被啟動(dòng)和刷新時(shí)恋博,所有的單例 bean 會(huì)被立即初始化。但有時(shí)私恬,可能希望某些 bean 在首次使用時(shí)才被初始化。
三炼吴、注解源碼
@Lazy
注解是 Spring 框架自 3.0 版本開始引入的一個(gè)核心注解本鸣,用于控制 bean 的懶加載行為。默認(rèn)情況下硅蹦,當(dāng) @Lazy
被應(yīng)用荣德,bean 不會(huì)在 Spring 容器啟動(dòng)時(shí)立即初始化闷煤,而是在首次被引用或請(qǐng)求時(shí)。這適用于通過 @Component
或 @Bean
定義的 bean涮瞻。此外鲤拿,@Lazy
還可以用于注入點(diǎn),如 @Autowired
署咽,創(chuàng)建一個(gè)懶解析代理近顷,從而實(shí)現(xiàn)延遲注入。當(dāng)用在 @Configuration
類上時(shí)宁否,它影響該配置中所有的 @Bean
定義窒升。
/**
* 指示一個(gè)bean是否應(yīng)懶加載初始化。
*
* 可直接或間接地用于帶 org.springframework.stereotype.Component @Component 注解的類慕匠,
* 或者用于帶有 Bean @Bean 注解的方法饱须。
*
* 如果此注解不在 @Component 或 @Bean 定義上,將會(huì)立即初始化台谊。
* 如果存在并且設(shè)置為 true蓉媳,除非被另一個(gè)bean引用或從包圍的 org.springframework.beans.factory.BeanFactory BeanFactory 中顯式檢索,
* 否則 @Bean 或 @Component 不會(huì)初始化锅铅。如果存在并設(shè)置為 false督怜,那么執(zhí)行積極初始化單例的bean工廠將在啟動(dòng)時(shí)實(shí)例化bean。
*
* 如果Lazy存在于 Configuration @Configuration 類上狠角,表示該 @Configuration 中的所有 @Bean 方法都應(yīng)懶加載号杠。
* 如果在一個(gè)帶有 @Lazy 注解的 @Configuration 類的 @Bean 方法上 @Lazy 設(shè)置為false,則表示覆蓋了“默認(rèn)懶惰”行為丰歌,該bean應(yīng)立即初始化家肯。
*
* 除了其在組件初始化中的作用外,此注解也可用于帶有 org.springframework.beans.factory.annotation.Autowired 或 javax.inject.Inject 的注入點(diǎn):
* 在這種情況下母赵,它會(huì)導(dǎo)致為所有受影響的依賴關(guān)系創(chuàng)建一個(gè)懶解析代理碍现,作為使用 org.springframework.beans.factory.ObjectFactory 或 javax.inject.Provider 的替代方法。
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see Primary
* @see Bean
* @see Configuration
* @see org.springframework.stereotype.Component
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
/**
* 是否應(yīng)進(jìn)行懶加載初始化晓勇。
*/
boolean value() default true;
}
四堂飞、主要功能
-
延遲初始化
- 當(dāng)
@Lazy
注解應(yīng)用于一個(gè)@Bean
或@Component
上時(shí),該 bean 不會(huì)在 Spring 容器啟動(dòng)時(shí)立即初始化绑咱。而是直到首次被引用或請(qǐng)求時(shí)才進(jìn)行初始化绰筛。
- 當(dāng)
-
延遲注入
- 當(dāng)
@Lazy
注解應(yīng)用于@Autowired
或其他注入點(diǎn)上時(shí),它導(dǎo)致為所注入的依賴關(guān)系創(chuàng)建一個(gè)懶解析代理描融,實(shí)現(xiàn)首次訪問時(shí)的延遲注入铝噩。
- 當(dāng)
五、最佳實(shí)踐
首先來看看啟動(dòng)類入口窿克,上下文環(huán)境使用AnnotationConfigApplicationContext
(此類是使用Java注解來配置Spring容器的方式)骏庸,構(gòu)造參數(shù)我們給定了一個(gè)MyConfiguration
組件類毛甲,然后從中獲取一個(gè) MyService
bean 并調(diào)用其 show
方法。
public class LazyApplication {
public static void main(String[] args) {
System.out.println("啟動(dòng) Spring ApplicationContext...");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
System.out.println("啟動(dòng)完成 Spring ApplicationContext...");
System.out.println("獲取MyService...");
MyService myService = context.getBean(MyService.class);
System.out.println("調(diào)用show方法...");
myService.show();
}
}
這里使用@Bean
注解具被,MyBean
被標(biāo)注了 @Lazy
玻募,這意味著它只會(huì)在首次被請(qǐng)求時(shí)才會(huì)初始化。MyService
沒有 @Lazy
注解一姿,所以它會(huì)被立即初始化七咧。
@Configuration
public class MyConfiguration {
@Bean
@Lazy
public MyBean myBean(){
System.out.println("MyBean 初始化了!");
return new MyBean();
}
@Bean
public MyService myService(){
return new MyService();
}
}
MyBean
類定義很簡單。它有一個(gè)構(gòu)造函數(shù)啸蜜,在構(gòu)造函數(shù)中打印了 "MyBean 的構(gòu)造函數(shù)被調(diào)用了!"坑雅,并有一個(gè) show
方法,該方法打印 "hello world!"衬横。
public class MyBean {
public MyBean() {
System.out.println("MyBean 的構(gòu)造函數(shù)被調(diào)用了!");
}
public void show() {
System.out.println("hello world!");
}
}
MyService
類有一個(gè)對(duì) MyBean
的引用裹粤,而該引用通過 @Autowired
進(jìn)行依賴注入。由于我們還在這個(gè)注入點(diǎn)上添加了 @Lazy
注解蜂林,這意味著 myBean
的實(shí)際注入將被延遲遥诉,直到我們首次嘗試訪問它時(shí)。
public class MyService {
@Autowired
@Lazy
private MyBean myBean;
public void show() {
System.out.println("MyBean Class = " + myBean.getClass());
myBean.show();
}
}
運(yùn)行結(jié)果發(fā)現(xiàn)
-
延遲初始化:
- 盡管
MyService
在應(yīng)用上下文啟動(dòng)后可用噪叙,但由于MyBean
上的@Lazy
注解矮锈,MyBean
在啟動(dòng)時(shí)并未被初始化。 - 只有在首次訪問或使用
MyBean
時(shí)睁蕾,它才真正被初始化苞笨。這在 "MyBean 初始化了!" 和 "MyBean 的構(gòu)造函數(shù)被調(diào)用了!" 的輸出中得到了體現(xiàn)。
- 盡管
-
延遲注入:
- 在
MyService
的show
方法首次被調(diào)用時(shí)子眶,由于@Autowired
和@Lazy
注解的組合瀑凝,Spring 不是直接注入原始的MyBean
實(shí)例,而是注入一個(gè)代理對(duì)象臭杰。 - 這個(gè)代理對(duì)象的類名為
com.xcs.spring.bean.MyBean$$EnhancerBySpringCGLIB$$2a517c55
粤咪,表明它是由 Spring 的 CGLIB 動(dòng)態(tài)生成的。 - 當(dāng)嘗試訪問該代理對(duì)象上的方法(如
show
方法)時(shí)渴杆,代理會(huì)確保真正的MyBean
被初始化并代理該方法調(diào)用寥枝。
- 在
啟動(dòng) Spring ApplicationContext...
啟動(dòng)完成 Spring ApplicationContext...
獲取MyService...
調(diào)用show方法...
MyBean Class = class com.xcs.spring.bean.MyBean$$EnhancerBySpringCGLIB$$2a517c55
MyBean 初始化了!
MyBean 的構(gòu)造函數(shù)被調(diào)用了!
hello world!
六、時(shí)序圖
Bean注冊(cè)時(shí)序圖
sequenceDiagram
LazyApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)<br>創(chuàng)建應(yīng)用上下文
AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()<br>刷新應(yīng)用上下文
AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)<br>調(diào)用BFPP方法
AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)<br>委托BFPP處理
PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)<br>調(diào)用BDRPP方法
PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry)<br>處理Bean定義
ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry)<br>處理配置類Bean
ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitions(configurationModel)<br>加載Bean定義
ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForConfigurationClass(configClass,trackedConditionEvaluator)<br>為配置類加載
ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForBeanMethod(beanMethod)<br>為@Bean方法加載
ConfigurationClassBeanDefinitionReader->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd,metadata)<br>處理注解定義
AnnotationConfigUtils->>AnnotationConfigUtils:attributesFor(metadata, Lazy.class)<br>獲取注解屬性
AnnotationConfigUtils->>AbstractBeanDefinition:setLazyInit(lazyInit)<br>設(shè)置懶加載屬性
Bean延遲創(chuàng)建時(shí)序圖
sequenceDiagram
DependsOnApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)<br>創(chuàng)建應(yīng)用上下文
AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()<br>刷新應(yīng)用上下文
AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)<br>完成bean工廠初始化
AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()<br>預(yù)實(shí)例化單例beans
DefaultListableBeanFactory->>AbstractBeanDefinition:isLazyInit()
AbstractBeanDefinition->>DefaultListableBeanFactory:返回Bean是否懶加載
DefaultListableBeanFactory->>AbstractBeanFactory:getBean(beanName)
alt Bean是懶加載
AbstractBeanFactory->>DefaultListableBeanFactory: 執(zhí)行初始化Bean對(duì)象
else Bean不是懶加載
AbstractBeanFactory->>DefaultListableBeanFactory: 跳過Bean初始化磁奖,等待真正使用時(shí)在初始化
end
Bean延遲注入時(shí)序圖
sequenceDiagram
Title: InstantiationAwareBeanPostProcessor時(shí)序圖
InstantiationAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext<br>開始初始化
AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh<br>刷新上下文
AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization<br>實(shí)例化非懶加載的單列Bean
AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons<br>預(yù)實(shí)例化Singleton
DefaultListableBeanFactory->>AbstractBeanFactory:getBean<br>根據(jù)beanName獲取對(duì)象
AbstractBeanFactory->>AbstractBeanFactory:doGetBean<br>返回指定bean的實(shí)例
AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton<br>獲取單例對(duì)象
DefaultSingletonBeanRegistry->>AbstractBeanFactory:getObject<br>ObjectFactory接口的工廠方法
AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean<br>創(chuàng)建一個(gè)bean實(shí)例囊拜,填充bean實(shí)例,應(yīng)用后處理器
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:doCreateBean(beanName,mbd,args)
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)
AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(target,beanName,pvs)
InjectionMetadata->>AutowiredFieldElement:inject(bean,beanName,pvs)
AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName)
AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(descriptor,requestingBeanName,autowiredBeanNames,typeConverter)
DefaultListableBeanFactory->>DefaultListableBeanFactory:getAutowireCandidateResolver()
DefaultListableBeanFactory->>ContextAnnotationAutowireCandidateResolver:getLazyResolutionProxyIfNecessary(descriptor,beanName)
ContextAnnotationAutowireCandidateResolver->>ContextAnnotationAutowireCandidateResolver:isLazy(descriptor)
ContextAnnotationAutowireCandidateResolver->>ContextAnnotationAutowireCandidateResolver:buildLazyResolutionProxy(descriptor, beanName)
ContextAnnotationAutowireCandidateResolver->>ProxyFactory:getProxy(classLoader)
ProxyFactory->>ContextAnnotationAutowireCandidateResolver:返回被代理的對(duì)象
ContextAnnotationAutowireCandidateResolver->>DefaultListableBeanFactory:返回被代理的對(duì)象
DefaultListableBeanFactory->>AutowiredFieldElement:返回被代理的對(duì)象
AutowiredFieldElement->>Field:field.set(bean, value)注入被代理的對(duì)象
七点寥、源碼分析
首先來看看啟動(dòng)類入口艾疟,上下文環(huán)境使用AnnotationConfigApplicationContext
(此類是使用Java注解來配置Spring容器的方式),構(gòu)造參數(shù)我們給定了一個(gè)MyConfiguration
組件類敢辩,然后從中獲取一個(gè) MyService
bean 并調(diào)用其 show
方法蔽莱。
public class LazyApplication {
public static void main(String[] args) {
System.out.println("啟動(dòng) Spring ApplicationContext...");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
System.out.println("啟動(dòng)完成 Spring ApplicationContext...");
System.out.println("獲取MyService...");
MyService myService = context.getBean(MyService.class);
System.out.println("調(diào)用show方法...");
myService.show();
}
}
在org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext
構(gòu)造函數(shù)中,執(zhí)行了三個(gè)步驟戚长。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
在org.springframework.context.support.AbstractApplicationContext#refresh
方法中盗冷,我們重點(diǎn)關(guān)注一下finishBeanFactoryInitialization(beanFactory)
這方法會(huì)對(duì)實(shí)例化所有剩余非懶加載的單列Bean對(duì)象,其他方法不是本次源碼閱讀的重點(diǎn)暫時(shí)忽略同廉。
@Override
public void refresh() throws BeansException, IllegalStateException {
// ... [代碼部分省略以簡化]
// 實(shí)例化所有剩余非懶加載的單列Bean對(duì)象
finishBeanFactoryInitialization(beanFactory);
// ... [代碼部分省略以簡化]
}
在org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
方法中仪糖,會(huì)繼續(xù)調(diào)用DefaultListableBeanFactory
類中的preInstantiateSingletons
方法來完成所有剩余非懶加載的單列Bean對(duì)象。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// ... [代碼部分省略以簡化]
// 完成所有剩余非懶加載的單列Bean對(duì)象迫肖。
beanFactory.preInstantiateSingletons();
}
延遲初始化
在org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
方法中锅劝,確保所有非懶加載的單例 beans 在容器啟動(dòng)時(shí)都被初始化,除非它們顯式地標(biāo)記為懶加載蟆湖。這也是為什么 @Lazy
注解對(duì)于那些想要推遲 bean 初始化的場景非常有用故爵。
public void preInstantiateSingletons() throws BeansException {
// ... [代碼部分省略以簡化]
// 復(fù)制Bean名稱列表
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 初始化非懶加載單例
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// Bean屬性檢查
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 是否為工廠Bean
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 是否為FactoryBean實(shí)例
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
// 安全權(quán)限檢查
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
// 是否立即初始化
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
// 立即初始化Bean
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 獲取/創(chuàng)建Bean
getBean(beanName);
}
}
}
// ... [代碼部分省略以簡化]
}
延遲注入
在org.springframework.beans.factory.support.AbstractBeanFactory#getBean()
方法中,又調(diào)用了doGetBean
方法來實(shí)際執(zhí)行創(chuàng)建Bean的過程隅津,傳遞給它bean的名稱和一些其他默認(rèn)的參數(shù)值诬垂。此處,doGetBean
負(fù)責(zé)大部分工作伦仍,如查找bean定義结窘、創(chuàng)建bean(如果尚未創(chuàng)建)、處理依賴關(guān)系等充蓝。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
在org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法中隧枫,首先檢查所請(qǐng)求的bean是否是一個(gè)單例并且已經(jīng)創(chuàng)建。如果尚未創(chuàng)建谓苟,它將創(chuàng)建一個(gè)新的實(shí)例官脓。在這個(gè)過程中,它處理可能的異常情況娜谊,如循環(huán)引用确买,并確保返回的bean是正確的類型。這是Spring容器bean生命周期管理的核心部分纱皆。
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// ... [代碼部分省略以簡化]
// 開始創(chuàng)建bean實(shí)例
if (mbd.isSingleton()) {
// 如果bean是單例的湾趾,我們會(huì)嘗試從單例緩存中獲取它
// 如果不存在,則使用lambda創(chuàng)建一個(gè)新的實(shí)例
sharedInstance = getSingleton(beanName, () -> {
try {
// 嘗試創(chuàng)建bean實(shí)例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// ... [代碼部分省略以簡化]
}
});
// 對(duì)于某些bean(例如FactoryBeans)派草,可能需要進(jìn)一步處理以獲取真正的bean實(shí)例
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ... [代碼部分省略以簡化]
// 確保返回的bean實(shí)例與請(qǐng)求的類型匹配
return adaptBeanInstance(name, beanInstance, requiredType);
}
在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()
方法中搀缠,主要負(fù)責(zé)從單例緩存中獲取一個(gè)已存在的bean實(shí)例,或者使用提供的ObjectFactory
創(chuàng)建一個(gè)新的實(shí)例近迁。這是確保bean在Spring容器中作為單例存在的關(guān)鍵部分艺普。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// ... [代碼部分省略以簡化]
// 同步訪問單例對(duì)象緩存,確保線程安全
synchronized (this.singletonObjects) {
// 從緩存中獲取單例對(duì)象
Object singletonObject = this.singletonObjects.get(beanName);
// 如果緩存中沒有找到
if (singletonObject == null) {
// ... [代碼部分省略以簡化]
try {
// 使用工廠創(chuàng)建新的單例實(shí)例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// ... [代碼部分省略以簡化]
}
catch (BeanCreationException ex) {
// ... [代碼部分省略以簡化]
}
finally {
// ... [代碼部分省略以簡化]
}
// ... [代碼部分省略以簡化]
}
// 返回單例對(duì)象
return singletonObject;
}
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()
方法中,主要的邏輯是調(diào)用 doCreateBean
歧譬,這是真正進(jìn)行 bean 實(shí)例化岸浑、屬性填充和初始化的地方。這個(gè)方法會(huì)返回新創(chuàng)建的 bean 實(shí)例瑰步。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... [代碼部分省略以簡化]
try {
// 正常的bean實(shí)例化矢洲、屬性注入和初始化。
// 這里是真正進(jìn)行bean創(chuàng)建的部分缩焦。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
// 記錄bean成功創(chuàng)建的日志
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// ... [代碼部分省略以簡化]
}
catch (Throwable ex) {
// ... [代碼部分省略以簡化]
}
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
方法中读虏,對(duì) bean 的屬性進(jìn)行注入。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... [代碼部分省略以簡化]
try {
// 屬性注入:這一步將配置中的屬性值注入到bean實(shí)例中袁滥。例如盖桥,XML中定義的屬性或使用@Autowired和@Value注解的屬性都會(huì)在這里被注入
populateBean(beanName, mbd, instanceWrapper);
// ... [代碼部分省略以簡化]
}
catch (Throwable ex) {
// ... [代碼部分省略以簡化]
}
// ... [代碼部分省略以簡化]
}
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
方法中,如果一個(gè)屬性被標(biāo)記為 @Autowired
题翻,并且與 @Lazy
結(jié)合使用揩徊,那么實(shí)際的懶加載邏輯會(huì)在這個(gè)步驟中的其他部分被處理(特別是通過 AutowiredAnnotationBeanPostProcessor
)。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// ... [代碼部分省略以簡化]
// 對(duì)每一個(gè)InstantiationAwareBeanPostProcessor進(jìn)行處理藐握,這些處理器可能會(huì)修改Bean的屬性值靴拱。
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
// 嘗試使用新版本的方法 postProcessProperties
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
// ... [代碼部分省略以簡化]
}
// ... [代碼部分省略以簡化]
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
方法中,用于處理 @Autowired
注解的依賴注入猾普。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 根據(jù)bean的名稱和類查找@Autowired注解元數(shù)據(jù)
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 執(zhí)行實(shí)際的依賴注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
// 拋出bean創(chuàng)建異常
throw ex;
}
catch (Throwable ex) {
// 處理其他類型的異常袜炕,轉(zhuǎn)換為Bean創(chuàng)建異常
throw new BeanCreationException(beanName, "依賴自動(dòng)注入失敗", ex);
}
// 返回屬性值
return pvs;
}
在org.springframework.beans.factory.annotation.InjectionMetadata#inject
方法中,遍歷所有這些元素并調(diào)用其 inject
方法初家,這樣實(shí)現(xiàn)了對(duì)帶有注解的字段或方法的實(shí)際注入偎窘。
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 獲取已校驗(yàn)的注入元素
Collection<InjectedElement> checkedElements = this.checkedElements;
// 如果沒有已校驗(yàn)的元素,則使用所有注入元素
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
// 遍歷所有待注入的元素(字段或方法)
for (InjectedElement element : elementsToIterate) {
// 執(zhí)行實(shí)際的注入操作
element.inject(target, beanName, pvs);
}
}
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
方法中溜在,這個(gè) inject
方法的核心邏輯是解析 @Autowired
字段的值(即找到匹配的 bean 依賴)并注入到目標(biāo) bean 中陌知。在這個(gè)過程中,使用緩存以提高性能掖肋。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 獲取帶有@Autowired注解的字段
Field field = (Field) this.member;
Object value;
// 如果字段的值已經(jīng)被緩存仆葡,則直接從緩存中獲取
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// 如果緩存中的bean意外被移除 -> 重新解析
value = resolveFieldValue(field, bean, beanName);
}
}
else {
// 解析字段的值(即找到要注入的bean)
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
// 使字段可訪問(例如,如果它是私有的)
ReflectionUtils.makeAccessible(field);
// 將解析出的值(bean)注入到目標(biāo)bean的字段中
field.set(bean, value);
}
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
方法中志笼,主要用于解析 @Autowired
注解的字段值沿盅。它確定了應(yīng)該為目標(biāo)字段注入哪個(gè) bean。
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
// ... [代碼部分省略以簡化]
Object value;
try {
// 解析依賴:這里的核心邏輯是使用bean工廠去解析字段的依賴纫溃。它會(huì)查找合適的bean來注入到此字段腰涧。
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
// 當(dāng)無法解析依賴時(shí),拋出異常
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
// ... [代碼部分省略以簡化]
// 返回解析到的值(bean)
return value;
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
方法中紊浩,這里的關(guān)鍵邏輯是 getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary
窖铡,該方法嘗試為描述的依賴關(guān)系獲取一個(gè)懶加載代理疗锐。如果該依賴關(guān)系標(biāo)記為懶加載 (@Lazy
),并且被 @Autowired
引用费彼,那么它將返回一個(gè)懶加載代理滑臊,而不是實(shí)際的 bean。這樣敌买,只有當(dāng)應(yīng)用程序真正訪問該依賴關(guān)系時(shí)简珠,實(shí)際的 bean 才會(huì)被初始化阶界。
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ... [代碼部分省略以簡化]
// 嘗試為描述的依賴關(guān)系獲取一個(gè)懶加載代理虹钮。如果依賴是懶加載的,這將返回一個(gè)代理對(duì)象膘融。
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
// 如果沒有為懶加載的依賴關(guān)系獲取到代理芙粱,則嘗試直接解析依賴關(guān)系
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result; // 返回解析得到的bean或者懶加載代理
}
在org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary
方法中,如果是懶加載氧映,會(huì)調(diào)用 buildLazyResolutionProxy
來創(chuàng)建一個(gè)代理春畔,當(dāng)首次訪問該代理時(shí),代理會(huì)觸發(fā)實(shí)際的 bean 初始化岛都。
@Override
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
// 判斷依賴描述是否被標(biāo)記為懶加載
// 如果是懶加載律姨,為其構(gòu)建一個(gè)懶加載代理
// 如果不是懶加載,則返回null
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
在org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy
方法中臼疫,核心部分是 TargetSource
和 ProxyFactory
择份。當(dāng)我們?cè)L問這個(gè)懶加載代理時(shí),TargetSource
的 getTarget
方法會(huì)被調(diào)用烫堤,它會(huì)解析和返回真正的 bean(或其他依賴項(xiàng))荣赶。使用 ProxyFactory
,可以為給定的 TargetSource
創(chuàng)建一個(gè)新的代理鸽斟。這是 @Lazy
注解在字段注入時(shí)的實(shí)際實(shí)現(xiàn)拔创,確保在首次訪問字段時(shí)才觸發(fā)依賴的解析和bean的初始化。
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
// 獲取當(dāng)前的BeanFactory
BeanFactory beanFactory = getBeanFactory();
// 確認(rèn)BeanFactory是DefaultListableBeanFactory的實(shí)例
Assert.state(beanFactory instanceof DefaultListableBeanFactory,
"BeanFactory needs to be a DefaultListableBeanFactory");
final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
// 創(chuàng)建一個(gè)目標(biāo)源(TargetSource)用于懶加載代理
TargetSource ts = new TargetSource() {
// 獲取依賴的類型
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
// 當(dāng)訪問代理時(shí)富蓄,該方法被調(diào)用來解析實(shí)際的依賴關(guān)系
@Override
public Object getTarget() {
// ... [代碼部分省略以簡化]
return target; // 返回解析得到的bean
}
@Override
public void releaseTarget(Object target) {
}
};
// 使用Spring的ProxyFactory創(chuàng)建一個(gè)新的代理
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
// 返回懶加載代理
return pf.getProxy(dlbf.getBeanClassLoader());
}
八剩燥、注意事項(xiàng)
-
默認(rèn)行為
- 如果沒有使用
@Lazy
注解,Spring 容器會(huì)在啟動(dòng)時(shí)立即實(shí)例化單例 bean立倍。通過使用@Lazy
灭红,我們可以改變這個(gè)行為,使得 bean 只在首次請(qǐng)求時(shí)被初始化帐萎。
- 如果沒有使用
-
構(gòu)造函數(shù)注入
- 如果一個(gè)懶加載的 bean 依賴于另一個(gè)非懶加載的 bean比伏,那么該懶加載的 bean 會(huì)在容器啟動(dòng)時(shí)被初始化,因?yàn)樗囊蕾囆枰肌_@種情況常常在構(gòu)造函數(shù)注入時(shí)出現(xiàn)赁项。
-
上下文的生命周期
-
@Lazy
對(duì)于應(yīng)用上下文的啟動(dòng)速度可能有益,因?yàn)樯倭艘恍┘磿r(shí)初始化的工作。但請(qǐng)注意悠菜,延遲初始化可能會(huì)導(dǎo)致我們?cè)谑状卧L問 bean 時(shí)遇到延遲舰攒。
-
-
與
@Autowired
結(jié)合使用- 如果我們?cè)谝粋€(gè)字段或 setter 方法上同時(shí)使用
@Autowired
和@Lazy
,Spring 會(huì)注入一個(gè)代理對(duì)象悔醋。這個(gè)代理對(duì)象會(huì)在我們首次訪問它時(shí)觸發(fā)真正的 bean 初始化摩窃。
- 如果我們?cè)谝粋€(gè)字段或 setter 方法上同時(shí)使用
-
懶加載的代理
- 當(dāng)與
@Autowired
結(jié)合使用時(shí),要確保 bean 的類型與代理類型兼容芬骄。如果注入的 bean 類型是一個(gè)接口猾愿,Spring 會(huì)創(chuàng)建一個(gè)基于接口的代理。如果是一個(gè)類账阻,Spring 會(huì)創(chuàng)建一個(gè)基于類的代理蒂秘。
- 當(dāng)與
-
錯(cuò)誤處理
- 延遲初始化意味著某些錯(cuò)誤可能在應(yīng)用啟動(dòng)時(shí)不會(huì)被立即發(fā)現(xiàn),例如 bean 配置錯(cuò)誤淘太。這樣的錯(cuò)誤只有在實(shí)際嘗試初始化 bean 時(shí)才會(huì)被拋出姻僧。
-
在組件類上使用
- 對(duì)于直接或間接使用
@Component
、@Service
蒲牧、@Repository
或@Controller
注解的類撇贺,可以使用@Lazy
注解來使這些組件在首次被注入或查找時(shí)才被初始化。
- 對(duì)于直接或間接使用
九冰抢、總結(jié)
最佳實(shí)踐總結(jié)
-
上下文初始化:
- 使用
AnnotationConfigApplicationContext
初始化應(yīng)用上下文是針對(duì)基于Java的配置的推薦做法松嘶。 - 提供一個(gè)配置類(如
MyConfiguration
)作為參數(shù)可以幫助上下文知道如何加載和配置beans。
- 使用
-
懶加載配置:
- 通過在配置類的
@Bean
方法上添加@Lazy
注解晒屎,可以確保特定的bean只在首次請(qǐng)求時(shí)被初始化喘蟆,而不是在應(yīng)用上下文啟動(dòng)時(shí)。 - 這可以提高應(yīng)用啟動(dòng)速度鼓鲁,特別是對(duì)于資源密集型的beans或需要長時(shí)間初始化的beans蕴轨。
- 通過在配置類的
-
依賴注入:
- 使用
@Autowired
注解是Spring的核心特性,它可以自動(dòng)注入bean的依賴關(guān)系骇吭。 - 當(dāng)與
@Lazy
注解組合使用時(shí)橙弱,Spring會(huì)注入一個(gè)代理對(duì)象而不是實(shí)際的bean實(shí)例。這個(gè)代理對(duì)象在首次訪問時(shí)會(huì)觸發(fā)真正的bean初始化燥狰。
- 使用
-
理解代理:
- 由于延遲注入棘脐,通過
@Autowired
和@Lazy
注解注入的對(duì)象是一個(gè)由CGLIB生成的代理對(duì)象。 - 這個(gè)代理對(duì)象負(fù)責(zé)在首次訪問時(shí)初始化真正的bean龙致。
- 由于延遲注入棘脐,通過
-
輸出與驗(yàn)證:
- 通過在bean的初始化和方法調(diào)用中添加日志或打印語句蛀缝,可以驗(yàn)證和觀察懶加載和代理的行為。
- 這對(duì)于確保應(yīng)用的預(yù)期行為和性能調(diào)優(yōu)非常有用目代。
源碼分析總結(jié)
-
啟動(dòng)及初始化:
- 使用
AnnotationConfigApplicationContext
初始化應(yīng)用上下文屈梁。 - 在
AnnotationConfigApplicationContext
的構(gòu)造函數(shù)中嗤练,執(zhí)行了注冊(cè)(register
)和刷新(refresh
)方法。
- 使用
-
Bean的實(shí)例化:
- 在上下文刷新過程中在讶,
finishBeanFactoryInitialization(beanFactory)
方法確保所有非懶加載的單例Beans被實(shí)例化煞抬。 -
DefaultListableBeanFactory#preInstantiateSingletons
方法確保所有非懶加載的單例Beans在容器啟動(dòng)時(shí)被初始化。
- 在上下文刷新過程中在讶,
-
延遲初始化:
- 如果Bean被標(biāo)記為
@Lazy
构哺,它將不會(huì)在容器啟動(dòng)時(shí)被初始化革答,但只在首次請(qǐng)求時(shí)。 -
DefaultListableBeanFactory#preInstantiateSingletons
方法中曙强,對(duì)于isLazyInit
返回true
的Beans残拐,不會(huì)進(jìn)行預(yù)初始化。
- 如果Bean被標(biāo)記為
-
Bean的獲取與依賴注入:
- 使用
AbstractBeanFactory#getBean
方法獲取Bean實(shí)例旗扑。 - 如果Bean尚未創(chuàng)建蹦骑,
doGetBean
方法將執(zhí)行Bean的實(shí)際創(chuàng)建,包括解析依賴關(guān)系臀防、處理循環(huán)引用等。 - 對(duì)于單例Beans边败,它們將被緩存袱衷,確保每次都返回相同的實(shí)例。
- 通過
AbstractAutowireCapableBeanFactory#createBean
來進(jìn)行實(shí)際的Bean創(chuàng)建笑窜,并且將其屬性通過populateBean
方法注入致燥。
- 使用
-
延遲注入:
- 如果一個(gè)字段或?qū)傩员?code>@Autowired注解,并與
@Lazy
結(jié)合使用排截,實(shí)際的懶加載邏輯會(huì)在屬性填充階段被處理嫌蚤。 - 使用
AutowiredAnnotationBeanPostProcessor
來處理帶有@Autowired
注解的屬性的注入。 - 在
AutowiredFieldElement#inject
方法中断傲,如果字段被標(biāo)記為@Lazy
脱吱,Spring不會(huì)直接注入真實(shí)的Bean,而是注入一個(gè)懶加載代理认罩。 - 這個(gè)懶加載代理的實(shí)際行為是在首次訪問時(shí)觸發(fā)真正的Bean初始化箱蝠。
- 如果一個(gè)字段或?qū)傩员?code>@Autowired注解,并與
-
懶加載代理的創(chuàng)建:
- 使用
ContextAnnotationAutowireCandidateResolver
來檢查依賴關(guān)系是否需要懶加載。 - 如果需要懶加載垦垂,它將使用
buildLazyResolutionProxy
方法來為該依賴關(guān)系創(chuàng)建一個(gè)代理宦搬。 - 這個(gè)代理的行為是:在首次訪問時(shí),它會(huì)解析和返回真正的Bean或其他依賴項(xiàng)劫拗。
- 使用Spring的
ProxyFactory
來為給定的目標(biāo)源創(chuàng)建新的代理间校。
- 使用