ใช้รหัสหลังจากเริ่มต้นฤดูใบไม้ผลิ


211

ฉันต้องการเรียกใช้รหัสหลังจากแอพspring-bootเริ่มตรวจสอบไดเรกทอรีสำหรับการเปลี่ยนแปลง

ฉันได้ลองใช้งานเธรดใหม่ แต่@Autowiredยังไม่ได้ตั้งค่าบริการ ณ จุดนั้น

ฉันสามารถค้นหาได้ApplicationPreparedEventซึ่งไฟก่อนที่@Autowiredจะมีการตั้งค่าคำอธิบายประกอบ โดยหลักการแล้วฉันต้องการให้เหตุการณ์เริ่มทำงานเมื่อแอปพลิเคชันพร้อมที่จะประมวลผลคำขอ HTTP

มีเหตุการณ์ที่ดีกว่าที่จะใช้หรือวิธีที่ดีกว่าในการใช้รหัสหลังจากที่แอปพลิเคชันเปิดใช้งานในฤดูใบไม้ผลิ - บูต ?



Spring boot จัดให้มีสองอินเตอร์เฟส ApplicationRunner และ CommandLineRunner ซึ่งสามารถใช้เมื่อคุณต้องการเรียกใช้รหัสหลังจากเริ่มต้น boot spring คุณสามารถอ้างอิงถึงบทความนี้สำหรับตัวอย่างการใช้งาน - jhooq.com/applicationrunner-spring-boot
Rahul Wagh

คำตอบ:


121

ลอง:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application extends SpringBootServletInitializer {

    @SuppressWarnings("resource")
    public static void main(final String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

        context.getBean(Table.class).fillWithTestdata(); // <-- here
    }
}

6
สิ่งนี้ไม่ทำงานเมื่อคุณปรับใช้แอปพลิเคชันเป็นไฟล์สงครามกับโพงภายนอก ใช้งานได้เฉพาะกับโพงแคทฝังตัว
Saurabh

ไม่มันไม่ทำงาน @Componentแต่ในกรณีการใช้งานนี้ผมชอบวิธีที่ชัดเจนมากขึ้นแทน ดูคำตอบจาก @cjstehno เพื่อให้มันทำงานในไฟล์สงคราม
Anton Bessonov

320

มันง่ายเหมือนอย่างนี้:

@EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
    System.out.println("hello world, I have just started up");
}

ทดสอบกับรุ่นแล้ว 1.5.1.RELEASE


7
ขอบคุณ. ทำให้รหัสของฉันทำงานได้โดยไม่ต้องมีการเปลี่ยนแปลงใด ๆ ขอบคุณอีกครั้งสำหรับคำตอบง่ายๆ สิ่งนี้จะทำงานกับคำอธิบายประกอบ @RequestMapping โดยไม่มีปัญหา
Harshit

16
บางคนอาจต้องการใช้@EventListener(ContextRefreshedEvent.class)แทนซึ่งจะถูกทริกเกอร์หลังจากการสร้าง bean แต่ก่อนที่เซิร์ฟเวอร์จะเริ่ม มันสามารถใช้ในการดำเนินกิจกรรมก่อนที่จะมีการร้องขอการตีเซิร์ฟเวอร์
neeraj

3
@neeraj คำถามเกี่ยวกับการเรียกใช้รหัสหลังจากเริ่มต้นฤดูใบไม้ผลิ ถ้าคุณใช้ContextRefreshedEventมันจะทำงานหลังจากรีเฟรชทุกครั้ง
cahen

4
ทดสอบใน Spring boot 2.0.5.RELEASE
ritesh

2
ทดสอบในรุ่น 2.2.2 มันทำงานได้อย่างสมบูรณ์แบบ วิธีนี้ช่วยประหยัดเวลาของฉัน
Arphile

96

คุณได้ลอง ApplicationReadyEvent แล้วหรือยัง

@Component
public class ApplicationStartup 
implements ApplicationListener<ApplicationReadyEvent> {

  /**
   * This event is executed as late as conceivably possible to indicate that 
   * the application is ready to service requests.
   */
  @Override
  public void onApplicationEvent(final ApplicationReadyEvent event) {

    // here your code ...

    return;
  }
}

รหัสจาก: http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/

นี่คือเอกสารที่กล่าวถึงเกี่ยวกับเหตุการณ์เริ่มต้น:

...

แอปพลิเคชันจะถูกส่งไปตามลำดับต่อไปนี้ขณะที่แอปพลิเคชันของคุณทำงาน:

ApplicationStartedEvent ถูกส่งไปที่จุดเริ่มต้นของการรัน แต่ก่อนประมวลผลใด ๆ ยกเว้นการลงทะเบียนของ listeners และ initializers

ApplicationEnvironmentPreparedEvent จะถูกส่งเมื่อทราบถึงสภาพแวดล้อมที่จะใช้ในบริบท แต่ก่อนที่บริบทจะถูกสร้างขึ้น

ApplicationPreparedEvent ถูกส่งก่อนรีเฟรชเริ่ม แต่หลังจากโหลดคำจำกัดความของถั่วแล้ว

