spring @lazy注解的使用

spring @lazy注解的使用

在spring中@lazy注解表达延迟的含义,但在不同情况下,这里的延迟并不是同一层意思。下面将描述我发现的两层含义。

1. 延迟初始化bean

​ 首先我们知道,容器启动之前会扫描所有的class文件,并将需要加载到容器中的类,整理成BeanDefinition存储。容器启动时将依次将BeanDefinition构建成bean,构建过程中同时解决依赖注入和循环引用的问题。

​ 但并不是所有的BeanDefinition都会被构建成bean,观察源码中下面方法 org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

​ 此为容器启动过程时,从BeanDefinition构建成bean的入口。这里有三种情况不会进行初始化,

  1. 非抽象类

  2. 是单例模式的bean

  3. 非Lazy模式的

    所以这里就体现了Lazy的第一层含义,添加Lazy的注解的bean不会在容器启动时主动创建。

@Override
public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }

    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) bean;
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                            (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                            getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                       ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
            }
            else {
                getBean(beanName);
            }
        }
    }

有两种方式添加Lazy注解,一种直接在类上加@Lazy注解。一种是如果使用@Bean模式创建的bean,在方法上添加@Lazy注解。

​ 但这里Lazy的含义仅仅是不在容器启动时主动生成bean,但可能会被动生成bean。如果bean A被 bean B依赖,A是lazy的,在启动时虽然A不会被主动创建,但在创建B时,需要依赖A,此时A就会被动的创建。

​ 所以仅对某个bean的创建添加Lazy意义不是特别大,因为bean相互之间都是有依赖关系的,即使不主动创建也会被被动创建!

2. 延迟依赖注入

第二种Lazy的方式就比较有用了,如下面的例子。

@Component
    public class A{

    }

    public class B{   
        @Lazy
        @Resource
        private A a;
    }

​ 这个例子中B依赖A。在构建B时需要将A注入,但是我们添加了@Lazy注解,注入时并不会真的从容器中查找A,而是注入一个A的动态代理。在运行阶段,调用动态代理类的方法时,才会真的从容器中查找A。

​ 下面代码是 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

的内部类,这里是负责依赖注入时查找依赖的部分, getResourceToInject 方法里进行判断,如我上面讲的,如果是lazy的依赖则会生成动态代理,否则才会从容器中真实获取。

​ lazyLookup 变量的在 ResourceElement 的构造方法里获取的。

private class ResourceElement extends LookupElement {

    private final boolean lazyLookup;

    public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
        super(member, pd);
        Resource resource = ae.getAnnotation(Resource.class);
        String resourceName = resource.name();
        Class<?> resourceType = resource.type();
        this.isDefaultName = !StringUtils.hasLength(resourceName);
        if (this.isDefaultName) {
            resourceName = this.member.getName();
            if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
                resourceName = Introspector.decapitalize(resourceName.substring(3));
            }
        }
        else if (embeddedValueResolver != null) {
            resourceName = embeddedValueResolver.resolveStringValue(resourceName);
        }
        if (Object.class != resourceType) {
            checkResourceType(resourceType);
        }
        else {
            // No resource type specified... check field/method.
            resourceType = getResourceType();
        }
        this.name = (resourceName != null ? resourceName : "");
        this.lookupType = resourceType;
        String lookupValue = resource.lookup();
        this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
        Lazy lazy = ae.getAnnotation(Lazy.class);
        this.lazyLookup = (lazy != null && lazy.value());
    }

    @Override
    protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
        return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
                getResource(this, requestingBeanName));
    }
}

3.配合使用

综上所述,如果将@Lazy 添加在类上,虽不会主动创建,但被依赖时还会被创建。

如果将@Lazy添加到注入的字段上,可以推迟注入的时间到运行时,但依赖已经被创建了,只是没注入而已,该耗费的时间也已经耗费了。

将两者配合使用,即可达到运行时再创建需要的对象,如果不需要可一直不创建。

