2010-03-08 28 views
144

Czy istnieje jakaś funkcja Spring 3 do wykonywania niektórych metod, gdy aplikacja uruchamia się po raz pierwszy? Wiem, że mogę poradzić sobie z ustawianiem metody za pomocą @Scheduled adnotation i jest ona wykonywana zaraz po uruchomieniu, ale wtedy będzie wykonywana okresowo.Wykonaj metodę przy uruchomieniu wiosną

Dzięki.

+1

jaka jest sztuczka z @Scheduled? właśnie tego chcę! – chrismarx

Odpowiedz

162

Jeśli przez "uruchomienie aplikacji" rozumiesz "uruchamianie kontekstu aplikacji", to tak, są many ways to do this, najłatwiejsze (na przykład w przypadku fasoli singleton) jest przypisywanie swojej metody do anotacji za pomocą @PostConstruct. Spójrz na link, aby zobaczyć inne opcje, ale w skrócie są to:

  • Metody adnotacją z @PostConstruct
  • afterPropertiesSet() zdefiniowane przez interfejs InitializingBean zwrotnej
  • Specjalnie skonfigurowany init() metoda

Z technicznego punktu widzenia są to haki do cyklu życia , a nie do cyklu życia kontekstu, ale w 99% przypadków są one równoważne.

Jeśli potrzebujesz przechwycić konkretnie w kontekście uruchamiania/zamykania, możesz zamiast tego użyć implement the Lifecycle interface, ale to prawdopodobnie nie jest konieczne.

+6

Po dogłębnej analizie jeszcze nie widziałem wdrożenia cyklu Lifecycle lub SmartLifecycle. Wiem, że to ma rok, ale jeśli masz cokolwiek, co możesz opublikować, to byłoby to bardzo cenne. –

+3

Powyższe metody są wywoływane przed utworzeniem całego kontekstu aplikacji (np. Ustawienie rozgraniczenia/przed/transakcją). –

+0

Otrzymuję dziwne ostrzeżenie próbujące użyć @PostConstruct w java 1.8: 'Ograniczenie dostępu: Typ PostConstruct nie jest dostępny z powodu ograniczenia wymaganej biblioteki /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar' – encrest

80

Można to łatwo zrobić za pomocą ApplicationListener. Dostałem to do pracy słuchając wiosennego 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 
    } 
} 

słuchacze aplikacji uruchamiane synchronicznie na wiosnę. Jeśli chcesz się upewnić, że kod jest wykonywany tylko raz, po prostu zachowaj pewien stan w swoim komponencie.

UPDATE

Począwszy od wiosny wersji 4.2 lub nowszej można również użyć @EventListener adnotacji przestrzegać ContextRefreshedEvent (dzięki @bphilipnyc za wskazanie na to uwagę):

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

To też działa dla mnie - idealne do jednorazowej inicjalizacji nie-ziarnistej. –

+8

N.B. dla tych, którzy mają ochotę użyć 'ContextStartedEvent', trudniej jest dodać detektor przed uruchomieniem zdarzenia. – OrangeDog

+1

Jak wywołać @Autowired repozytorium JPA w zdarzenie? repozytorium ma wartość NULL. –

7

Co zrobiliśmy było rozszerzenie org.springframework.web.context.ContextLoaderListener, aby wydrukować coś, gdy zaczyna się kontekst.

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

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

Konfiguracja podklasa następnie w web.xml:

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

Dla użytkowników Java 1.8, którzy otrzymują ostrzeżenie podczas próby odniesienia się do @PostC po dodaniu adnotacji, zamiast tego podszedłem do adnotacji @Scheduled, którą możesz zrobić, jeśli masz już zaplanowaną pracę z fixedRate lub fixedDelay.

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"); 
    } 

} 
+0

zobacz także http://stackoverflow.com/questions/3564361/scheduling-tasks-to-run-once-using-the-spring-task-namespace – Joram

8

Jeśli używasz sprężynowego buta, jest to najlepsza odpowiedź.

Czuję, że @PostConstruct i inne przerywniki cyklu życia są okrągłe. Mogą one prowadzić bezpośrednio do problemów środowiska wykonawczego lub powodować mniej oczywiste defekty z powodu nieoczekiwanych zdarzeń cyklu życia/kontekstu. Dlaczego nie chcesz bezpośrednio wywoływać komponentu bean za pomocą zwykłej Java? Nadal powołujesz się na fasolę "wiosenną drogą" (np. Poprzez proxy proxy AoP). A co najważniejsze, to zwykła Java, nie może być prostsza. Nie ma potrzeby korzystania z detektorów kontekstowych lub nieparzystych harmonogramów.

@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

Jest to dobry pomysł w ogóle, ale przy uruchamianiu kontekstu aplikacji wiosennej z test integracyjny, główny nigdy nie jest uruchamiany! –

+0

@ JonasGeiregat: Dodatkowo istnieją inne scenariusze, w których nie ma wcale 'main()', na przykład podczas korzystania z architektury aplikacji (np. JavaServer Faces). – sleske

0

Jeśli chcesz skonfigurować fasoli przed aplikacja jest w pełni funkcjonalny, można użyć @Autowired:

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

uwaga, to zaleca się tylko wtedy, gdy metoda runOnceOnStartup zależy pełni zainicjalizowany kontekst sprężyny. Na przykład: wan wywołać dao z rozgraniczeniem transakcji

Można również użyć zaplanowanego sposobu z fixedDelay ustawiona bardzo wysoko

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

Ma to tę zaletę, że cała aplikacja jest okablowany (transakcje, Dao, ...)

widać na Scheduling tasks to run once, using the Spring task namespace

+0

Nie widzę żadnej korzyści z używania '@ PostConstruct'? –

+0

@WimDeblauwe zależy od tego, co chcesz zrobić w dosomething() wywoływanie Autowired dao z demaskacją Trasaction wymaga uruchomienia całego kontekstu, nie tylko tego fasoli – Joram

+0

czy możesz rozwinąć? –

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

     } 
    } 
} 
+0

ApplicationReadyEvent jest w wiosennym rozruchu, a nie wiosną 3 –

25

Wiosną 4.2+ możesz teraz po prostu zrobić:

@Component 
class StartupHousekeeper { 

    @EventListener(ContextRefreshedEvent.class) 
    void contextRefreshedEvent() { 
     //do whatever 
    } 
} 
Powiązane problemy