[Spring Boot ์‹œ์ž‘ ๊ณผ์ •] 3. Bean Initialization๊ณผ Event System

์šฐ๋ฆฌ๊ฐ€ ์ž‘์„ฑํ•œ ์ˆ˜๋งŽ์€ ์„œ๋น„์Šค ๊ฐ์ฒด๋“ค์ด ์–ด๋А ์‹œ์ ์— ์ƒ์„ฑ๋˜๊ณ  ์–ด๋–ค ๊ณผ์ •์„ ๊ฑฐ์ณ ์™„์„ฑ๋˜๋ฉฐ ์–ด๋–ป๊ฒŒ ์„œ๋กœ ์†Œํ†ตํ•˜๋Š”์ง€๋ฅผ ์•„๋Š” ๊ฒƒ์€ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‹จ๊ณ„์—์„œ ์ œ์–ดํ•˜๋Š” ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๋Š” ํ•ต์‹ฌ ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” Bean์˜ ์ƒ์„ฑ ํ›„ ์ดˆ๊ธฐ ์ž‘์—…(Bean Initialization)๊ณผ Bean ์ƒ์„ฑ ์‹œ์ ์— ํ‹ˆ์ƒˆ๋ฅผ ๊ณต๋žตํ•˜๋Š” ํ™•์žฅ ๊ธฐ๋ฒ• ๊ทธ๋ฆฌ๊ณ  ๋ถ€ํŠธ์ŠคํŠธ๋ž˜ํ•‘ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ์™€ ์ด๋ฒคํŠธ ์‹œ์Šคํ…œ(Event System)์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•ด ๋ณด๊ณ ์ž ํ•œ๋‹ค.


๋จผ์ € ๋ณด๋ฉด ์ข‹์„ ํฌ์ŠคํŒ…

Spring Boot ์‹œ์ž‘ โ€“ SpringApplication ์ƒ์„ฑ๊ณผ ์ž๋™ ๊ฐ์ง€
Spring Boot โ€“ Application Bootstraping์„ ์œ„ํ•œ 13๋‹จ๊ณ„


Bean Initialization ๊ณผ์ •

BeanPostProcessor์˜ ์—ญํ• 

์•„๋ž˜ ์ฝ”๋“œ๋Š” BeanPostProcessor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. BeanPostProcessor๋Š” Spring Boot ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด ์™ธ์—๋„ Bean ์ƒ์„ฑ ๊ณผ์ •์—์„œ ์‚ฌ์šฉ์ž ์ •์˜ ์ปค์Šคํ…€ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ํ™•์žฅ ํฌ์ธํŠธ ์—ญํ• ์„ ํ•œ๋‹ค.
BeanPostProcessor๋Š” ๊ฐœ๋ณ„ Bean์ด ์ƒ์„ฑ๋  ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ์‹คํ–‰๋œ๋‹ค. BeanPostProcessor์—์„œ ๋ฆฌํ„ดํ•˜๋Š” bean์ด ์ตœ์ข…์ ์œผ๋กœ ApplicationContext์— Bean์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค.

public interface BeanPostProcessor {
    //๋นˆ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ์˜์กด์„ฑ ์ฃผ์ž…์ด ์™„๋ฃŒ๋œ ํ›„ @PostConstruct๋‚˜ afterPropertiesSet์ด ํ˜ธ์ถœ๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰๋œ๋‹ค.
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    //์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ(@PostConstruct๋“ฑ)๊ฐ€ ์‹คํ–‰๋œ ์งํ›„์— ์‹คํ–‰๋œ๋‹ค. ์ฃผ๋กœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ๊ฐ์‹ธ๊ฑฐ๋‚˜(AOP) ์ตœ์ข… ๊ฒ€์ฆ์„ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }
}
Java

