Аннотация @Lazy

По умолчанию все бины создаются при запуске Spring Boot приложения. Но такое поведение можно отменить с помощью аннотации @Lazy.

Аннотация @Lazy на бине

Если бин помечен аннотацией @Lazy, то он не создается до тех пор, пока не будет явно запрошен из контекста. (При условии, что на него не ссылаются другие не Lazy бины; в этом случае он все равно будет создан).

Итак, создадим пустое Spring Boot приложение в инициализаторе  и в нем бин A:

@Component
public class A {
    public A(){
        System.out.println("A constructor");
    }
}

Запустим приложение и увидим в консоли, что бин создается:

A constructor

Теперь пометим его аннотацией @Lazy и снова запустим приложение:

@Component
@Lazy
public class A {
    public A(){
        System.out.println("A constructor");
    }
}

Вот теперь бин не создается, поскольку ссылок на него нет и он не используется. Чтобы он создавался, нужно его явно запросить из контекста, например так:

@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext applicationContext=SpringApplication.run(MainApplication.class, args);
        A a=applicationContext.getBean(A.class);
    }

}

Но если мы внедрим A в другой бин, например MainApplication, то А все равно будет создаваться, несмотря на аннотацию @Lazy. И неважно, что мы никак не используем A в приложении — не запрашиваем его из контекста и не вызываем его методов (которых нет):

@SpringBootApplication
public class MainApplication {
    private A a;

    @Autowired
    public MainApplication(A a) {
        this.a = a;
    }

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext applicationContext = SpringApplication.run(MainApplication.class, args);
    }

}

В консоли видно, что конструктор A все равно вызывается, то есть бин А создается:

A constructor
2021-04-13 14:49:33.236  INFO 9404 --- [           main] ru.sysout.MainApplication                : Started MainApplication in 0.731 seconds (JVM running for 1.6)

Аннотация @Lazy возле @Autowired

Чтобы изменить такое поведение, нужно поставить @Lazy возле @Autowired. В этом случае в поле A будет внедрен прокси. А сам бин A не будет создан до тех пор, пока реально не потребуется.

Так бин A не создается:

@SpringBootApplication
public class MainApplication {
    private A a;

    @Autowired
    @Lazy
    public MainApplication(A a) {
        this.a = a;
    }

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext applicationContext = SpringApplication.run(MainApplication.class, args);
    }

}

Бин A будет создан только тогда, когда будет вызван его метод или же он будет явно запрошен из контекста.

Запросим его явно из контекста:

@SpringBootApplication
public class MainApplication {
    private A a;

    @Autowired
    @Lazy
    public MainApplication(A a) {
        this.a = a;
    }

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext applicationContext = SpringApplication.run(MainApplication.class, args);
        System.out.println("waiting for a second...");
        Thread.sleep(1000);
        A a = applicationContext.getBean(A.class);
    }
}

В консоли видно, что сначала запускается приложение и поднимается контекст. Затем мы ждем секунду и запрашиваем бин A — в этот момент он и создается:

2021-04-13 14:56:18.411  INFO 12508 --- [           main] ru.sysout.MainApplication                : Started MainApplication in 0.671 seconds (JVM running for 1.392)
waiting for a second...
A constructor

Аналогично, если бы мы не запрашивали A из контекста, а просто вызывали методы A из другого бина, то A создавался бы при вызове его метода. При условии, что A был бы внедрен в этот другой бин с помощью @Lazy @Autowired. 

Итоги

Мы рассмотрели аннотацию @Lazy. Исходный код примера доступен на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *