如何在Springboot 2.2中对所有通过@Bean或@Component定义的Spring bean进行惰性加载。

huangapple go评论89阅读模式
英文:

How to Lazy load all the Spring beans whether it is defined by @Bean or @Component in Springboot 2.2

问题

以下是您要翻译的内容:

我正在编写一个Spring应用程序,该应用程序是交互式的,基本上处理许多命令,比如创建、列出、更新、删除各种类型的资源。

目前,暂时假设应用程序的单次运行只处理单个命令,然后程序退出。

为了验证命令、执行命令、每种资源所需的工厂类,都有单独的类,每个类都用@Component注解进行注解,以便Spring管理所有组件。

还有一些通过@Bean方法手动定义的bean。

现在,我的应用程序首先要确定执行的是什么类型的命令(创建、删除、列出、更新等),我希望仅创建与该命令相关的bean,并在需要时进行自动装配(在从用户那里接收命令之后),并且我希望避免创建与其他命令相关的许多bean。

在搜索中,我了解到Spring提供了惰性实例化的Bean。但我不确定这是否是我正在寻找的解决方案。

我尝试过的

  • 首先,我发现了@Lazy注解,但由于我想要惰性加载所有的bean,我不想在每个类中都写@Lazy。
  • 然后,我发现在application.yml中设置以下属性可以实现这个目标。
spring:
  main:
    lazy-initialization: true

我尝试过,但仍然没有惰性地创建bean。

我的application.yml文件如下所示:

spring:
  main:
    lazy-initialization: true

我的主SpringBootApplication文件如下所示:

@Slf4j
@SpringBootApplication
public class SpringBootApplication {
    public static void main(String[] args) {
        System.out.println("Loading Application...");
        ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);

        final AtomicInteger counter = new AtomicInteger(0);
        log.info("**************** START: Total Bean Objects: {} ******************", context.getBeanDefinitionCount());

        Arrays.asList(context.getBeanDefinitionNames())
                .forEach(beanName -> {
                    log.info("{}) Bean Name: {} ", counter.incrementAndGet(), beanName);
                });

        log.info("**************** END: Total Bean: {} ******************", context.getBeanDefinitionCount());
    }
}

我的其他类如下所示:

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MyClass1 implements ResourceCreator<MyClass2, MyClass3> {
    private final RequestValidatorImpl requestValidator;
    private final ResourceCreator resourceCreator;

    @Override
    public MyClass2 toImplementFunction(MyClass3 myclass3) {
        //logic
    }
}

运行应用程序后,它会打印所有被我注解为@Component的类,以及由@Bean方法创建的bean。

我还尝试在Application.properties中使用以下内容,但仍然没有效果。

spring.main.lazy-initialization=true

此外,如果您愿意,可以就我是否应该像我现在使用的那样为每个类使用@Component进行注解发表评论,以及更好的实践是什么。

英文:

I am writing a spring application which is interactive and basically handles lots of commands like create, list, update, delete various types of resources.

For now, just assume a single run of the application handles only a single command and the program exits.

For all the classes to validate command, execute the command, required factory classes for each resource there is a separate class and each class is annotated with @Component annotation for spring to manage all the components.

There are also some of the manually defined beans by @Bean method.

Now that my application first identifies what kind of command is executed (create, delete, list, update, etc), I want the only beans of that command to be created and Autowired wherever required (after taking command from user) and I want to avoid the creation of dozens of beans related to other commands.

On searching, I came to know about Lazy instantiation of Beans that spring provides.
However, I am not sure if it is the weapon I am searching for.

What I tried

  • Very first I found @Lazy annotation, but since I want to lazily load all the Beans, I don't want to write @Lazy everywhere in each class.
  • Then I found setting below property in application.yml does the work.
spring:
  main:
    lazy-initialization: true

I tried that but still, it is not lazily creating the beans.

My application.yml files looks like this

spring:
  main:
    lazy-initialization: true

My main SpringBootApplication file looks like this:

@Slf4j
@SpringBootApplication
public class SpringBootApplication {
    public static void main(String[] args) {
        System.out.println(&quot;Loading Application...&quot;);
        ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);