Spring Boot โ€“ Application Bootstraping์„ ์œ„ํ•œ 13๋‹จ๊ณ„์—์„œ 10๋‹จ๊ณ„ ApplicationContext Refresh ๊ณผ์ •์—์„œ Spring Boot ๋‚ด๋ถ€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” BeanPostProcessor ๊ตฌํ˜„์ฒด๋“ค์„ ๋“ฑ๋กํ•˜๋Š”๋ฐ ๋Œ€ํ‘œ์ ์ธ ๊ตฌํ˜„์ฒด๋“ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. AutowiredAnnotationBeanPostProcessor
    • ๋ชฉ์ : ์˜์กด์„ฑ ์ฃผ์ž…(DI)์˜ ํ•ต์‹ฌ ์—”์ง„
    • ์—ญํ• : @Autowired, @Value, @Inject ์–ด๋…ธํ…Œ์ด์…˜์„ ๊ฐ์ง€ํ•˜์—ฌ ํ•ด๋‹น ํ•„๋“œ๋‚˜ ๋ฉ”์„œ๋“œ์— ๋นˆ์„ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•œ๋‹ค.
    • ์‹คํ–‰์‹œ์ : Bean ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋œ ์งํ›„ ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ(@PostConstruct๋“ฑ)๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ๋™์ž‘ํ•œ๋‹ค.
  2. CommonAnnotationBeanPostProcessor
    • ๋ชฉ์ : ์ž๋ฐ” ํ‘œ์ค€(JSR-250) ์–ด๋…ธํ…Œ์ด์…˜ ์ง€์›
    • ์—ญํ• : @PostConstruct, @PreDestroy ์–ด๋…ธํ…Œ์ด์…˜์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๋˜ํ•œ @Resource ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•œ ๋ฆฌ์†Œ์Šค ์ฃผ์ž…๋„ ๋‹ด๋‹นํ•œ๋‹ค.
    • ์‹คํ–‰์‹œ์ : ์ดˆ๊ธฐํ™” ์ „ํ›„ ๋‹จ๊ณ„์—์„œ ๊ฐ๊ฐ ์ƒ๋ช…์ฃผ๊ธฐ ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  3. AnnotationAwareAspectJAutoProxyCreator
    • ๋ชฉ์ : AOP ํ”„๋ก์‹œ ์ƒ์„ฑ
    • ์—ญํ• : @Aspect ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ Bean์„ ๋ถ„์„ํ•˜๊ณ  ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋  ๋Œ€์ƒ ๋นˆ๋“ค์„ ์ฐพ์•„ ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ๋ฐ”๊พผ๋‹ค.
    • ์‹คํ–‰์‹œ์ : postProcessAfterInitialization ๋‹จ๊ณ„์—์„œ ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ํ”„๋ก์‹œ๋กœ ๋ž˜ํ•‘ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  4. ApplicationContextAwareProcessor
    • ๋ชฉ์ : ์ธํ”„๋ผ ๊ฐ์ฒด ์ฃผ์ž…
    • ์—ญํ• : Bean์ด ApplicationContextAware, EnvironmentAware, ResourceLoaderAware๋“ฑ์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ๋นˆ์—๊ฒŒ ApplicationContext๋‚˜, Environment ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
  5. ConfigurationClassPostProcessor
    • ์—„๋ฐ€ํžˆ ๋งํ•˜๋ฉด BeanFactoryPostProcessor์ด์ง€๋งŒ ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜์–ด @Configuration, @Bean, @Component, @Import๋ฅผ ์Šค์บ”ํ•˜๊ณ  Bean ์ •์˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋งค์šฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•œ๋‹ค.

InitializingBean๊ณผ @PostConstruct

๋‹ค์Œ์€ Bean ์ดˆ๊ธฐํ™”๋ฅผ ์œ„ํ•œ ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

@Component
public class MyService implements InitializingBean {

    @PostConstruct
    public void postConstruct() {
        // @PostConstruct๊ฐ€ ๋จผ์ € ์‹คํ–‰๋จ
        System.out.println("@PostConstruct executed");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // InitializingBean.afterPropertiesSet()์ด ๋‚˜์ค‘์— ์‹คํ–‰๋จ
        System.out.println("afterPropertiesSet executed");
    }
}
Java

์‹คํ–‰ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ์ƒ์„ฑ์ž ํ˜ธ์ถœ
  2. ์˜์กด์„ฑ ์ฃผ์ž…
  3. BeanPostProcessor.postProcessBeforeInitialization()
  4. @PostConstruct ๋ฉ”์„œ๋“œ ์‹คํ–‰
  5. InitializingBean.afterPropertiesSet() ์‹คํ–‰. (@PostConstruct๊ฐ€ ์šฐ์„  ์‹คํ–‰๊ถŒ์„ ๊ฐ€์ง„๋‹ค)
  6. ์ปค์Šคํ…€ init-method ์‹คํ–‰ (@Bean ์–ด๋…ธํ…Œ์ด์…˜์˜ initMethod ์†์„ฑ์œผ๋กœ ์ง€์ •๋œ ๋ฉ”์„œ๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค)
  7. BeanPostProcessor.postProcessAfterInitialization()

SmartInitializingSingleton

SmartInitializingSingleton์€ ๋ชจ๋“  ์‹ฑ๊ธ€ํ†ค Bean์ด ๋งŒ๋“ค์–ด์ง„ ํ›„์— ๋”ฑ ํ•œ๋ฒˆ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ๋กœ์ง์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
SmartInitializingSingleton์€ 10๋‹จ๊ณ„์˜ ApplicationContext Refresh ๊ณผ์ •์˜ ๋งˆ์ง€๋ง‰ ์‹œ์ ์ธ finishBeanFactoryInitialization() ๋‹จ๊ณ„์—์„œ ๋™์ž‘ํ•œ๋‹ค. ๋งˆ์ง€๋ง‰ ๊ณผ์ •์ธ๋งŒํผ ๋ชจ๋“  Non-Lazy ์‹ฑ๊ธ€ํ†ค Bean์˜ ์ธ์Šคํ„ด์Šคํ™” ๋ฐ ์˜์กด์„ฑ ์ฃผ์ž…, ์ดˆ๊ธฐํ™” ์ฝœ๋ฐฑ(@PostConstruct)์ด ์™„์ „ํžˆ ๋๋‚œ ํ›„์— ์‹คํ–‰๋œ๋‹ค.

