Jalankan metode saat startup di Spring

176

Apakah ada fitur Spring 3 untuk menjalankan beberapa metode saat aplikasi dimulai untuk pertama kalinya? Saya tahu bahwa saya dapat melakukan trik pengaturan metode dengan@Scheduled anotasi dan dijalankan setelah startup, tetapi kemudian akan dieksekusi secara berkala.

Javi
sumber
1
apa masalahnya dengan @ Terjadwal? itulah yang saya inginkan!
chrismarx

Jawaban:

185

Jika dengan "startup aplikasi" yang Anda maksudkan dengan "startup konteks aplikasi", maka ya, ada banyak cara untuk melakukan ini , yang paling mudah (untuk kacang lajang, tetap) dengan menjelaskan metode Anda @PostConstruct. Lihatlah tautan untuk melihat opsi lain, tetapi secara ringkas mereka adalah:

  • Metode yang dijelaskan dengan @PostConstruct
  • afterPropertiesSet()seperti yang didefinisikan oleh InitializingBeanantarmuka panggilan balik
  • Metode init () yang dikonfigurasi khusus

Secara teknis, ini adalah kait ke dalam siklus hidup kacang , bukan siklus hidup konteks, tetapi dalam 99% kasus, keduanya setara.

Jika Anda perlu menghubungkan secara khusus ke konteks startup / shutdown, maka Anda dapat mengimplementasikan Lifecycleantarmuka , tetapi itu mungkin tidak perlu.

skaffman
sumber
7
Saya belum melihat implementasi Lifecycle atau SmartLifecycle setelah melakukan sedikit riset. Saya tahu ini berumur satu tahun, tetapi skaffman jika Anda memiliki sesuatu yang dapat Anda posting yang akan sangat dihargai.
4
Metode di atas dipanggil sebelum seluruh konteks aplikasi dibuat (mis. / Sebelum / demarkasi transaksi telah diatur).
Hans Westerbeek
Saya mendapat peringatan aneh ketika mencoba menggunakan @PostConstruct di java 1.8: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
Ada kasus-kasus penting di mana siklus hidup kacang dan konteks sangat berbeda. Seperti yang dicatat oleh @HansWesterbeek, sebuah kacang dapat diatur sebelum konteksnya tergantung sepenuhnya. Dalam situasi saya, kacang tergantung pada JMS - itu sepenuhnya dibangun, jadi @PostConstructmetodenya disebut, tetapi infra-struktur JMS yang secara tidak langsung tergantung padanya belum sepenuhnya terhubung (dan menjadi Spring semuanya gagal secara diam-diam). Saat beralih ke @EventListener(ApplicationReadyEvent.class)semuanya berfungsi ( ApplicationReadyEventadalah Spring Boot khusus untuk vanilla Spring lihat jawaban Stefan).
George Hawkins
@ Skaffman: bagaimana jika kacang saya tidak dirujuk oleh kacang apa pun dan saya ingin menginisialisasi kacang tanpa digunakan di mana pun
Sagar Kharab
104

Ini mudah dilakukan dengan ApplicationListener. Saya mendapatkan ini untuk bekerja mendengarkan 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 
  }
}

Pendengar aplikasi berjalan secara sinkron di Musim Semi. Jika Anda ingin memastikan kode Anda dieksekusi hanya sekali, simpan saja beberapa status di komponen Anda.

MEMPERBARUI

Dimulai dengan Spring 4.2+ Anda juga dapat menggunakan @EventListeneranotasi untuk mengamati ContextRefreshedEvent(terima kasih kepada @bphilipnyc untuk menunjukkan ini):

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 
  }
}
Stefan Haberl
sumber
1
Ini bekerja untuk saya juga - sempurna untuk inisialisasi non-bean satu kali.
Rory Hunter
9
NB bagi mereka yang tergoda untuk menggunakannya ContextStartedEvent, lebih sulit untuk menambahkan pendengar sebelum acara dimulai.
OrangeDog
2
Bagaimana cara memanggil repositopri JPA @Autowired ke acara? repositori adalah nol.
e-info128
Tidak bekerja untukku. Saya menggunakan spring mvc 3. Metode ini onApplicationEvent (___) tidak dipanggil saat aplikasi dimulai. Bantuan apa saja. Berikut ini adalah kode saya @Component public App Appartartener mengimplementasikan ApplicationListener <ContextRefreshedEvent> {public void onApplicationEvent (final ContextRefreshedEvent event) {System.out.println ("\ n \ n \ nDalam acara aplikasi"); }}
Vishwas Tyagi
@VishwasTyagi Bagaimana Anda memulai wadah Anda? Apakah Anda yakin, AppStartListener Anda adalah bagian dari pemindaian komponen Anda?
Stefan Haberl
38

