По умолчанию все бины создаются при запуске 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.