@PostConstruct ์‹œ์ ์—๋Š” ์•„์ง ๋‹ค๋ฅธ Bean๋“ค์ด ์ƒ์„ฑ ์ค‘์ผ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ๋นˆ์„ ๋‹ค ๋’ค์ ธ์„œ ํŠน์ • ์ž‘์—…์„ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด @PostConstruct๋Š” ์œ„ํ—˜ํ•˜๋‹ค. ์•„์ง ์ƒ์„ฑ๋˜์ง€ ์•Š์€ Bean์ด ์žˆ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
์•„๋ž˜ ์ฝ”๋“œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ์งํ›„ ๋“ฑ๋ก๋œ ๋ชจ๋“  Controller๋ฅผ ์ฐพ์•„ ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ƒ˜ํ”Œ ์ฝ”๋“œ๋‹ค.

@Component
public class RouteConfigurationLogger implements SmartInitializingSingleton {

    private final ListableBeanFactory beanFactory;

    public RouteConfigurationLogger(ListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public void afterSingletonsInstantiated() {
        // ๋ชจ๋“  ๋นˆ์ด ์ด๋ฏธ ๋‹ค ์ƒ์„ฑ๋˜์—ˆ์œผ๋ฏ€๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
        Map<String, Object> controllers = beanFactory.getBeansWithAnnotation(RestController.class);
        
        System.out.println(">>> [์ตœ์ข… ํ™•์ธ] ํ˜„์žฌ ๋“ฑ๋ก๋œ ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ ์ˆ˜: " + controllers.size());
        controllers.forEach((name, bean) -> {
            // ์ƒ์„ธ ๋กœ์ง ์ฒ˜๋ฆฌ...
        });
    }
}
Java

Spring Boot Event System

Spring Boot๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜์—ฌ ํ™•์žฅ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค.

@EventListener ๋™์ž‘ ์›๋ฆฌ

Spring์˜ ์ด๋ฒคํŠธ ์‹œ์Šคํ…œ์€ Observer ํŒจํ„ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ @EventListener ์–ด๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

@EventListener ๋“ฑ๋ก ๊ณผ์ •

  1. Bean ์ •์˜ ๋กœ๋“œ & ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    • ApplicationContext๋ฅผ ์ดˆ๊ธฐํ™” ํ•˜๋ฉด์„œ ๋ชจ๋“  @Component, @Service, @Configuration ๋“ฑ์˜ ๋นˆ์„ ์ƒ์„ฑ ํ•œ๋‹ค.
    • ์ด ์‹œ์ ์—๋Š” @EventListener๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋Š” ์•„์ง ๋ฆฌ์Šค๋„ˆ๋กœ ๋“ฑ๋ก๋˜์ง€ ์•Š์€ ์ƒํƒœ๋‹ค.
  2. EventListenerMethodProcessor Bean ํ›„์ฒ˜๋ฆฌ๊ธฐ ์‹คํ–‰ (10๋‹จ๊ณ„ ApplicationContext Refresh)
    • Spring Boot ์ž๋™ ์„ค์ •์— ์˜ํ•ด์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค.
    • ๋ชจ๋“  Bean์„ ์ˆœํšŒํ•˜๋ฉฐ @EventListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์Šค์บ”ํ•œ๋‹ค.
    • ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ApplicationListener๋ฅผ ๊ตฌํ˜„ํ•œ ApplicationListenerMethodAdapter ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. (ApplicationListenerMethodAdapter๊ฐ€ ๋ฆฌ์Šค๋„ˆ ์—ญํ• ์„ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.)
    • ApplicationEventMulticaster์— ApplicationListenerMethodAdapter๋ฅผ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋กœ ์ถ”๊ฐ€ ๋“ฑ๋กํ•œ๋‹ค.
  3. ApplicationListenerMethodAdapter ์ด๋ฒคํŠธ ์ˆ˜์‹ 
    • ApplicationListenerMethodAdapter๊ฐ€ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  Event๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ApplicationEventMulticaster๊ฐ€ ApplicationListenerMethodAdapter์—๊ฒŒ ์ด๋ฒคํŠธ ๋ฐœํ–‰ ์†Œ์‹์„ ์ „๋‹ฌํ•œ๋‹ค.
    • ApplicationListenerMethodAdapter๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด @EventListener ๋ฉ”์„œ๋“œ๋ฅผ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค.

์•„๋ž˜ ์ฝ”๋“œ๋Š” EventListenerMethodProcessor ํด๋ž˜์Šค๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

// ๊ฐœ๋ณ„ Bean์ด ์ƒ์„ฑ๋  ๋•Œ๋งˆ๋‹ค BeanPostProcessor๊ฐ€ ๋™์ž‘
public class EventListenerMethodProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // Bean์—์„œ @EventListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ ์Šค์บ”
        // ์ฆ‰ @EventListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ํด๋ž˜์Šค๋Š” Bean ์ด์–ด์•ผ ํ•จ.
        // Bean์ด ์•„๋‹ˆ๋ฉด @EventListener๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์Œ.
        Method[] methods = bean.getClass().getDeclaredMethods();

        for (Method method : methods) {
            if (method.isAnnotationPresent(EventListener.class)) {
                // ApplicationEventMulticaster์— ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
                registerEventListener(bean, method);
            }
        }
        return bean;
    }
}
Java

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์ƒ์„ฑ๋œ Bean์—์„œ @EventListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์•„ registerEventListener๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

