1. 소개

이 기사에서는 Spring 애플리케이션을 시작할 때 로직실행 하는 방법에 초점을 맞출 것 입니다.

2. 시작시 로직 실행

Spring 애플리케이션 시작 중 / 후에 로직을 실행하는 것은 일반적인 시나리오이지만 여러 문제를 일으키는 시나리오입니다.

Inverse of Control의 이점을 얻으려면 당연히 컨테이너로가는 애플리케이션의 흐름에 대한 부분적인 제어를 포기해야합니다. 이것이 바로 인스턴스화, 시작시 로직 설정 등에 특별한주의가 필요한 이유입니다.

빈의 생성자에 로직을 포함 시키거나 객체의 인스턴스화 후에 메서드를 호출 할 수는 없습니다. 우리는 그 과정에서 통제권을 가지고 있지 않습니다.

실제 예를 살펴 보겠습니다.

@Component
public class InvalidInitExampleBean {

    @Autowired
    private Environment env;

    public InvalidInitExampleBean() {
        env.getActiveProfiles();
    }
}

여기서는 생성자에서 autowired 필드에 액세스하려고합니다 . 생성자가 호출 될 때 Spring Bean은 아직 완전히 초기화되지 않았습니다. 때문에이 문제가 호출이 아직 초기화 필드의 과정에서 발생합니다하지 NullPointerException이 .

Spring은 이러한 상황을 관리하는 몇 가지 방법을 제공합니다.

2.1. @PostConstruct 어노테이션

Javax의 @PostConstruct 어노테이션은 Bean 초기화 직후 한 번 실행되어야하는 메소드에 어노테이션을 작성하는 데 사용할 수 있습니다 . 주입 할 것이 없더라도 어노테이션이 달린 메서드는 Spring에 의해 실행된다는 점을 명심하십시오.

작동중인 @PostConstruct다음과 같습니다 .

@Component
public class PostConstructExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(PostConstructExampleBean.class);

    @Autowired
    private Environment environment;

    @PostConstruct
    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

위의 예제에서 환경 인스턴스가 안전하게 주입 된 다음 NullPointerException을 발생 시키지 않고 @PostConstruct 어노테이션이 달린 메서드 에서 호출 되었음을 알 수 있습니다 .

2.2. InitializingBean 표시 인터페이스

InitializingBean 표시 방법은 이전에 꽤 유사하게 작동합니다. 메소드에 어노테이션을 추가하는 대신 InitializingBean 인터페이스와 afterPropertiesSet () 메소드 를 구현해야 합니다 .

여기에서 InitializingBean 인터페이스를 사용하여 구현 된 이전 예제를 볼 수 있습니다 .

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG 
      = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.3. 만약 ApplicationListener

이 접근법은 Spring 컨텍스트가 초기화 된 후 로직실행하는 데 사용할 수 있으므로 특정 Bean에 초점을 맞추지 않고 모든 Bean이 초기화되기를 기다리고 있습니다.

이를 위해 ApplicationListener <ContextRefreshedEvent> 인터페이스 를 구현하는 빈을 생성해야합니다 .

@Component
public class StartupApplicationListenerExample implements 
  ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOG 
      = Logger.getLogger(StartupApplicationListenerExample.class);

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

새로 도입 된 @EventListener 어노테이션 을 사용하여 동일한 결과를 얻을 수 있습니다 .

@Component
public class EventListenerExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(EventListenerExampleBean.class);

    public static int counter;

    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

이 예에서는 ContextRefreshedEvent를 선택했습니다 . 귀하의 필요에 맞는 적절한 이벤트를 선택하십시오.

2.4. @Bean Initmethod 속성

 initMethod의 속성은 빈의 초기화 후에 방법을 실행하는 데 사용할 수 있습니다.

다음은 빈의 모습입니다.

public class InitMethodExampleBean {

    private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

    @Autowired
    private Environment environment;

    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

구현 된 특수 인터페이스 나 특수 어노테이션이 사용되지 않음을 알 수 있습니다.

그런 다음 @Bean 어노테이션을 사용하여 빈을 정의 할 수 있습니다 .

@Bean(initMethod="init")
public InitMethodExampleBean initMethodExampleBean() {
    return new InitMethodExampleBean();
}

그리고 이것은 XML 설정에서 bean 정의가 보이는 방법입니다.

<bean id="initMethodExampleBean"
  class="com.baeldung.startup.InitMethodExampleBean"
  init-method="init">
</bean>

2.5. 생성자 주입

생성자 주입을 사용하여 필드를 주입하는 경우 생성자에 논리를 포함하기 만하면됩니다.

@Component 
public class LogicInConstructorExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(LogicInConstructorExampleBean.class);

    private final Environment environment;

    @Autowired
    public LogicInConstructorExampleBean(Environment environment) {
        this.environment = environment;
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.6. 스프링 부트 CommandLineRunner

Spring 부트는 Spring 애플리케이션 컨텍스트가 인스턴스화 된 후 애플리케이션 시작시 호출 될 수 있는 콜백 run () 메소드가 있는 CommandLineRunner 인터페이스를 제공합니다 .

예를 살펴 보겠습니다.

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

    public static int counter;

    @Override
    public void run(String...args) throws Exception {
        LOG.info("Increment counter");
        counter++;
    }
}

참고 : 문서 에서 언급했듯이 여러 CommandLineRunner Bean을 동일한 애플리케이션 컨텍스트 내에서 정의 할 수 있으며 @Ordered 인터페이스 또는 @Order 어노테이션을 사용하여 주문할 수 있습니다 .

2.7. 스프링 부트 ApplicationRunner

CommandLineRunner와 유사하게 Spring 부트는 애플리케이션 시작시 호출 되는 run () 메소드가 있는 ApplicationRunner 인터페이스 도 제공합니다 . 그러나 콜백 메서드에 전달 된 원시 문자열 인수 대신 ApplicationArguments 클래스 의 인스턴스가 있습니다 .

ApplicationArguments의 인터페이스는 옵션과 일반 인수 값이다 인수 값을 얻을 수있는 방법이있다. – – 접두사가 붙은 인수는 옵션 인수입니다.

예를 살펴 보겠습니다.

@Component
public class AppStartupRunner implements ApplicationRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(AppStartupRunner.class);

    public static int counter;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOG.info("Application started with option names : {}", 
          args.getOptionNames());
        LOG.info("Increment counter");
        counter++;
    }
}

3. 메커니즘 결합

Bean을 완전히 제어하려면 위의 메커니즘을 함께 결합 할 수 있습니다.

실행 순서는 다음과 같습니다.

  1. 생성자
  2. @PostConstruct 어노테이션 방법
  3. InitializingBean의 afterPropertiesSet () 메소드
  4. XML에서 init-method 로 지정된 초기화 메소드

모든 메커니즘을 결합하는 스프링 빈을 만들어 보자 :

@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {

    private static final Logger LOG 
      = Logger.getLogger(AllStrategiesExampleBean.class);

    public AllStrategiesExampleBean() {
        LOG.info("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.info("PostConstruct");
    }

    public void init() {
        LOG.info("init-method");
    }
}

이 Bean을 인스턴스화하려고하면 위에 지정된 순서와 일치하는 로그를 볼 수 있습니다.

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method

4. 결론

이 기사에서 우리는 Spring의 애플리케이션 시작에서 로직을 실행하는 여러 방법을 설명했다.

코드 샘플은 GitHub 에서 찾을 수 있습니다 .