Spring 시작시 메소드 실행


176

애플리케이션이 처음 시작될 때 일부 메소드를 실행하는 Spring 3 기능이 있습니까? @Scheduled주석 으로 메소드를 설정하는 트릭을 수행 할 수 있으며 시작 직후 실행되지만 주기적으로 실행됩니다.


1
@Scheduled의 트릭은 무엇입니까? 바로 내가 원하는 것입니다!
chrismarx

답변:


185

"응용 프로그램 시작"이 "응용 프로그램 컨텍스트 시작"을 의미하는 경우 그렇습니다.이 작업을 수행 하는 가장 쉬운 방법여러 가지@PostConstruct있습니다. 다른 옵션을 보려면 링크를 살펴보십시오. 요약하면 다음과 같습니다.

  • 주석이 달린 방법 @PostConstruct
  • afterPropertiesSet()InitializingBean콜백 인터페이스에 의해 정의 된
  • 사용자 정의 구성된 init () 메소드

기술적으로 이들은 컨텍스트 라이프 사이클이 아닌 Bean 라이프 사이클에 연결되지만 99 %의 경우 두 가지가 동일합니다.

컨텍스트 시작 / 종료에 구체적으로 연결해야하는 경우 대신 인터페이스구현할Lifecycle 수 있지만 불필요 할 수 있습니다 .


7
꽤 많은 연구 끝에 Lifecycle 또는 SmartLifecycle 구현을 아직 보지 못했습니다. 나는 이것이 1 살이라는 것을 알고 있지만, 당신이 게시 할 수있는 것이 있다면 매우 감사 할 것입니다.

4
위의 메소드는 전체 애플리케이션 컨텍스트가 작성되기 전에 호출됩니다 (예 : / before / transaction demarcation이 설정 됨).
Hans Westerbeek

Java 1.8에서 @PostConstruct를 사용하려고 시도하는 이상한 경고가 표시됩니다.Access restriction: The type PostConstruct is not accessible due to restriction on required library /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar
encrest

2
Bean컨텍스트 라이프 사이클이 매우 다른 중요한 경우가 있습니다 . @HansWesterbeek이 언급했듯이, 컨텍스트가 완전히 준비되기 전에 Bean을 설정할 수 있습니다. 내 상황에서 bean은 JMS에 의존했다. JMS는 완전히 구성되었으므로 @PostConstruct메소드가 호출되었지만 간접적으로 의존했던 JMS 인프라는 아직 완전히 연결되지 않았다 (그리고 Spring은 모든 것이 조용히 실패했다). @EventListener(ApplicationReadyEvent.class)모든 작업으로 전환하면 ( ApplicationReadyEvent바닐라 스프링에 대한 스프링 부트는 스테판의 답변 참조).
George Hawkins

@Skaffman : 내 콩이 어떤 콩에 의해서도 언급되지 않고 어디서나 사용되지 않고 콩을 초기화하고 싶다면
Sagar Kharab

104

이것은로 쉽게 수행 할 수 있습니다 ApplicationListener. Spring의 청취를 위해이 작업을 수행했습니다 ContextRefreshedEvent.

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class StartupHousekeeper implements ApplicationListener<ContextRefreshedEvent> {

  @Override
  public void onApplicationEvent(final ContextRefreshedEvent event) {
    // do whatever you need here 
  }
}

애플리케이션 리스너는 Spring에서 동기식으로 실행됩니다. 코드가 한 번만 실행되도록하려면 구성 요소의 상태를 유지하십시오.

최신 정보

Spring 4.2 이상부터는 @EventListener주석을 사용 하여 ContextRefreshedEvent( 이 점을 지적한 @bphilipnyc 덕분에) 관찰 할 수 있습니다 .

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void contextRefreshedEvent() {
    // do whatever you need here 
  }
}

1
이것은 나에게도 효과적이었습니다. 일회성 비콩 초기화에 완벽합니다.
Rory Hunter

9
ContextStartedEvent대신 NB를 사용하려는 사람들 은 이벤트가 발생하기 전에 리스너를 추가하기가 더 어렵습니다.
OrangeDog

2
@Autowired JPA 리포지토리를 이벤트에 호출하는 방법? 저장소가 널입니다.
e-info128

나를 위해 작동하지 않습니다. 스프링 mvc 3을 사용하고 있습니다.이 mehod onApplicationEvent (___)는 응용 프로그램이 시작될 때 호출되지 않습니다. 어떤 도움이라도. 다음은 내 코드입니다. @Component public class AppStartListener는 ApplicationListener <ContextRefreshedEvent>를 구현합니다. {public void onApplicationEvent (final ContextRefreshedEvent event) {System.out.println ( "\ n \ n \ n 응용 프로그램 이벤트 내부"); }}
Vishwas Tyagi

@VishwasTyagi 컨테이너는 어떻게 시작합니까? AppStartListener가 구성 요소 스캔의 일부입니까?
Stefan Haberl

38

Spring 4.2 이상에서는 이제 간단하게 할 수 있습니다.

@Component
class StartupHousekeeper {

    @EventListener(ContextRefreshedEvent.class)
    public void contextRefreshedEvent() {
        //do whatever
    }
}

시작 후에이 리스너가 한 번만 호출되는 것이 보장됩니까?
gstackoverflow 14시 32 분

아니요, 위의 답변을 참조하십시오. 청취자가 처음으로 실행 중인지 확인하기 위해 리스너의 상태를 유지하십시오
Stefan Haberl

13