@EventListener ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๋ฉ”์ปค๋‹ˆ์ฆ˜

Spring Boot๋Š” @EventListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ApplicationListener ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. (ApplicationListenerMethodAdapter) ํ•ต์‹ฌ์€ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ Event ํƒ€์ž…์„ ๋ถ„์„ํ•ด์„œ ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋งŒ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ฆ‰ @EventListener ๋ฉ”์„œ๋“œ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์™€ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๊ฐ€ ๋™์ผํ•œ Event์ธ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์•„์„œ ํ˜ธ์ถœ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

  1. ApplicationEventMulticaster์—์„œ ๋ฐœํ–‰๋œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฆฌ์Šค๋„ˆ ์ฐพ๊ธฐ
    • Event๊ฐ€ ๋ฐœํ–‰๋˜๋ฉด ApplicationEventMulticaster๋Š” ๋“ฑ๋ก๋œ ๋ชจ๋“  ๋ฆฌ์Šค๋„ˆ์˜ ๋ช…๋‹จ์„ ํ›‘๋Š”๋‹ค.
    • ์ด ๋•Œ ๋ฆฌ์Šค๋„ˆ์˜ supportsEventType ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๋‹น ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” ์ด๋ฒคํŠธ ํƒ€์ž…์„ ์–ป์–ด์„œ ๋ฐœํ–‰๋œ ์ด๋ฒคํŠธ ํƒ€์ž…๊ณผ ๋น„๊ตํ•˜์—ฌ ํƒ€์ž…์ด ๋งž๋Š” ๋ฆฌ์Šค๋„ˆ(ApplicationListenerMethodAdapter)์˜ onApplicationEvent ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  2. ApplicationListenerMethodAdapter์˜ 2์ฐจ ๊ฒ€์ฆ(shouldHandle)
    • Payload ์ฒดํฌ: PayloadApplicationEvent์ธ ๊ฒฝ์šฐ ์‹ค์ œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ๋งž๋Š”์ง€ ๋‹ค์‹œ ํ™•์ธํ•œ๋‹ค.
    • Condition ์ฒดํฌ: @EventListener(condition = “#event.success == true”) ์ฒ˜๋Ÿผ ์กฐ๊ฑด์‹์ด ๋ถ™์–ด ์žˆ๋‹ค๋ฉด ์ด ์กฐ๊ฑด๊นŒ์ง€ ๋งŒ์กฑํ•ด์•ผ @EventListener ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

์•„๋ž˜ ApplicationListenerMethodAdapter ํด๋ž˜์Šค ์ฝ”๋“œ์˜ ์ฃผ์„์„ ์ฐธ๊ณ ํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.

// Spring์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ApplicationListener ๋ž˜ํผ
public class ApplicationListenerMethodAdapter implements ApplicationListener<ApplicationEvent> {

    private final Object targetBean;
    private final Method targetMethod;
    private final Class<?> eventType;  // ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ ์ถ”์ถœํ•œ ์ด๋ฒคํŠธ ํƒ€์ž…

    // @EventListener ๋ฉ”์„œ๋“œ๊ฐ€ ํฌํ•จ๋œ Bean๊ณผ method ์ •๋ณด๋ฅผ ์„ค์ •ํ•œ๋‹ค.
    public ApplicationListenerMethodAdapter(Object bean, Method method) {
        this.targetBean = bean;
        this.targetMethod = method;
        // ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ ์ด๋ฒคํŠธ ํƒ€์ž… ์ถ”์ถœ
        this.eventType = method.getParameterTypes()[0];
    }
    
    ...
    ...

    // ApplicationEventMulticaster์˜ 1์ฐจ ํ•„ํ„ฐ(๋ฐœํ–‰๋œ Event ํƒ€์ž…๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ Event ํƒ€์ž…์ด ๋งž๋Š”..) ํ†ต๊ณผ์‹œ ํ˜ธ์ถœ
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
		    if (isDefaultExecution()) {
            processEvent(event);
        }
    }
    
    ...
    ...
    
    public void processEvent(ApplicationEvent event) {
        // ๋ฐœํ–‰๋œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์ธ์ž ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
    		Object[] args = resolveArguments(event);
    		// @EventListener(condition="...") ๋“ฑ์˜ ์กฐ๊ฑด์ด ์žˆ๋‹ค๋ฉด ์ฒดํฌ
    		// ์—ฌ๊ธฐ์„œ 2์ฐจ ํ•„ํ„ฐ ๋™์ž‘
		    if (shouldHandle(event, args)) {
		        // ์‹ค์ œ Bean์˜ @EventListener ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
            Object result = doInvoke(args);
            if (result != null) {
                // ๋ฉ”์„œ๋“œ ๋ฐ˜ํ™˜๊ฐ’์ด ์žˆ๋‹ค๋ฉด ๊ทธ ๋ฐ˜ํ™˜๊ฐ’์„ ๋‹ค์‹œ ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๋กœ ๋ฐœํ–‰ (Chaning)
                handleResult(result);
            }
            else {
                logger.trace("No result object given - no result to handle");
            }
        }
    }
    
    ...
    ...
    
    @Nullable
    protected Object[] resolveArguments(ApplicationEvent event) {
        // ํ˜„์žฌ ๋ฐœํ–‰๋œ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ •์˜๋œ ์ด๋ฒคํŠธ ํƒ€์ž…์„ ๊ฐ€์ ธ์˜จ๋‹ค.
        ResolvableType declaredEventType = getResolvableType(event);
    		if (declaredEventType == null) {
            return null;
        }
        // @EventListener ๋ฉ”์„œ๋“œ์— ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด ์ธ์ž๋ฅผ ๋„˜๊ธธ ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋นˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜
        if (this.method.getParameterCount() == 0) {
            return new Object[0];
        }
        // ๋ฉ”์„œ๋“œ์— ์„ ์–ธ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ Class ํƒ€์ž…์„ ๊ฐ€์ ธ์˜ด
        Class<?> declaredEventClass = declaredEventType.toClass();
        // ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ApplicationEvent๋ฅผ ์ƒ์†๋ฐ›์ง€ ์•Š์•˜๊ณ  (์ฆ‰, ์ผ๋ฐ˜ DTO๋‚˜ String๊ณผ ๊ฐ™์€.)
        // ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ PayloadApplicationEvent(๋‚ด์šฉ์„ ๋‹ด๊ณ  ์žˆ๋Š” ์ด๋ฒคํŠธ)๋ผ๋ฉด
        if (!ApplicationEvent.class.isAssignableFrom(declaredEventClass) &&
            event instanceof PayloadApplicationEvent<?> payloadEvent) {
            // ๋ฐœํ–‰๋œ ์ด๋ฒคํŠธ์—์„œ ์‹ค์ œ ๋ฐ์ดํ„ฐ(payload)๋ฅผ ๊บผ๋‚ธ๋‹ค.
            Object payload = payloadEvent.getPayload();
            // ๊บผ๋‚ธ payload ํƒ€์ž…์ด ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
            if (declaredEventClass.isInstance(payload)) {
                // ์ผ์น˜ํ•œ๋‹ค๋ฉด ์ด๋ฒคํŠธ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ๋ฐ์ดํ„ฐ(payload)๋ฅผ ์ธ์ž๋กœ ๊ฒฐ์ •ํ•œ๋‹ค.
                return new Object[] {payload};
            }
        }
        return new Object[] {event};
    }
}
Java