ApplicationReadyEvent จะถูกส่งหลังจากรีเฟรชและมีการประมวลผลการเรียกกลับที่เกี่ยวข้องใด ๆ เพื่อระบุว่าแอปพลิเคชันพร้อมที่จะให้บริการตามคำขอ

ApplicationFailedEvent จะถูกส่งหากมีข้อยกเว้นในการเริ่มต้น

...


11
คุณสามารถทำได้โดยใช้@EventListenerคำอธิบายประกอบบนเมธอด Bean ส่งผ่านเป็นอาร์กิวเมนต์คลาสเหตุการณ์ที่คุณต้องการขอ
padilo

2
นี่ควรเป็นคำตอบที่เลือก
varun113

2
สิ่งนี้มีการเปลี่ยนแปลงใน spring-boot 2 หากคุณกำลังย้ายพอร์ตจาก 1.x และใช้ ApplicationStartedEvent ดังนั้นตอนนี้คุณต้องการ ApplicationStartingEvent แทน
แอนดี้บราวน์

ทำงานได้ดีอย่างแน่นอนและฉันคิดว่าวิธีที่ดีที่สุดที่จะทำ
AVINASH SHRIMALI

คุณดีที่สุด
ancm

83

ทำไมไม่เพียงแค่สร้าง bean ที่เริ่มต้นมอนิเตอร์ของคุณเมื่อเริ่มต้นสิ่งที่ชอบ:

@Component
public class Monitor {
    @Autowired private SomeService service

    @PostConstruct
    public void init(){
        // start your monitoring in here
    }
}

initวิธีการจะไม่ถูกเรียกจน autowiring ใด ๆ ที่จะทำเพื่อถั่ว


14
บางครั้ง@PostConstructไฟก็เร็วเกินไป ตัวอย่างเช่นเมื่อใช้ Spring Cloud Stream Kafka @PostConstructไฟก่อนที่แอปพลิเคชันจะผูกกับ Kafka วิธีการแก้ปัญหาของ Dave Syer ดีกว่าเพราะมันทำงานได้ทันเวลา
Elnur Abdurrakhimov

9
@PostConstructเกิดขึ้นในช่วงเริ่มต้นไม่ใช่หลังจาก แม้ว่าจะมีประโยชน์ในบางกรณี แต่ก็ไม่ใช่คำตอบที่ถูกต้องหากคุณต้องการเรียกใช้หลังจากเริ่มการบู๊ตสปริง ตัวอย่างเช่นในขณะที่@PostConstructยังไม่เสร็จสิ้นจะไม่มีจุดสิ้นสุดใด ๆ
cahen

63

ฤดูใบไม้ผลิ "Boot" CommandLineRunnerวิธีคือการใช้ เพียงเพิ่มถั่วประเภทนั้นและคุณก็พร้อมที่จะไป ใน Spring 4.1 (Boot 1.2) นอกจากนี้ยังมีตัวSmartInitializingBeanที่ได้รับการติดต่อกลับหลังจากทุกอย่างได้เริ่มต้น และมีSmartLifecycle(จากฤดูใบไม้ผลิ 3)


มีตัวอย่างอะไรบ้าง? เป็นไปได้ไหมที่จะเรียกใช้งาน bean หลังจากแอพทำงานผ่านบรรทัดคำสั่งในช่วงเวลาที่กำหนด?
Emilio

5
ไม่ทราบว่าคุณหมายถึงอะไรโดย "ช่วงเวลาโดยพลการ" คู่มือผู้ใช้ Spring Boot และตัวอย่างมีตัวอย่างของการใช้CommandLineRunner(และใหม่กว่าApplicationRunner): docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/
Dave Syer

ฉันพบว่า Lifecycle เป็นตัวเลือกที่ต้องการในการทำงานแบบอะซิงโครนัสในขั้นตอนเริ่ม / หยุดของแอปพลิเคชันและฉันพยายามมองเห็นความแตกต่างอื่น ๆ ระหว่าง CommandLineRunner และ InitializingBeans แต่ไม่พบอะไรเกี่ยวกับเรื่องนั้น
saljuama

3
คู่ตัวอย่างรหัสปกติของการใช้CommandLineRunner
Alexey Simonov

41

คุณสามารถขยายคลาสโดยใช้ApplicationRunnerแทนที่run()วิธีการและเพิ่มรหัสที่นั่น

import org.springframework.boot.ApplicationRunner;

@Component
public class ServerInitializer implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {

        //code goes here

    }
}

สมบูรณ์แบบใน Spring Boot แต่เรียกใช้ () วิธีการถูกเรียกสองครั้งเมื่อมี ApplicationScope สำหรับชั้นเรียน ดังนั้นวิธี PostConstruct กับข้างต้นทำงานได้ดีขึ้น
Sam

26

ApplicationReadyEventมีประโยชน์จริง ๆ ถ้างานที่คุณต้องการทำไม่ใช่ข้อกำหนดสำหรับการดำเนินการของเซิร์ฟเวอร์ที่ถูกต้อง การเริ่มภารกิจ async เพื่อตรวจสอบบางอย่างสำหรับการเปลี่ยนแปลงเป็นตัวอย่างที่ดี

