framework/spring

4. SpringBootApplication 설정, @Import, Profile, @EventListener

wooweee 2023. 9. 24. 14:26
728x90

1. bootApplication 설정

  • configFile : 수동 bean 등록 file

  • testDataInit : 초기 test file
    • @EventListener(ApplicationReadyEvent.class)
    • @PostConstruct

  • bootApplication
    • @Import
    • @SpringBootApplication(scanBasePackages = "")
    • app 내부 @Bean, @Profile("")

  • application.properties
    • main : local
    • test  : test

 

  • 해당 파일 code
package hello.itemservice.config;

@Configuration
public class MemoryConfig {
    @Bean
    public ItemService itemService() { return new ItemServiceV1(itemRepository()); } // bean 등록 + 의존관계 주입
    @Bean
    public ItemRepository itemRepository() { return new MemoryItemRepository(); } // bean 등록
}
package hello.itemservice;

@Slf4j
@RequiredArgsConstructor
public class TestDataInit {
    private final ItemRepository itemRepository;
    /**
     * 확인용 초기 데이터 추가
     */
    @EventListener(ApplicationReadyEvent.class)
    public void initData() {
        log.info("test data init");
        itemRepository.save(new Item("itemA", 10000, 10));
        itemRepository.save(new Item("itemB", 20000, 20));
    }
}
package hello.itemservice;

// 현재 componentScan의 영역이 web 이하이므로 @Import로 수동 bean 등록 내용을 받아야 springContainer가 받아와야하는 bean을 받아올 수 있다.
@Import(MemoryConfig.class)
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")
public class ItemServiceApplication {
   public static void main(String[] args) {
      SpringApplication.run(ItemServiceApplication.class, args);  //web 내부 @Component만 bean으로 등록한다는 의미
   }
   @Bean // 수동 bean 등록 방법 중 직접 등록 방법
   @Profile("local") // application.properties의 설정 중 local 환경일 경우 @Bean으로 등록
   public TestDataInit testDataInit(ItemRepository itemRepository) { // 의존관계 주입도 들어감 - ItemRepository
      return new TestDataInit(itemRepository);
   }
}
# main/resources/application.properties
# 해당 설정이 없을 경우, profiles는 default로 지정된다.
spring.profiles.active=local
# test/resources/application.properties

# test package에 지정한 profiles로 boot test 실행시, 해당 profiles가 우선시 된다.
#설정이 없으면 main profiles를 따른다.

spring.profiles.active=test

 

 

1.1. ConfigFile

package hello.itemservice.config;

@Configuration
public class MemoryConfig {
    @Bean
    public ItemService itemService() { return new ItemServiceV1(itemRepository()); } // bean 등록 + 의존관계 주입
    @Bean
    public ItemRepository itemRepository() { return new MemoryItemRepository(); } // bean 등록
}

 

  • @springBootApplication의 componentScan 범위가 Controller pacakge로 한정이 되어있어서 수동 config파일을 통해서 @Import @Bean 등록을 수행

 

 

1.2. TestDataInit

package hello.itemservice;

@Slf4j
@RequiredArgsConstructor
public class TestDataInit {
    private final ItemRepository itemRepository;
    
    @EventListener(ApplicationReadyEvent.class)
    public void initData() {
        log.info("test data init");
        itemRepository.save(new Item("itemA", 10000, 10));
        itemRepository.save(new Item("itemB", 20000, 20));
    }
}

 

  • @EventListener(ApplicationReadyEvent.class)
    • AOP를 포함한 spring container가 완전히 초기화를 다 끝내고, 실행 준비가 됬을 경우 해당 어노테이션이 붙은 메서드가 호출
    • AOP설정 초기화는 @Transactional 읽고 proxy 객체 만드는 과정을 의미

  • @PostConstruct
    • @EventListener(ApplicationReadyEvent.class)와 유사한 기능이지만, AOP 같은 부분이 아직 다 처리되지 않은 시점에 호출될 수 있기 때문에, 간혹 문제가 발생

 

1.3. SpringBootApplication

package hello.itemservice;

// 현재 componentScan의 영역이 web 이하이므로 @Import로 수동 bean 등록 내용을 받아야 springContainer가 받아와야하는 bean을 받아올 수 있다.
@Import(MemoryConfig.class)
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")
public class ItemServiceApplication {
   public static void main(String[] args) {
      SpringApplication.run(ItemServiceApplication.class, args);  //web 내부 @Component만 bean으로 등록한다는 의미
   }
   @Bean // 수동 bean 등록 방법 중 직접 등록 방법
   @Profile("local") // application.properties의 설정 중 local 환경일 경우 @Bean으로 등록
   public TestDataInit testDataInit(ItemRepository itemRepository) { // 의존관계 주입도 들어감 - ItemRepository
      return new TestDataInit(itemRepository);
   }
}

 

  • @SpringBootApplication(scanBasePackages = "hello.itemservice.web")
    • scanBasePackages : 해당 pacakge부터 componentScan을 해서 bean을 등록하겠다는 의미

  • @Import("CLASS")
    • 해당 class를 설정파일로 사용한다는 뜻 
    • @Configuration, @Bean으로 수동 주입하는 방법과 동일한 역할
    • @Import하면 해당 class를 빈으로 등록해주어서 수동 주입 할 필요없이 깔끔하게 처리된다.

  • @Bean
    • Application 에 직접적으로 @bean 등록하는 직접 수동 방법
    • @Configuration 없이 (@SpringBootApplication 내부에 있으므로) 자동으로 springContainer에 bean 등록 됨

    • bean 등록 방법 3가지
      1. 자동 등록 - @ComponentScan - @Controller, @Service, @Repository - @Autowired
      2. 수동 등록 (Config file 이용) - @Configuration @Bean
      3. 수동 등록 (boot file에 직접 등록) - @Bean
  • @Profile
    • spring은 로딩 시점에 application.properties spring.profiles.active 을 읽어서 프로필로 사용
    • @Profile("local")이 설정된 method나 설정, class는 application.properties가 local인 경우 작동

2. Profile

# main/resources/application.properties
# 해당 설정이 없을 경우, profiles는 default로 지정된다.
spring.profiles.active=local
# test/resources/application.properties

# test package에 지정한 profiles로 boot test 실행시, 해당 profiles가 우선시 된다.
#설정이 없으면 main profiles를 따른다.

spring.profiles.active=test

 

  • Profile
    • application.properties의 spring.profiles.active = "" 로 profile 지정이 가능
    • 따로 지정을 하지 않을 시, profile은 (default)로 설정
    • 관습적 분류 : dev, local, prod, test(test패키지에서 사용)
  • profile별 사용 방식
    1. @profile
    2. application-{profile}.properties || application-{profile}.yml

  • 동작 방식
    • 공통설정은 application.properties 에서 수행

    • 이후 profilte 지정 시,
      1) 해당 application-{profile}.yml 설정 내용 수행
      2) @Profile("{profile}")에 부합한 method 수행

    • 단, 중복 코드 발생시, 우선권은 application.properties에서 지정한 profile 파일의 설정이 우선권을 가진다.
      = profile설정으로 code를 덮어씌운다.

  • test package의 경우
    • test package 내부에 resources/application.properties를 생성 가능

    • test package에 application.properties 파일 자체가 없는 경우에는 main/resources/application.properties를 따름
    • test package에 applcation.properties가 생기는 순간 부터, test file은 application.properties에 profiles의 존재 유무에 관계없이 해당 application.properties를 따른다.

    • main/resources/application.properties와 test/resources/application.properties에 profile 설정을 안해서 둘다 default를 가지더라도 각각 다른 default임을 헷갈리지 말자