๋‹ค์Œ์€ @EventListener๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒ˜ํ”Œ์ฝ”๋“œ๋‹ค. ์ฃผ์„์„ ์ฐธ๊ณ ํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.

// @EventListener ๋ฉ”์„œ๋“œ - ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์œผ๋กœ ์ด๋ฒคํŠธ ๋งคํ•‘
@Component
public class MyEventListener {

 // ApplicationStartingEvent๊ฐ€ ๋ฐœํ–‰๋˜๋ฉด
 // ApplicationEventMulticaster --> handleStarting ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌ์šฉ ApplicationListenerMethodAdapter ํ˜ธ์ถœ
 // ApplicationListernerMethodAdapter์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ AplicationStartingEvent ๊ฐ์ฒด์™€ ํ•จ๊ป˜ handleStarting์„ ํ˜ธ์ถœ
    @EventListener
    public void handleStarting(ApplicationStartingEvent event) {
        log.info("Application is starting...");
    }

// ApplicationReadyEvent๊ฐ€ ๋ฐœํ–‰๋˜๋ฉด
// ApplicationEventMulticaster --> handleReady ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌ์šฉ ApplicationListenerMethodAdapter ํ˜ธ์ถœ
// ApplicationListernerMethodAdapter์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ApplicationReadyEvent ๊ฐ์ฒด์™€ ํ•จ๊ป˜ handleReady๋ฅผ ํ˜ธ์ถœ
    @EventListener
    public void handleReady(ApplicationReadyEvent event) {
        log.info("Application is ready...");
    }

// ApplicationEvent๊ฐ€ ๋ฐœํ–‰๋˜๋ฉด
// ApplicationEventMulticaster --> handleAnyEvent ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌ์šฉ ApplicationListenerMethodAdapter ํ˜ธ์ถœ
// ApplicationListernerMethodAdapter์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ApplicationEvent ๊ฐ์ฒด์™€ ํ•จ๊ป˜ handleAnyEvent๋ฅผ ํ˜ธ์ถœ
// ApplicationEvent๋Š” ๋ชจ๋“  ApplicationEvent ํƒ€์ž…์˜ ์ถ”์ƒ ํด๋ž˜์Šค์ด๋ฏ€๋กœ ๋ชจ๋“  ApplicationEvent ํƒ€์ž…์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ๋˜๋ฉด ํ˜ธ์ถœ
    @EventListener
    public void handleAnyEvent(ApplicationEvent event) {
        log.info("Any application event occurred: {}", event.getClass().getSimpleName());
    }
    
// Payload ๊ธฐ๋ฐ˜ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ (POJO ๋ฐฉ์‹)
// ApplicationEvent๋ฅผ ์ƒ์†๋ฐ›์ง€ ์•Š์€ ์ˆœ์ˆ˜ ๊ฐ์ฒด(String, DTO ๋“ฑ)๋„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•˜๋‹ค.
// String ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœํ–‰๋˜๋ฉด
// ๋‚ด๋ถ€์ ์œผ๋กœ ApplicationListenerMethodAdapter์˜ resolveArguments๊ฐ€ payload๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. (String ํƒ€์ž…)
// ApplicationListernerMethodAdapter์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ String ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ handleStringPayload ํ˜ธ์ถœ.
    @EventListener
    public void handleStringPayload(String message) {
        log.info("๋ฌธ์ž์—ด ํŽ˜์ด๋กœ๋“œ ์ด๋ฒคํŠธ ์ˆ˜์‹ : {}", message);
    }
    
// PayloadApplicationEvent ์ž์ฒด๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ๊ฒฝ์šฐ
// ํŽ˜์ด๋กœ๋“œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ด๋ฒคํŠธ์˜ ๋ถ€๊ฐ€ ์ •๋ณด(ํƒ€์ž„์Šคํƒฌํ”„ ๋“ฑ)๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    @EventListener
    public void handlePayloadEvent(PayloadApplicationEvent<MyCustomDto> event) {
        MyCustomDto dto = event.getPayload();
        log.info("PayloadApplicationEvent ๋ž˜ํผ๋ฅผ ํ†ตํ•œ ์ˆ˜์‹ : {}, ๋ฐœํ–‰์‹œ๊ฐ„: {}", 
                 dto.getName(), event.getTimestamp());
    }
}
Java

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ถ€ํŠธ์ŠคํŠธ๋ž˜ํ•‘ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ ์ˆœ์„œ

  1. ApplicationStartingEvent: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์งํ›„ (1๋‹จ๊ณ„)
  2. ApplicationEnvironmentPreparedEvent: Environment ์ค€๋น„ ์™„๋ฃŒ (6๋‹จ๊ณ„)
  3. ApplicationContextInitializedEvent: ApplicationContext ์ดˆ๊ธฐํ™” ์™„๋ฃŒ (9๋‹จ๊ณ„)
  4. ApplicationContextPreparedEvent: ApplicationContext ์ค€๋น„, Bean ์ •์˜ ๋กœ๋“œ (Bean ์ƒ์„ฑ ์•„๋‹˜) (9๋‹จ๊ณ„)
  5. ContextRefreshedEvent: ApplicationContext Refresh ์™„๋ฃŒ (10๋‹จ๊ณ„)
  6. ApplicationStartedEvent: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์™„๋ฃŒ – ApplicationContext๊ฐ€ ์™„์ „ํžˆ ๋กœ๋“œ๋œ ์งํ›„
  7. AvailabilityChangeEvent: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ€์šฉ์„ฑ ์ƒํƒœ ๋ณ€๊ฒฝ (12๋‹จ๊ณ„)
  8. ApplicationReadyEvent: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ค€๋น„ ์™„๋ฃŒ (13๋‹จ๊ณ„)