<think>好的,我现在需要回答用户关于Spring框架中@Lazy注解使用场景及示例的问题。首先,我需要回忆一下@Lazy注解的基本作用。根据用户提供的引用内容,@Lazy是用于延迟初始化bean的,也就是说,只有在真正需要这个bean的时候才会创建和初始化,而不是在应用启动时就初始化。这有助于减少启动时的资源消耗,特别是对于那些可能不经常使用的bean来说非常有用。 接下来,我需要确定使用@Lazy的主要场景。第一个想到的是解决循环依赖的问题,比如引用[2]提到的例子,当两个服务类互相注入时,可能会出现循环依赖,这时候使用@Lazy可以延迟其中一个bean的初始化,从而打破循环。另一个场景是优化启动性能,对于那些初始化耗时较长的bean,延迟加载可以加快应用的启动速度。此外,有些资源可能只在特定条件下才需要,比如特定配置开启后才使用,这时候也可以用@Lazy来避免不必要的初始化。 然后,我需要考虑如何给出示例。用户提供的引用中有一个例子,其中taskServiceImpl被注入到groupServiceImpl中,导致循环依赖。这时候在其中一个类上使用@Lazy,比如在GroupServiceImpl的构造函数参数上添加@Lazy注解,可以延迟TaskServiceImpl的初始化,从而解决循环依赖的问题。另外,还可以举一个配置类中使用@Lazy的例子,比如一个耗时较长的DataSource配置,使用@Lazy可以确保只有在第一次访问数据库时才初始化这个数据源。 在编写示例代码时,需要注意正确的注解位置,比如在构造函数的参数前,或者在配置类的方法上。同时,要提醒用户注意,@Lazy使用需要结合作用域,比如在原型作用域下可能需要不同的处理方式。另外,需要指出可能的误用情况,比如在单例bean之间使用@Lazy可能导致意外行为,或者在单元测试中需要确保bean已经被初始化。 最后,生成的相关问题要围绕用户可能进一步想了解的内容,比如与@Lazy相关的其他注解、如何调试延迟加载的问题、在微服务中的应用等。这样可以帮助用户更全面地理解@Lazy的应用场景和注意事项。</think>### @Lazy 注解Spring 中的使用场景及示例 #### 1. **延迟加载的核心作用** `@Lazy` 注解通过延迟 Bean 的初始化,**仅在首次实际使用时才创建实例**,而非应用启动时立即加载。此特性特别适用于: - **资源密集型 Bean**(如数据库连接池) - **循环依赖场景** - **条件化启用的组件**(如特定环境配置) #### 2. **典型使用场景** **场景 1:解决循环依赖** 当两个 Bean 互相注入时(如 `ServiceA` 依赖 `ServiceB`,`ServiceB` 又依赖 `ServiceA`),可通过 `@Lazy` 切断初始化死锁。 ```java @Service public class GroupServiceImpl { private final TaskServiceImpl taskService; @Autowired public GroupServiceImpl(@Lazy TaskServiceImpl taskService) { // 延迟注入 this.taskService = taskService; } } ``` 此方式使 `TaskServiceImpl` 的初始化推迟到实际调用时,打破循环链[^2]。 **场景 2:优化启动性能** 对于初始化耗时的 Bean(如第三方服务客户端),使用 `@Lazy` 可加速应用启动: ```java @Configuration public class AppConfig { @Bean @Lazy // 首次访问数据库时初始化 public DataSource heavyDataSource() { return new HikariDataSource(...); // 假设配置复杂、耗时 } } ``` **场景 3:条件化资源加载** 结合 `@Profile` 或 `@Conditional` 注解,实现按需加载: ```java @Bean @Lazy @Profile("cloud") public CloudStorageService cloudStorage() { return new AWSStorageService(); // 仅当 cloud 环境激活时加载 } ``` #### 3. **注意事项** - **作用域影响**:在原型作用域(`@Scope("prototype")`)中,`@Lazy` 可能导致每次请求生成新实例。 - **单例误用**:单例 Bean 之间过度使用 `@Lazy` 可能掩盖设计问题(如应重构的紧耦合)。 - **调试陷阱**:延迟加载的 Bean 在启动时不会暴露初始化错误,需通过实际调用触发验证。 #### 4. **扩展对比:与其他注解的关系** | 注解 | 目标 | 与 `@Lazy` 的协作 | |---------------|------------------------------|---------------------------| | `@Autowired` | 依赖注入 | 可组合使用(如示例 1) | | `@Conditional`| 条件化 Bean 创建 | 共同实现按需加载 | | `@PostConstruct` | 初始化后操作 | 延迟到首次使用时才执行 | ```java // 组合使用示例 @Service public class ReportService { @Lazy @Autowired @Qualifier("advancedGenerator") private ReportGenerator generator; // 按需加载指定实现类 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值