Di Spring 4.2+ sekarang Anda bisa melakukan:

@Component
class StartupHousekeeper {

    @EventListener(ContextRefreshedEvent.class)
    public void contextRefreshedEvent() {
        //do whatever
    }
}
vphilipnyc
sumber
Apakah dijamin pendengar ini hanya memanggil sekali setelah startup?
gstackoverflow
Tidak, lihat jawaban saya di atas. Simpan beberapa status di pendengar Anda untuk memeriksa apakah itu mengeksekusi untuk pertama kalinya
Stefan Haberl
13

Jika Anda menggunakan spring-boot, ini adalah jawaban terbaik.

Saya merasa bahwa @PostConstructdan berbagai interupsi siklus hidup adalah cara-cara bulat. Ini dapat menyebabkan masalah runtime secara langsung atau menyebabkan cacat yang kurang jelas karena peristiwa siklus hidup kacang / konteks yang tidak terduga. Mengapa tidak langsung memohon kacang Anda menggunakan Java biasa? Anda masih memanggil kacang dengan 'cara pegas' (mis: melalui proxy pegas AoP). Dan yang terbaik, itu java biasa, tidak bisa lebih sederhana dari itu. Tidak perlu pendengar konteks atau penjadwal aneh.

@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();
    }
}
Zombie
sumber
5
Ini adalah ide yang baik secara umum tetapi ketika memulai konteks aplikasi pegas Anda dari tes integrasi, main tidak pernah berjalan!
Jonas Geiregat
@JonasGeiregat: Plus, ada skenario lain di mana tidak ada main()sama sekali, misalnya saat menggunakan kerangka kerja aplikasi (misalnya JavaServer Faces).
sleske
9

Untuk pengguna Java 1.8 yang mendapatkan peringatan ketika mencoba mereferensikan anotasi @PostConstruct, saya malah malah membuntuti anotasi @Jadwal yang dapat Anda lakukan jika Anda sudah memiliki pekerjaan @Jadwal dengan fixedRate atau 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");
    }

}
encrest
sumber
7

Apa yang kami lakukan adalah memperluas org.springframework.web.context.ContextLoaderListeneruntuk mencetak sesuatu ketika konteksnya dimulai.

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

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

Konfigurasikan subclass lalu di web.xml:

<listener>
    <listener-class>
        com.mycomp.myapp.web.context.ContextLoaderListener
    </listener-class>
</listener>
Wim Deblauwe
sumber
7

Dengan SpringBoot, kita dapat menjalankan metode saat startup melalui @EventListeneranotasi

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

Perhatian, ini hanya disarankan jika runOnceOnStartupmetode Anda bergantung pada konteks pegas yang diinisialisasi penuh. Misalnya: Anda ingin memanggil dao dengan demarkasi transaksi

Anda juga dapat menggunakan metode terjadwal dengan fixedDelay yang diatur sangat tinggi

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

Ini memiliki keuntungan bahwa seluruh aplikasi terhubung (Transaksi, Dao, ...)

terlihat di Penjadwalan tugas untuk dijalankan sekali, menggunakan namespace tugas Musim Semi

Joram
sumber
Saya tidak melihat keuntungan dibandingkan menggunakan @PostConstruct?
Wim Deblauwe
@WimDeblauwe tergantung pada apa yang ingin Anda lakukan dalam dosomething () memanggil dao Autowired dengan demarkasi Trasaction memerlukan seluruh konteks untuk dimulai, bukan hanya kacang ini
Joram
5
@WimDeblauwe '@PostConstruct' metode kebakaran ketika kacang diinisialisasi, seluruh konteks mungkin tidak siap (fe Manajemen transaksi)
Joram
Ini lebih elegan daripada posting konstruksi atau antarmuka atau acara
aliopi
1
AppStartListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ApplicationReadyEvent){
            System.out.print("ciao");

        }
    }
}
dnocode
sumber
2
ApplicationReadyEvent ada di booting musim semi bukan di musim semi 3
John Mercier
0

Jika Anda ingin mengonfigurasi kacang sebelum aplikasi Anda berjalan sepenuhnya, Anda dapat menggunakan @Autowired:

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

Anda dapat menggunakan @EventListenerkomponen Anda, yang akan dipanggil setelah server dimulai dan semua kacang diinisialisasi.

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}
krmanish007
sumber
0

Untuk file yang StartupHousekeeper.javaada dalam paketcom.app.startup ,

Lakukan ini di StartupHousekeeper.java:

@Component
public class StartupHousekeeper {

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

Dan lakukan ini di myDispatcher-servlet.java:

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

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

</beans>
Cameron Hudson
sumber