ApplicationStartingEvent ๋ถ€ํ„ฐ ApplicationReadyEvent ๊นŒ์ง€

@EventListener ๋ฉ”์„œ๋“œ์˜ ๊ฐ ApplicationEvent๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ง€์ •ํ•˜์—ฌ ํ•ด๋‹น ์ด๋ฒคํŠธ ๋ฐœ์ƒ์‹œ ์›ํ•˜๋Š” ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์ƒ˜ํ”Œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๊ตฌํ˜„ ์˜ˆ์‹œ
@Component
@Slf4j
public class StartupEventListener {

    //์•„๋ž˜ ์ด๋ฒคํŠธ๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
    //@Component๋กœ Bean์„ ๋“ฑ๋กํ•˜๋Š”๋ฐ Bean์ด ์ƒ์„ฑ๋˜๋Š” 10๋‹จ๊ณ„ (ApplicationContext Refresh)
    //๊ณผ์ •์—์„œ ์ด Bean์ด ์ƒ์„ฑ๋˜๋Š”๋ฐ ApplicationStartingEvent๋Š” ์ด Bean์ด ์ƒ์„ฑ๋˜๊ธฐ
    //์ด์ „์— ๋ฐœํ–‰๋˜๋Š” ์ด๋ฒคํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
    @EventListener
    public void handleApplicationStarting(ApplicationStartingEvent event) {
        // ๊ฐ€์žฅ ๋จผ์ € ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ
        // ๋กœ๊น… ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”, ๊ธฐ๋ณธ ์„ค์ • ๋“ฑ
        log.info("Application starting...");
    }

    //์•„๋ž˜ ์ด๋ฒคํŠธ ๋ฉ”์„œ๋“œ๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
    //@Component๋กœ Bean์„ ๋“ฑ๋กํ•˜๋Š”๋ฐ Bean์ด ์ƒ์„ฑ๋˜๋Š” 10๋‹จ๊ณ„ (ApplicationContext Refresh)
    //๊ณผ์ •์—์„œ ์ด Bean์ด ์ƒ์„ฑ๋˜๋Š”๋ฐ ApplicationEnvironmentPreparedEvent๋Š” ์ด Bean์ด ์ƒ์„ฑ๋˜๊ธฐ
    //์ด์ „์— ๋ฐœํ–‰๋˜๋Š” ์ด๋ฒคํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
    @EventListener
    public void handleEnvironmentPrepared(ApplicationEnvironmentPreparedEvent event) {
        // Environment๊ฐ€ ์ค€๋น„๋˜์–ด ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ ๊ฐ€๋Šฅ
        ConfigurableEnvironment env = event.getEnvironment();
        String profile = String.join(",", env.getActiveProfiles());
        log.info("Active profiles: {}", profile);
    }

