## @Lazy - [@Lazy](#lazy) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [Bean注册时序图](#bean注册时序图) - [Bean延迟创建时序图](#bean延迟创建时序图) - [Bean延迟注入时序图](#bean延迟注入时序图) - [七、源码分析](#七源码分析) - [延迟初始化](#延迟初始化) - [延迟注入](#延迟注入) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/133800805) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Lazy源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-lazy) ### 二、注解描述 `@Lazy`注解,它的主要用途是延迟依赖注入的初始化。通常情况下,当 ApplicationContext 被启动和刷新时,所有的单例 bean 会被立即初始化。但有时,可能希望某些 bean 在首次使用时才被初始化。 ### 三、注解源码 `@Lazy`注解是 Spring 框架自 3.0 版本开始引入的一个核心注解,用于控制 bean 的懒加载行为。默认情况下,当 `@Lazy` 被应用,bean 不会在 Spring 容器启动时立即初始化,而是在首次被引用或请求时。这适用于通过 `@Component` 或 `@Bean` 定义的 bean。此外,`@Lazy` 还可以用于注入点,如 `@Autowired`,创建一个懒解析代理,从而实现延迟注入。当用在 `@Configuration` 类上时,它影响该配置中所有的 `@Bean` 定义。 ```java /** * 指示一个bean是否应懒加载初始化。 * * 可直接或间接地用于带 org.springframework.stereotype.Component @Component 注解的类, * 或者用于带有 Bean @Bean 注解的方法。 * * 如果此注解不在 @Component 或 @Bean 定义上,将会立即初始化。 * 如果存在并且设置为 true,除非被另一个bean引用或从包围的 org.springframework.beans.factory.BeanFactory BeanFactory 中显式检索, * 否则 @Bean 或 @Component 不会初始化。如果存在并设置为 false,那么执行积极初始化单例的bean工厂将在启动时实例化bean。 * * 如果Lazy存在于 Configuration @Configuration 类上,表示该 @Configuration 中的所有 @Bean 方法都应懒加载。 * 如果在一个带有 @Lazy 注解的 @Configuration 类的 @Bean 方法上 @Lazy 设置为false,则表示覆盖了“默认懒惰”行为,该bean应立即初始化。 * * 除了其在组件初始化中的作用外,此注解也可用于带有 org.springframework.beans.factory.annotation.Autowired 或 javax.inject.Inject 的注入点: * 在这种情况下,它会导致为所有受影响的依赖关系创建一个懒解析代理,作为使用 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 { /** * 是否应进行懒加载初始化。 */ boolean value() default true; } ``` ### 四、主要功能 1. **延迟初始化** + 当 `@Lazy` 注解应用于一个 `@Bean` 或 `@Component` 上时,该 bean 不会在 Spring 容器启动时立即初始化。而是直到首次被引用或请求时才进行初始化。 2. **延迟注入** + 当 `@Lazy` 注解应用于 `@Autowired` 或其他注入点上时,它导致为所注入的依赖关系创建一个懒解析代理,实现首次访问时的延迟注入。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后从中获取一个 `MyService` bean 并调用其 `show` 方法。 ```java public class LazyApplication { public static void main(String[] args) { System.out.println("启动 Spring ApplicationContext..."); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("启动完成 Spring ApplicationContext..."); System.out.println("获取MyService..."); MyService myService = context.getBean(MyService.class); System.out.println("调用show方法..."); myService.show(); } } ``` 这里使用`@Bean`注解,`MyBean` 被标注了 `@Lazy`,这意味着它只会在首次被请求时才会初始化。`MyService` 没有 `@Lazy` 注解,所以它会被立即初始化。 ```java @Configuration public class MyConfiguration { @Bean @Lazy public MyBean myBean(){ System.out.println("MyBean 初始化了!"); return new MyBean(); } @Bean public MyService myService(){ return new MyService(); } } ``` `MyBean` 类定义很简单。它有一个构造函数,在构造函数中打印了 "MyBean 的构造函数被调用了!",并有一个 `show` 方法,该方法打印 "hello world!"。 ```java public class MyBean { public MyBean() { System.out.println("MyBean 的构造函数被调用了!"); } public void show() { System.out.println("hello world!"); } } ``` `MyService` 类有一个对 `MyBean` 的引用,而该引用通过 `@Autowired` 进行依赖注入。由于我们还在这个注入点上添加了 `@Lazy` 注解,这意味着 `myBean` 的实际注入将被延迟,直到我们首次尝试访问它时。 ```java public class MyService { @Autowired @Lazy private MyBean myBean; public void show() { System.out.println("MyBean Class = " + myBean.getClass()); myBean.show(); } } ``` 运行结果发现 1. **延迟初始化**: - 尽管 `MyService` 在应用上下文启动后可用,但由于 `MyBean` 上的 `@Lazy` 注解,`MyBean` 在启动时并未被初始化。 - 只有在首次访问或使用 `MyBean` 时,它才真正被初始化。这在 "MyBean 初始化了!" 和 "MyBean 的构造函数被调用了!" 的输出中得到了体现。 2. **延迟注入**: - 在 `MyService` 的 `show` 方法首次被调用时,由于 `@Autowired` 和 `@Lazy` 注解的组合,Spring 不是直接注入原始的 `MyBean` 实例,而是注入一个代理对象。 - 这个代理对象的类名为 `com.xcs.spring.bean.MyBean$$EnhancerBySpringCGLIB$$2a517c55`,表明它是由 Spring 的 CGLIB 动态生成的。 - 当尝试访问该代理对象上的方法(如 `show` 方法)时,代理会确保真正的 `MyBean` 被初始化并代理该方法调用。 ```java 启动 Spring ApplicationContext... 启动完成 Spring ApplicationContext... 获取MyService... 调用show方法... MyBean Class = class com.xcs.spring.bean.MyBean$$EnhancerBySpringCGLIB$$2a517c55 MyBean 初始化了! MyBean 的构造函数被调用了! hello world! ``` ### 六、时序图 #### Bean注册时序图 ~~~mermaid sequenceDiagram LazyApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)
调用BFPP方法 AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)
委托BFPP处理 PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)
调用BDRPP方法 PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry)
处理Bean定义 ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry)
处理配置类Bean ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitions(configurationModel)
加载Bean定义 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForConfigurationClass(configClass,trackedConditionEvaluator)
为配置类加载 ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForBeanMethod(beanMethod)
为@Bean方法加载 ConfigurationClassBeanDefinitionReader->>AnnotationConfigUtils:processCommonDefinitionAnnotations(abd,metadata)
处理注解定义 AnnotationConfigUtils->>AnnotationConfigUtils:attributesFor(metadata, Lazy.class)
获取注解属性 AnnotationConfigUtils->>AbstractBeanDefinition:setLazyInit(lazyInit)
设置懒加载属性 ~~~ #### Bean延迟创建时序图 ~~~mermaid sequenceDiagram DependsOnApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)
创建应用上下文 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()
刷新应用上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization(beanFactory)
完成bean工厂初始化 AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons()
预实例化单例beans DefaultListableBeanFactory->>AbstractBeanDefinition:isLazyInit() AbstractBeanDefinition->>DefaultListableBeanFactory:返回Bean是否懒加载 DefaultListableBeanFactory->>AbstractBeanFactory:getBean(beanName) alt Bean是懒加载 AbstractBeanFactory->>DefaultListableBeanFactory: 执行初始化Bean对象 else Bean不是懒加载 AbstractBeanFactory->>DefaultListableBeanFactory: 跳过Bean初始化,等待真正使用时在初始化 end ~~~ #### Bean延迟注入时序图 ~~~mermaid sequenceDiagram Title: InstantiationAwareBeanPostProcessor时序图 InstantiationAwareBeanPostProcessorApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext
开始初始化 AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh
刷新上下文 AbstractApplicationContext->>AbstractApplicationContext:finishBeanFactoryInitialization
实例化非懒加载的单列Bean AbstractApplicationContext->>DefaultListableBeanFactory:preInstantiateSingletons
预实例化Singleton DefaultListableBeanFactory->>AbstractBeanFactory:getBean
根据beanName获取对象 AbstractBeanFactory->>AbstractBeanFactory:doGetBean
返回指定bean的实例 AbstractBeanFactory->>DefaultSingletonBeanRegistry:getSingleton
获取单例对象 DefaultSingletonBeanRegistry->>AbstractBeanFactory:getObject
ObjectFactory接口的工厂方法 AbstractBeanFactory->>AbstractAutowireCapableBeanFactory:createBean
创建一个bean实例,填充bean实例,应用后处理器 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:返回被代理的对象 ContextAnnotationAutowireCandidateResolver->>DefaultListableBeanFactory:返回被代理的对象 DefaultListableBeanFactory->>AutowiredFieldElement:返回被代理的对象 AutowiredFieldElement->>Field:field.set(bean, value)注入被代理的对象 ~~~ ### 七、源码分析 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类,然后从中获取一个 `MyService` bean 并调用其 `show` 方法。 ```java public class LazyApplication { public static void main(String[] args) { System.out.println("启动 Spring ApplicationContext..."); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); System.out.println("启动完成 Spring ApplicationContext..."); System.out.println("获取MyService..."); MyService myService = context.getBean(MyService.class); System.out.println("调用show方法..."); myService.show(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); } ``` 在`org.springframework.context.support.AbstractApplicationContext#refresh`方法中,我们重点关注一下`finishBeanFactoryInitialization(beanFactory)`这方法会对实例化所有剩余非懒加载的单列Bean对象,其他方法不是本次源码阅读的重点暂时忽略。 ```java @Override public void refresh() throws BeansException, IllegalStateException { // ... [代码部分省略以简化] // 实例化所有剩余非懒加载的单列Bean对象 finishBeanFactoryInitialization(beanFactory); // ... [代码部分省略以简化] } ``` 在`org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization`方法中,会继续调用`DefaultListableBeanFactory`类中的`preInstantiateSingletons`方法来完成所有剩余非懒加载的单列Bean对象。 ```java protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // ... [代码部分省略以简化] // 完成所有剩余非懒加载的单列Bean对象。 beanFactory.preInstantiateSingletons(); } ``` #### 延迟初始化 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons`方法中,确保所有非懒加载的单例 beans 在容器启动时都被初始化,除非它们显式地标记为懒加载。这也是为什么 `@Lazy` 注解对于那些想要推迟 bean 初始化的场景非常有用。 ```java public void preInstantiateSingletons() throws BeansException { // ... [代码部分省略以简化] // 复制Bean名称列表 List 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实例 if (bean instanceof FactoryBean) { FactoryBean factory = (FactoryBean) bean; boolean isEagerInit; // 安全权限检查 if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction) ((SmartFactoryBean) factory)::isEagerInit, getAccessControlContext()); } else { // 是否立即初始化 isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit()); } // 立即初始化Bean if (isEagerInit) { getBean(beanName); } } } else { // 获取/创建Bean getBean(beanName); } } } // ... [代码部分省略以简化] } ``` #### 延迟注入 在`org.springframework.beans.factory.support.AbstractBeanFactory#getBean()`方法中,又调用了`doGetBean`方法来实际执行创建Bean的过程,传递给它bean的名称和一些其他默认的参数值。此处,`doGetBean`负责大部分工作,如查找bean定义、创建bean(如果尚未创建)、处理依赖关系等。 ```java @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } ``` 在`org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean`方法中,首先检查所请求的bean是否是一个单例并且已经创建。如果尚未创建,它将创建一个新的实例。在这个过程中,它处理可能的异常情况,如循环引用,并确保返回的bean是正确的类型。这是Spring容器bean生命周期管理的核心部分。 ```java protected T doGetBean( String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // ... [代码部分省略以简化] // 开始创建bean实例 if (mbd.isSingleton()) { // 如果bean是单例的,我们会尝试从单例缓存中获取它 // 如果不存在,则使用lambda创建一个新的实例 sharedInstance = getSingleton(beanName, () -> { try { // 尝试创建bean实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { // ... [代码部分省略以简化] } }); // 对于某些bean(例如FactoryBeans),可能需要进一步处理以获取真正的bean实例 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } // ... [代码部分省略以简化] // 确保返回的bean实例与请求的类型匹配 return adaptBeanInstance(name, beanInstance, requiredType); } ``` 在`org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton()`方法中,主要负责从单例缓存中获取一个已存在的bean实例,或者使用提供的`ObjectFactory`创建一个新的实例。这是确保bean在Spring容器中作为单例存在的关键部分。 ```java public Object getSingleton(String beanName, ObjectFactory singletonFactory) { // ... [代码部分省略以简化] // 同步访问单例对象缓存,确保线程安全 synchronized (this.singletonObjects) { // 从缓存中获取单例对象 Object singletonObject = this.singletonObjects.get(beanName); // 如果缓存中没有找到 if (singletonObject == null) { // ... [代码部分省略以简化] try { // 使用工厂创建新的单例实例 singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // ... [代码部分省略以简化] } catch (BeanCreationException ex) { // ... [代码部分省略以简化] } finally { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } // 返回单例对象 return singletonObject; } } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()`方法中,主要的逻辑是调用 `doCreateBean`,这是真正进行 bean 实例化、属性填充和初始化的地方。这个方法会返回新创建的 bean 实例。 ```java @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 正常的bean实例化、属性注入和初始化。 // 这里是真正进行bean创建的部分。 Object beanInstance = doCreateBean(beanName, mbdToUse, args); // 记录bean成功创建的日志 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`方法中,对 bean 的属性进行注入。 ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // ... [代码部分省略以简化] try { // 属性注入:这一步将配置中的属性值注入到bean实例中。例如,XML中定义的属性或使用@Autowired和@Value注解的属性都会在这里被注入 populateBean(beanName, mbd, instanceWrapper); // ... [代码部分省略以简化] } catch (Throwable ex) { // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean`方法中,如果一个属性被标记为 `@Autowired`,并且与 `@Lazy` 结合使用,那么实际的懒加载逻辑会在这个步骤中的其他部分被处理(特别是通过 `AutowiredAnnotationBeanPostProcessor`)。 ```java protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // ... [代码部分省略以简化] // 对每一个InstantiationAwareBeanPostProcessor进行处理,这些处理器可能会修改Bean的属性值。 for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 尝试使用新版本的方法 postProcessProperties PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties`方法中,用于处理 `@Autowired` 注解的依赖注入。 ```java @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 根据bean的名称和类查找@Autowired注解元数据 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // 执行实际的依赖注入 metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { // 抛出bean创建异常 throw ex; } catch (Throwable ex) { // 处理其他类型的异常,转换为Bean创建异常 throw new BeanCreationException(beanName, "依赖自动注入失败", ex); } // 返回属性值 return pvs; } ``` 在`org.springframework.beans.factory.annotation.InjectionMetadata#inject`方法中,遍历所有这些元素并调用其 `inject` 方法,这样实现了对带有注解的字段或方法的实际注入。 ```java public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取已校验的注入元素 Collection checkedElements = this.checkedElements; // 如果没有已校验的元素,则使用所有注入元素 Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { // 遍历所有待注入的元素(字段或方法) for (InjectedElement element : elementsToIterate) { // 执行实际的注入操作 element.inject(target, beanName, pvs); } } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中,这个 `inject` 方法的核心逻辑是解析 `@Autowired` 字段的值(即找到匹配的 bean 依赖)并注入到目标 bean 中。在这个过程中,使用缓存以提高性能。 ```java @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 获取带有@Autowired注解的字段 Field field = (Field) this.member; Object value; // 如果字段的值已经被缓存,则直接从缓存中获取 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)注入到目标bean的字段中 field.set(bean, value); } } ``` 在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,主要用于解析 `@Autowired` 注解的字段值。它确定了应该为目标字段注入哪个 bean。 ```java @Nullable private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { // ... [代码部分省略以简化] Object value; try { // 解析依赖:这里的核心逻辑是使用bean工厂去解析字段的依赖。它会查找合适的bean来注入到此字段。 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { // 当无法解析依赖时,抛出异常 throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); } // ... [代码部分省略以简化] // 返回解析到的值(bean) return value; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,这里的关键逻辑是 `getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary`,该方法尝试为描述的依赖关系获取一个懒加载代理。如果该依赖关系标记为懒加载 (`@Lazy`),并且被 `@Autowired` 引用,那么它将返回一个懒加载代理,而不是实际的 bean。这样,只有当应用程序真正访问该依赖关系时,实际的 bean 才会被初始化。 ```java @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] // 尝试为描述的依赖关系获取一个懒加载代理。如果依赖是懒加载的,这将返回一个代理对象。 Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); // 如果没有为懒加载的依赖关系获取到代理,则尝试直接解析依赖关系 if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; // 返回解析得到的bean或者懒加载代理 } ``` 在`org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary`方法中,如果是懒加载,会调用 `buildLazyResolutionProxy` 来创建一个代理,当首次访问该代理时,代理会触发实际的 bean 初始化。 ```java @Override @Nullable public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { // 判断依赖描述是否被标记为懒加载 // 如果是懒加载,为其构建一个懒加载代理 // 如果不是懒加载,则返回null return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null); } ``` 在`org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy`方法中,核心部分是 `TargetSource` 和 `ProxyFactory`。当我们访问这个懒加载代理时,`TargetSource` 的 `getTarget` 方法会被调用,它会解析和返回真正的 bean(或其他依赖项)。使用 `ProxyFactory`,可以为给定的 `TargetSource` 创建一个新的代理。这是 `@Lazy` 注解在字段注入时的实际实现,确保在首次访问字段时才触发依赖的解析和bean的初始化。 ```java protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) { // 获取当前的BeanFactory BeanFactory beanFactory = getBeanFactory(); // 确认BeanFactory是DefaultListableBeanFactory的实例 Assert.state(beanFactory instanceof DefaultListableBeanFactory, "BeanFactory needs to be a DefaultListableBeanFactory"); final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory; // 创建一个目标源(TargetSource)用于懒加载代理 TargetSource ts = new TargetSource() { // 获取依赖的类型 @Override public Class getTargetClass() { return descriptor.getDependencyType(); } @Override public boolean isStatic() { return false; } // 当访问代理时,该方法被调用来解析实际的依赖关系 @Override public Object getTarget() { // ... [代码部分省略以简化] return target; // 返回解析得到的bean } @Override public void releaseTarget(Object target) { } }; // 使用Spring的ProxyFactory创建一个新的代理 ProxyFactory pf = new ProxyFactory(); pf.setTargetSource(ts); Class dependencyType = descriptor.getDependencyType(); if (dependencyType.isInterface()) { pf.addInterface(dependencyType); } // 返回懒加载代理 return pf.getProxy(dlbf.getBeanClassLoader()); } ``` ### 八、注意事项 1. **默认行为** + 如果没有使用 `@Lazy` 注解,Spring 容器会在启动时立即实例化单例 bean。通过使用 `@Lazy`,我们可以改变这个行为,使得 bean 只在首次请求时被初始化。 2. **构造函数注入** + 如果一个懒加载的 bean 依赖于另一个非懒加载的 bean,那么该懒加载的 bean 会在容器启动时被初始化,因为它的依赖需要它。这种情况常常在构造函数注入时出现。 3. **上下文的生命周期** + `@Lazy` 对于应用上下文的启动速度可能有益,因为少了一些即时初始化的工作。但请注意,延迟初始化可能会导致我们在首次访问 bean 时遇到延迟。 4. **与 `@Autowired` 结合使用** + 如果我们在一个字段或 setter 方法上同时使用 `@Autowired` 和 `@Lazy`,Spring 会注入一个代理对象。这个代理对象会在我们首次访问它时触发真正的 bean 初始化。 5. **懒加载的代理** + 当与 `@Autowired` 结合使用时,要确保 bean 的类型与代理类型兼容。如果注入的 bean 类型是一个接口,Spring 会创建一个基于接口的代理。如果是一个类,Spring 会创建一个基于类的代理。 6. **错误处理** + 延迟初始化意味着某些错误可能在应用启动时不会被立即发现,例如 bean 配置错误。这样的错误只有在实际尝试初始化 bean 时才会被抛出。 7. **在组件类上使用** + 对于直接或间接使用 `@Component`、`@Service`、`@Repository` 或 `@Controller` 注解的类,可以使用 `@Lazy` 注解来使这些组件在首次被注入或查找时才被初始化。 ### 九、总结 #### 最佳实践总结 1. **上下文初始化**: - 使用 `AnnotationConfigApplicationContext` 初始化应用上下文是针对基于Java的配置的推荐做法。 - 提供一个配置类(如 `MyConfiguration`)作为参数可以帮助上下文知道如何加载和配置beans。 2. **懒加载配置**: - 通过在配置类的 `@Bean` 方法上添加 `@Lazy` 注解,可以确保特定的bean只在首次请求时被初始化,而不是在应用上下文启动时。 - 这可以提高应用启动速度,特别是对于资源密集型的beans或需要长时间初始化的beans。 3. **依赖注入**: - 使用 `@Autowired` 注解是Spring的核心特性,它可以自动注入bean的依赖关系。 - 当与 `@Lazy` 注解组合使用时,Spring会注入一个代理对象而不是实际的bean实例。这个代理对象在首次访问时会触发真正的bean初始化。 4. **理解代理**: - 由于延迟注入,通过 `@Autowired` 和 `@Lazy` 注解注入的对象是一个由CGLIB生成的代理对象。 - 这个代理对象负责在首次访问时初始化真正的bean。 5. **输出与验证**: - 通过在bean的初始化和方法调用中添加日志或打印语句,可以验证和观察懒加载和代理的行为。 - 这对于确保应用的预期行为和性能调优非常有用。 #### 源码分析总结 1. **启动及初始化**: - 使用`AnnotationConfigApplicationContext`初始化应用上下文。 - 在`AnnotationConfigApplicationContext`的构造函数中,执行了注册(`register`)和刷新(`refresh`)方法。 2. **Bean的实例化**: - 在上下文刷新过程中,`finishBeanFactoryInitialization(beanFactory)`方法确保所有非懒加载的单例Beans被实例化。 - `DefaultListableBeanFactory#preInstantiateSingletons`方法确保所有非懒加载的单例Beans在容器启动时被初始化。 3. **延迟初始化**: - 如果Bean被标记为`@Lazy`,它将不会在容器启动时被初始化,但只在首次请求时。 - `DefaultListableBeanFactory#preInstantiateSingletons`方法中,对于`isLazyInit`返回`true`的Beans,不会进行预初始化。 4. **Bean的获取与依赖注入**: - 使用`AbstractBeanFactory#getBean`方法获取Bean实例。 - 如果Bean尚未创建,`doGetBean`方法将执行Bean的实际创建,包括解析依赖关系、处理循环引用等。 - 对于单例Beans,它们将被缓存,确保每次都返回相同的实例。 - 通过`AbstractAutowireCapableBeanFactory#createBean`来进行实际的Bean创建,并且将其属性通过`populateBean`方法注入。 5. **延迟注入**: - 如果一个字段或属性被`@Autowired`注解,并与`@Lazy`结合使用,实际的懒加载逻辑会在属性填充阶段被处理。 - 使用`AutowiredAnnotationBeanPostProcessor`来处理带有`@Autowired`注解的属性的注入。 - 在`AutowiredFieldElement#inject`方法中,如果字段被标记为`@Lazy`,Spring不会直接注入真实的Bean,而是注入一个懒加载代理。 - 这个懒加载代理的实际行为是在首次访问时触发真正的Bean初始化。 6. **懒加载代理的创建**: - 使用`ContextAnnotationAutowireCandidateResolver`来检查依赖关系是否需要懒加载。 - 如果需要懒加载,它将使用`buildLazyResolutionProxy`方法来为该依赖关系创建一个代理。 - 这个代理的行为是:在首次访问时,它会解析和返回真正的Bean或其他依赖项。 - 使用Spring的`ProxyFactory`来为给定的目标源创建新的代理。