스프링 부트를 사용하는 경우 이것이 가장 좋은 대답입니다.

나는 느낌 @PostConstruct등 다양한 수명주기 감탄사가 라운드에 대한 방법입니다합니다. 이로 인해 런타임 문제가 직접 발생하거나 예기치 않은 Bean / 컨텍스트 라이프 사이클 이벤트로 인해 눈에 띄지 않는 결함이 발생할 수 있습니다. 왜 일반 Java를 사용하여 Bean을 직접 호출하지 않습니까? 당신은 여전히 ​​빈을 'spring way'(예 : 스프링 AoP 프록시를 통해) 호출합니다 그리고 무엇보다도, 그것은 평범한 자바이며, 그보다 더 간단해질 수는 없습니다. 컨텍스트 리스너 나 홀수 스케줄러가 필요하지 않습니다.

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args);

        MyBean myBean = (MyBean)app.getBean("myBean");

        myBean.invokeMyEntryPoint();
    }
}

5
이것은 일반적으로 좋은 생각이지만 통합 테스트에서 스프링 애플리케이션 컨텍스트를 시작할 때 main이 실행되지 않습니다!
Jonas Geiregat

@JonasGeiregat : 또한 main()애플리케이션 프레임 워크 (예 : JavaServer Faces)를 사용할 때와 같은 다른 시나리오도 없습니다 .
sleske

9

@PostConstruct 주석을 참조하려고 할 때 경고가 표시되는 Java 1.8 사용자의 경우, fixedRate 또는 fixedDelay를 사용하여 @Scheduled 작업이 이미있는 경우 수행 할 수있는 @Scheduled 주석을 피기 백하는 대신 종료되었습니다.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@EnableScheduling
@Component
public class ScheduledTasks {

private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTasks.class);

private static boolean needToRunStartupMethod = true;

    @Scheduled(fixedRate = 3600000)
    public void keepAlive() {
        //log "alive" every hour for sanity checks
        LOGGER.debug("alive");
        if (needToRunStartupMethod) {
            runOnceOnlyOnStartup();
            needToRunStartupMethod = false;
        }
    }

    public void runOnceOnlyOnStartup() {
        LOGGER.debug("running startup job");
    }

}


7

우리가 한 일은 org.springframework.web.context.ContextLoaderListener컨텍스트가 시작될 때 무언가를 인쇄하도록 확장 되었습니다.

public class ContextLoaderListener extends org.springframework.web.context.ContextLoaderListener
{
    private static final Logger logger = LoggerFactory.getLogger( ContextLoaderListener.class );

    public ContextLoaderListener()
    {
        logger.info( "Starting application..." );
    }
}

다음에서 서브 클래스를 구성하십시오 web.xml.

<listener>
    <listener-class>
        com.mycomp.myapp.web.context.ContextLoaderListener
    </listener-class>
</listener>

7

SpringBoot를 사용하면 시작시 @EventListener주석을 통해 메소드를 실행할 수 있습니다

@Component
public class LoadDataOnStartUp
{   
    @EventListener(ApplicationReadyEvent.class)
    public void loadData()
    {
        // do something
    }
}

4

주의 : 이것은 runOnceOnStartup메소드가 완전히 초기화 된 스프링 컨텍스트에 의존하는 경우에만 권장됩니다 . 예를 들어, 트랜잭션 경계 설정이있는 dao를 호출하려고합니다.

fixedDelay를 매우 높게 설정하여 예약 된 방법을 사용할 수도 있습니다.

@Scheduled(fixedDelay = Long.MAX_VALUE)
public void runOnceOnStartup() {
    dosomething();
}

이것은 전체 응용 프로그램이 연결되어 있다는 이점이 있습니다 (Transactions, Dao, ...)

Spring 작업 네임 스페이스를 사용하여 한 번 실행되도록 예약 작업 에서 볼 수 있음


사용하는 것보다 이점이 보이지 @PostConstruct않습니까?
Wim Deblauwe

@WimDeblauwe 당신이 Trasaction 경계과를 autowire의 DAO가 시작되는 전체 문맥을 필요로 호출) (해봐요에서 수행하려는 작업에 따라 달라집니다뿐만 아니라이 콩
요람

5
Bean이 초기화 될 때 @WimDeblauwe '@PostConstruct'메소드가 실행되면 전체 컨텍스트가 준비되지 않았을 수 있습니다 (fe 트랜잭션 관리)
Joram

이것은 포스트 컨스
트럭 트나


1
AppStartListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ApplicationReadyEvent){
            System.out.print("ciao");

        }
    }
}

2
ApplicationReadyEvent는 3
John Mercier

0

애플리케이션이 완전히 실행되기 전에 Bean을 구성하려는 경우 다음을 사용할 수 있습니다 @Autowired.

@Autowired
private void configureBean(MyBean: bean) {
    bean.setConfiguration(myConfiguration);
}

0

@EventListener서버에서 시작되고 모든 Bean이 초기화 된 후에 호출되는 컴포넌트에서 사용할 수 있습니다 .

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}

0

StartupHousekeeper.javapackage에 있는 파일의 com.app.startup경우

이 작업을 수행하십시오 StartupHousekeeper.java.

@Component
public class StartupHousekeeper {

  @EventListener(ContextRefreshedEvent.class)
  public void keepHouse() {
    System.out.println("This prints at startup.");
  }
}

그리고 이것을하십시오 myDispatcher-servlet.java:

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <mvc:annotation-driven />
    <context:component-scan base-package="com.app.startup" />

</beans>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.