    // ์•„๋ž˜ ์ด๋ฒคํŠธ๋Š” ์ด Bean์ด ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ์ด๋ฏ€๋กœ ํ˜ธ์ถœ๋œ๋‹ค.
    @EventListener
    public void handleApplicationReady(ApplicationReadyEvent event) {
        // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์™„์ „ํžˆ ์ค€๋น„๋จ
        // ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์—ฐ๊ฒฐ, ํ—ฌ์Šค ์ฒดํฌ ๋“ฑ๋ก ๋“ฑ
        log.info("Application ready to serve requests");
    }
}
Java

์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์‹คํ–‰ ์ˆœ์„œ ์ œ์–ด

์—ฌ๋Ÿฌ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๊ฐ™์€ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

@Component
public class OrderedEventListener {

    @EventListener
    @Order(Ordered.HIGHEST_PRECEDENCE)  // ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„
    public void handleFirst(ApplicationReadyEvent event) {
        log.info("First handler executed");
    }

    @EventListener
    @Order(Ordered.LOWEST_PRECEDENCE)   // ๊ฐ€์žฅ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„
    public void handleLast(ApplicationReadyEvent event) {
        log.info("Last handler executed");
    }
}
Java

์šฐ์„  ์ˆœ์œ„์— ๋”ฐ๋ผ handleFirst -> handleLast ์ˆœ์„œ๋กœ ์‹คํ–‰ ๋œ๋‹ค.

์กฐ๊ฑด๋ถ€ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ

SpEL(Spring Expression Language)์„ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ๊ฑด๋ถ€๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
์กฐ๊ฑด๋ถ€ ์ฒดํฌ๋Š” ApplicationListenerMethodAdapter์˜ shouldHandle ๋‚ด์—์„œ ์ฒดํฌ๋ฅผ ํ•œ๋‹ค๊ณ  ํ–ˆ๋‹ค.

@Component
public class ConditionalEventListener {

    /*
        activeProfiles์— 'dev'๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ์—๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
    */
    @EventListener(condition = "#event.environment.activeProfiles.contains('dev')")
    public void handleDevOnly(ApplicationEnvironmentPreparedEvent event) {
        log.info("This only runs in dev profile");
    }

    /*
        ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘์— 5์ดˆ(5000ms) ์ด์ƒ ์†Œ์š”๋œ ๊ฒฝ์šฐ์—๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
    */
    @EventListener(condition = "#event.timeTaken.toMillis() > 5000")
    public void handleSlowStartup(ApplicationStartedEvent event) {
        log.warn("Application took more than 5 seconds to start: {} ms",
                event.getTimeTaken().toMillis());
    }
}
Java
  • event.environment.activeProfiles
    • ApplicationEnvironmentPreparedEvent ๊ฐ์ฒด์— ์ „๋‹ฌ๋œ environment(Environment ๊ฐ์ฒด)๋ฅผ ๋ฐ”๋กœ access ํ•œ๋‹ค.
    • Environment ๊ฐ์ฒด์— ์„ค์ •๋œ ํ™œ์„ฑํ™”๋œ ํ”„๋กœํŒŒ์ผ(active profiles) ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

์ปค์Šคํ…€ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๊ตฌํ˜„ ํŒจํ„ด

์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๋ฆฌ์Šค๋„ˆ

์•ž์—์„œ ์‚ดํŽด๋ดค๋˜ @EventListener ์–ด๋…ธํ…Œ์ด์…˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Component
public class CustomEventListener {

    //Application Bootstraping์ด ๋๋‚œ ํ›„ ApplicationReadyEvent๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
    @EventListener
    @Async
    public void handleAsyncEvent(ApplicationReadyEvent event) {
        // ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
    }

    //Application์ด ์‹œ์ž‘๋œ ์งํ›„ ApplicationStartedEvent๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
    @EventListener
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public void handleHighPriorityEvent(ApplicationStartedEvent event) {
        // ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์ฒ˜๋ฆฌ
    }
}
Java
ApplicationListener ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„

Spring Boot โ€“ Application Bootstraping์„ ์œ„ํ•œ 13๋‹จ๊ณ„ ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ ์ฒ˜๋ฆฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

@Component
public class TypedEventListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // ํƒ€์ž… ์•ˆ์ „ํ•œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
        log.info("Application ready at: {}", event.getTimeTaken());
    }
}
Java

์‚ฌ์šฉ์ž ์ •์˜ ์ด๋ฒคํŠธ ์‚ฌ์šฉํ•˜๊ธฐ

Application Bootstraping ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์™ธ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ํ›„ ์ง„ํ–‰๋˜๋Š” ๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ •์˜ํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฒคํŠธ ํด๋ž˜์Šค ์ •์˜ (TaskCompletionEvent)

@Getter
public class TaskCompletionEvent {
    private final Long taskId;
    private final String userEmail;