อย่างไรก็ตามหากเซิร์ฟเวอร์ของคุณอยู่ในสถานะ 'ไม่พร้อม' จนกว่างานจะเสร็จสมบูรณ์ก็จะดีกว่าที่จะใช้SmartInitializingSingletonเพราะคุณจะได้รับการติดต่อกลับก่อนที่พอร์ต REST ของคุณจะถูกเปิดและเซิร์ฟเวอร์ของคุณเปิดสำหรับธุรกิจ

อย่าถูกล่อลวงให้ใช้@PostConstructงานที่ควรจะเกิดขึ้นครั้งเดียวเท่านั้น คุณจะได้รับความประหลาดใจเมื่อคุณสังเกตเห็นว่ามันถูกเรียกหลายครั้ง ...


นี่ควรเป็นคำตอบที่เลือก @Andy ชี้ให้เห็นว่า SmartInitializingSingleton ถูกเรียกก่อนเปิดพอร์ต
Srikanth

24

ด้วยการกำหนดค่าสปริง:

@Configuration
public class ProjectConfiguration {
    private static final Logger log = 
   LoggerFactory.getLogger(ProjectConfiguration.class);

   @EventListener(ApplicationReadyEvent.class)
   public void doSomethingAfterStartup() {
    log.info("hello world, I have just started up");
  }
}

12

ใช้SmartInitializingSingletonถั่วในฤดูใบไม้ผลิ> 4.1

@Bean
public SmartInitializingSingleton importProcessor() {
    return () -> {
        doStuff();
    };

}

ในฐานะที่เป็นทางเลือกCommandLineRunnerถั่วสามารถดำเนินการได้หรือ annotating @PostConstructวิธีถั่วที่มี


ฉันสามารถขอการพึ่งพาอัตโนมัติได้ภายในวิธีการนั้นได้หรือไม่ ฉันต้องการตั้งค่าโปรไฟล์
LppEdd

7

ยกตัวอย่างสำหรับคำตอบของเดฟไซเซอร์ซึ่งใช้งานได้ดี

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

    @Override
    public void run(String...args) throws Exception {
        logger.info("Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.", Arrays.toString(args));
    }
}

7

ลองอันนี้แล้วมันจะเรียกใช้รหัสของคุณเมื่อบริบทของแอปพลิเคชันเริ่มต้นขึ้นอย่างสมบูรณ์

 @Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent arg0) {
                // EXECUTE YOUR CODE HERE 
    }
}

6

ฉันชอบข้อเสนอแนะสำหรับการใช้EventListenerคำอธิบายประกอบโดย @cahen ( https://stackoverflow.com/a/44923402/9122660 ) เนื่องจากมันสะอาดมาก น่าเสียดายที่ฉันไม่สามารถใช้สิ่งนี้ในการติดตั้ง Spring + Kotlin สิ่งที่ใช้ได้ผลสำหรับ Kotlin คือการเพิ่มคลาสเป็นพารามิเตอร์ method:

@EventListener 
fun doSomethingAfterStartup(event: ApplicationReadyEvent) {
    System.out.println("hello world, I have just started up");
}

ใส่ไว้ในคลาสแอพพลิเคชั่นบูทบูทโดยไม่สุ่มออกด้านข้าง@SpringBootApplication class MyApplication { @EventListener(ApplicationReadyEvent::class) fun doSomethingAfterStartup() { println("hello world, I have just started up") } }
อาเหม็ดนา

คุณไม่จำเป็นต้องใส่คลาส @SpringBootApplication คลาสการกำหนดค่าใด ๆ จะทำ
George Marin

5

วิธีที่ดีที่สุดในการเรียกใช้การบล็อกรหัสหลังจากที่แอปพลิเคชัน Spring Boot เริ่มต้นใช้การเพิ่มความคิดเห็นของ PostConstruct หรือคุณสามารถใช้ command line Runner เหมือนกัน

1. การใช้คำอธิบายประกอบ PostConstruct

@Configuration
public class InitialDataConfiguration {

    @PostConstruct
    public void postConstruct() {
        System.out.println("Started after Spring boot application !");
    }

}

2. ใช้ command line runner bean

@Configuration
public class InitialDataConfiguration {

    @Bean
    CommandLineRunner runner() {
        return args -> {
            System.out.println("CommandLineRunner running in the UnsplashApplication class...");
        };
    }
}

3

เพียงแค่ใช้ CommandLineRunner สำหรับแอพพลิเคชั่นบู๊ทสปริง คุณต้องใช้วิธีการเรียกใช้

public classs SpringBootApplication implements CommandLineRunner{

    @Override
        public void run(String... arg0) throws Exception {
        // write your logic here 

        }
}

0

วิธีที่ดีที่สุดที่คุณใช้ CommandLineRunner หรือ ApplicationRunner ความแตกต่างเพียงอย่างเดียวระหว่างวิธีการรัน () วิธี CommandLineRunner ยอมรับอาร์เรย์ของสตริงและ ApplicationRunner ยอมรับ ApplicationArugument

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.