        final AtomicInteger counter = new AtomicInteger(0);
        log.info(&quot;**************** START: Total Bean Objects: {} ******************&quot;, context.getBeanDefinitionCount());

        Arrays.asList(context.getBeanDefinitionNames())
                .forEach(beanName -&gt; {
                    log.info(&quot;{}) Bean Name: {} &quot;, counter.incrementAndGet(), beanName);
                });

        log.info(&quot;**************** END: Total Bean: {} ******************&quot;, context.getBeanDefinitionCount());
    }
}

My other classes looks like this:


@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MyClass1 implements ResourceCreator&lt;MyClass2, MyClass3&gt; {
    private final RequestValidatorImpl requestValidator;
    private final ResourceCreator resourceCreator;

@Override
public MyClass2 toImplementFunction(MyClass3 myclass3) {
//logic
}

On running the application, It prints all the classes where I annotated @Component as well as beans created by @Bean method.

I have also tried using below in Application.properties but still no use.

spring.main.lazy-initialization=true

Also, if you wish, please comment on whether I should use @Component for each Class like I am using or not and what is better practice instead.

答案1

得分: 2

I think you misunderstood the meaning of the lazy flag you are passing,
It means that the object will be created only when it is invoked but it does not say that it will not scan that package. Spring will scan all packages and store bean definition names but it will create the objects only when it is invoked, if you have passed the lazy flag to it.

You can verify this behavior by checking the number of beans created when you pass the lazy flag as true and false.
you can check it as given below

ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
System.out.println("count:" + context.getBeanDefinitionCount());

Edit on Apr/7th 2020 start

Another way to do that is to create a constructor and use that to inject the autowired properties and print out a log when they enter the constructor.

I did the same in a sample project, and below is the result. The first one is for eager initialization, and the next one is for lazy.

spring.main.lazy-initialization=false
Application logs

Inside Constructor
calling bean
inside bean method

spring.main.lazy-initialization=true
Application logs

calling bean
Inside Constructor
inside bean method

Edit on Apr/7th 2020 end

Please mark this as answered if I answered your question.
Thank you

英文:

I think you misunderstood the meaning of the lazy flag you are passing,
It means that the object will be created only when it is invoked but it does not say that it will not scan that package. Spring will scan all packages and store bean definition names but it will create the objects only when it is invoked, if you have passed the lazy flag to it.

You can verify this behaviour by checking the number of beans created when you pass the lazy flag as true and false.
you can check it as given below

ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
System.out.println(&quot;count:&quot;+context.getBeanDefinitionCount());

Edit on Apr/7th 2020 start

Another way to do that is create a constructor and use that to inject the autowired properties and print out a log when they enter the constructor.

I did the same in a sample project and below is he result, first one is for eager initialization and next one for lazy.

spring.main.lazy-initialization=false
Application logs

Inside Constructor
calling bean
inside bean method

spring.main.lazy-initialization=true
Application logs

calling bean
Inside Constructor
inside bean method

Edit on Apr/7th 2020 end

Please mark this as answered if I answered your question.
Thank you

答案2

得分: 1

长话短说:
对于希望懒惰地初始化整个Spring Boot上下文的任何人,将此属性设置为true是可行的方式:

spring.main.lazy-initialization=true

专业提示:
它可以与 @Lazy 注解结合使用,设置为false;因此,所有定义的bean都将使用懒惰初始化,除了我们使用 @Lazy(false) 显式配置的那些bean。

通过这种方式,懒惰初始化变成了选择退出,而不是默认的选择加入

英文:

Long story short:
For anyone wanting to lazily initialize their whole Spring Boot context, setting this property to true is the way to go:

spring.main.lazy-initialization=true

Pro tip:
It can be used in combination with the @Lazy annotation, set to false; so all the defined beans will use lazy initialization, except for those that we explicitly configure with @Lazy(false).

In such a way, the lazy initialization becomes opt-out instead of the default opt-in.

huangapple
  • 本文由 发表于 2020年4月6日 23:00:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/61062721.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定