    public TaskCompletionEvent( Long taskId, String userEmail ) {
        this.taskId = taskId;
        this.userEmail = userEmail;
    }
}
Java

Task ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ ๋ฐœํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ •์˜ํ•œ๋‹ค.

์ด๋ฒคํŠธ ๋ฐœํ–‰ (TaskService)

Task ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ›„ ์™„๋ฃŒ๋˜๋ฉด ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค.

@Service
public class TaskService {
    // publisher์—๋Š” ๋ณดํ†ต ApplicationContext ๊ตฌํ˜„์ฒด๊ฐ€ ์ฃผ์ž…๋ฉ๋‹ˆ๋‹ค.
    // ApplicationContext ์ธํ„ฐํŽ˜์ด์Šค๋Š” ApplicationEventPublisher๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
    private final ApplicationEventPublisher publisher;

    public TaskService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void goTask(Long orderId, String email) {
        // task ์ฒ˜๋ฆฌ ๋กœ์ง
        // task ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ ๋˜๋ฉด TaskCompletionEvent ๋ฐœํ–‰
        publisher.publishEvent(new TaskCompletionEvent(orderId, email));
    }
}
Java

publisher.publishEvent ํ˜ธ์ถœ๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฒ˜๋ฆฌ๊ฐ€ ์ผ์–ด๋‚œ๋‹ค.

  • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœํ–‰๋˜๋ฉด ApplicationEventMulticaster๊ฐ€ ๋“ฑ๋ก๋œ ApplicationListener ๋ชฉ๋ก์—์„œ ์ด๋ฒคํŠธ ํƒ€์ž…(์—ฌ๊ธฐ์„œ๋Š” TaskCompletionEvent)์— ๋งž๋Š” ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ฐพ๋Š”๋‹ค. @EventListener๋กœ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ •์˜ํ–ˆ๋‹ค๋ฉด ApplicationListenerMethodAdapter ํƒ€์ž…์˜ ๋ฆฌ์Šค๋„ˆ์ผ ๊ฒƒ์ด๋‹ค.
  • ๋งค์นญ๋œ ๋ฆฌ์Šค๋„ˆ(ApplicationListenerMethodAdapter) ํ˜ธ์ถœ์„ ํ†ตํ•ด์„œ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ TaskCompletionEvent๋ฅผ ์ „๋‹ฌํ•˜๋ฉด์„œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ (TaskCompletionEventListener)

@Component
public class TaskEventListener {

    // TaskCompletionEvent ๋ฐœ์ƒ์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
    @EventListener
    public void onTaskCompleted(TaskCompletionEvent event) {
        //task ์™„๋ฃŒ ํ›„ ์ฒ˜๋ฆฌ
        // ๋™๊ธฐ ์ฒ˜๋ฆฌ (๊ธฐ๋ณธ)
        // ์˜ˆ: ์ด๋ฉ”์ผ ํ ์ ์žฌ, ๊ฐ์‚ฌ ๋กœ๊ทธ ๊ธฐ๋ก ๋“ฑ
    }
}
Java

@EventListener ๋ฉ”์„œ๋“œ์—์„œ ์ด๋ฒคํŠธ ๋ฐœํ–‰์„ ํ†ตํ•ด์„œ ์ „๋‹ฌ๋œ TaskCompletionEvent๋ฅผ ์ „๋‹ฌ ๋ฐ›์•„ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค.


์ง€๊ธˆ๊นŒ์ง€ Bean ์ƒ์„ฑ์‹œ ์ดˆ๊ธฐํ™”์™€ Spring Boot Event System์„ ํ†ตํ•ด์„œ @EventListener ์–ด๋…ธํ…Œ์ด์…˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์–ด๋–ค ํ๋ฆ„์„ ํ†ตํ•ด์„œ ํ˜ธ์ถœ์ด ๋˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์ •๋ฆฌํ•ด ๋ณด์•˜๋‹ค.
Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ธฐ๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹จ ํ•œ์ค„์˜ ์ฝ”๋“œ๋ฉด ๋˜์ง€๋งŒ ๊ทธ ํ•œ์ค„์˜ ์ฝ”๋“œ์•ˆ์—๋Š” ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€ ์ผ๋“ค์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค. Spring Boot Application์ด ์–ด๋–ค Bootstrap ๊ณผ์ •์„ ๊ฑฐ์น˜๋Š”์ง€๋ฅผ ์•Œ๊ณ  ์žˆ์œผ๋ฉด ๊ธฐ๋™ ์‹œ์ ์— ์–ด๋–ค ํŠน์ • ์ž‘์—…์„ ํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜ ์ผ๋ฐ˜์ ์ด์ง€ ์•Š์€ ์„ค์ • ์ฒ˜๋ฆฌ ์ž‘์—…์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.


๋จผ์ € ๋ณด๋ฉด ์ข‹์€ ํฌ์ŠคํŒ…

Spring Boot ์‹œ์ž‘ โ€“ SpringApplication ์ƒ์„ฑ๊ณผ ์ž๋™ ๊ฐ์ง€
Spring Boot โ€“ Application Bootstraping์„ ์œ„ํ•œ 13๋‹จ๊ณ„