Chạy mã sau khi Spring Boot bắt đầu


211

Tôi muốn chạy mã sau khi ứng dụng khởi động mùa xuân của tôi bắt đầu theo dõi một thư mục để thay đổi.

Tôi đã thử chạy một chủ đề mới nhưng các @Autowireddịch vụ chưa được thiết lập tại thời điểm đó.

Tôi đã có thể tìm thấy ApplicationPreparedEvent, nó kích hoạt trước khi các @Autowiredchú thích được đặt. Lý tưởng nhất là tôi muốn sự kiện này được kích hoạt khi ứng dụng đã sẵn sàng xử lý các yêu cầu http.

Có sự kiện nào tốt hơn để sử dụng hay cách chạy mã tốt hơn sau khi ứng dụng hoạt động trong spring-boot ?



Spring boot cung cấp hai giao diện ApplicationRunner và CommandLineRunner có thể được sử dụng khi bạn muốn chạy mã sau khi khởi động mùa xuân. Bạn có thể tham khảo bài viết này để biết ví dụ triển khai - jhooq.com/applicationrunner-spring-boot
Rahul Wagh

Câu trả lời:


121

Thử:

@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
điều này không hoạt động khi bạn triển khai ứng dụng dưới dạng tệp tin chiến tranh sang tomcat bên ngoài. Nó chỉ hoạt động với tomcat nhúng
Saurabh

Không, nó không hoạt động. Nhưng trong trường hợp sử dụng này, tôi thích cách rõ ràng hơn thay vì @Component. Xem câu trả lời từ @cjstehno để làm cho nó hoạt động trong một tập tin chiến tranh.
Anton Bessonov

320

Nó đơn giản như thế này:

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

Đã thử nghiệm trên phiên bản 1.5.1.RELEASE


7
cảm ơn bạn. điều này làm cho mã của tôi hoạt động mà không cần bất kỳ thay đổi nào. Cảm ơn một lần nữa cho một câu trả lời đơn giản. Điều này cũng sẽ hoạt động với chú thích @RequestMapping mà không có vấn đề gì.
Harshit

16
Ai đó cũng có thể muốn sử dụng @EventListener(ContextRefreshedEvent.class)thay thế, được kích hoạt sau khi tạo bean, nhưng trước khi máy chủ khởi động. Nó có thể được sử dụng để thực hiện các hoạt động trước khi bất kỳ yêu cầu nào đến máy chủ.
neeraj

3
@neeraj, câu hỏi là về việc chạy mã sau khi Spring Boot bắt đầu. Nếu bạn sử dụng ContextRefreshedEvent, nó sẽ chạy sau mỗi lần làm mới.
cahen

4
đã thử nghiệm trên Spring boot 2.0.5.RELEASE
ritesh

2
Đã thử nghiệm trên 2.2.2. nó hoạt động hoàn hảo giải pháp này tiết kiệm thời gian của tôi.
Arphile

96

Bạn đã thử ApplicationReadyEvent chưa?

@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;
  }
}

Mã từ: http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/

Đây là những gì tài liệu đề cập về các sự kiện khởi động:

...

Các sự kiện ứng dụng được gửi theo thứ tự sau, khi ứng dụng của bạn chạy:

Một ApplicationStartedEvent được gửi khi bắt đầu chạy, nhưng trước khi xử lý ngoại trừ việc đăng ký người nghe và người khởi tạo.

Một ApplicationEn Môi trườngPreparedEvent được gửi khi Môi trường được sử dụng trong bối cảnh được biết đến, nhưng trước khi bối cảnh được tạo.

Một ApplicationPreparedEvent được gửi ngay trước khi bắt đầu làm mới, nhưng sau khi các định nghĩa bean đã được tải.

ApplicationReadyEvent được gửi sau khi làm mới và mọi cuộc gọi lại liên quan đã được xử lý để cho biết ứng dụng đã sẵn sàng cho các yêu cầu dịch vụ.

Một ApplicationFailedEvent được gửi nếu có một ngoại lệ khi khởi động.

...


11
Cách khác, bạn có thể thực hiện việc này bằng cách sử dụng @EventListenerchú thích trên phương thức Bean, chuyển qua làm đối số cho sự kiện lớp mà bạn muốn nối vào.
padilo

2
Đây nên là câu trả lời được lựa chọn.
varun113

2
Điều này đã thay đổi trong spring-boot 2. Nếu bạn đang chuyển lên từ 1.x và đang sử dụng ApplicationStartedEvent thì bây giờ bạn muốn ApplicationStartingEvent thay thế.
Andy Brown

Điều này hoạt động hoàn toàn tốt và tôi nghĩ cách tốt nhất để làm.
AVINASH SHRIMALI 17/12/18

bạn là nhất
ancm

83

Tại sao không chỉ tạo một bean khởi động màn hình của bạn khi khởi tạo, đại loại như:

@Component
public class Monitor {
    @Autowired private SomeService service

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

các initphương pháp sẽ không được gọi đến bất kỳ autowiring được thực hiện cho hạt cà phê.


14
Đôi khi @PostConstructcháy quá sớm. Ví dụ: khi sử dụng Spring Cloud Stream Kafka, sẽ @PostConstructkích hoạt trước khi ứng dụng liên kết với Kafka. Giải pháp của Dave Syer tốt hơn bởi vì nó bắn kịp thời.
Elnur Abdurrakhimov

9
@PostConstructxảy ra trong quá trình khởi tạo, không phải sau đó. Mặc dù điều này có thể hữu ích trong một số trường hợp, nhưng đó không phải là câu trả lời chính xác nếu bạn muốn chạy sau khi Spring Boot bắt đầu. Ví dụ, trong khi @PostConstructkhông kết thúc, không có điểm cuối nào khả dụng.
cahen

63

Cách "Spring Boot" là sử dụng a CommandLineRunner. Chỉ cần thêm đậu loại đó và bạn là tốt để đi. Trong Spring 4.1 (Boot 1.2) cũng SmartInitializingBeancó một cuộc gọi lại sau khi mọi thứ đã được khởi tạo. Và có SmartLifecycle(từ mùa xuân 3).


Bất kỳ ví dụ về điều đó? Có thể thực thi một bean sau khi ứng dụng đang chạy, thông qua dòng lệnh trong một thời điểm tùy ý?
Emilio

5
Không biết ý của bạn là "khoảnh khắc tùy ý". Hướng dẫn sử dụng và các mẫu của Spring Boot có các ví dụ về việc sử dụng CommandLineRunner(và mới hơn ApplicationRunner): docs.spring.io/spring-boot/docs/civerse-SNAPSHOT/reference/ đấm .
Dave Syer

Tôi thấy rằng Vòng đời là tùy chọn ưa thích để thực hiện các tác vụ không đồng bộ ở các giai đoạn bắt đầu / dừng của ứng dụng và tôi đang cố gắng phát hiện ra những khác biệt khác giữa CommandLineRunner và LaunchizingBeans, nhưng không thể tìm thấy bất cứ điều gì về điều đó.
saljuama

3
vài ví dụ thông thường về mã sử dụngCommandLineRunner
Alexey Simonov

41

Bạn có thể mở rộng một lớp bằng cách sử dụng ApplicationRunner, ghi đè run()phương thức và thêm mã vào đó.

import org.springframework.boot.ApplicationRunner;

@Component
public class ServerInitializer implements ApplicationRunner {

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

        //code goes here

    }
}

Hoàn hảo trong Spring Boot. Nhưng phương thức run () được gọi hai lần khi có ApplicationScope cho lớp. Vì vậy, phương pháp PostConstruct với ở trên hoạt động tốt hơn.
Sam

26

ApplicationReadyEventthực sự chỉ hữu ích nếu tác vụ bạn muốn thực hiện không phải là một yêu cầu cho hoạt động máy chủ chính xác. Bắt đầu một nhiệm vụ không đồng bộ để theo dõi một cái gì đó để thay đổi là một ví dụ tốt.

Tuy nhiên nếu máy chủ của bạn đang ở trong một 'chưa sẵn sàng' nhà nước cho đến khi công việc được hoàn thành sau đó nó tốt hơn để thực hiện SmartInitializingSingletonvì bạn sẽ nhận được các cuộc gọi lại trước cổng REST của bạn đã được mở và máy chủ của bạn là mở cho doanh nghiệp.

Đừng cố sử dụng @PostConstructcho các nhiệm vụ chỉ nên xảy ra một lần. Bạn sẽ nhận được một sự ngạc nhiên thô lỗ khi bạn nhận thấy nó được gọi nhiều lần ...


Đây nên là câu trả lời được lựa chọn. Như @Andy chỉ ra, SmartInitializingSingleton được gọi ngay trước khi các cổng được mở.
Srikanth

24

Với cấu hình lò xo:

@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

Sử dụng một SmartInitializingSingletonhạt đậu vào mùa xuân> 4.1

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

}

Thay thế một CommandLineRunnerbean có thể được thực hiện hoặc chú thích một phương thức bean với @PostConstruct.


Tôi có thể yêu cầu một phụ thuộc Autowired trong phương thức đó không? Tôi muốn đặt Tiểu sử
LppEdd

7

Cung cấp một ví dụ cho câu trả lời của Dave Syer, hoạt động như một cơ duyên:

@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

Hãy thử cái này và nó sẽ chạy mã của bạn khi bối cảnh ứng dụng đã bắt đầu hoàn toàn.

 @Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {

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

6

Tôi thực sự thích đề xuất sử dụng EventListenerchú thích của @cahen ( https://stackoverflow.com/a/44923402/9122660 ) vì nó rất sạch sẽ. Thật không may, tôi không thể làm điều này để hoạt động trong một thiết lập Spring + Kotlin. Điều gì làm việc cho Kotlin là thêm lớp làm tham số phương thức:

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

Đặt nó trong lớp ứng dụng khởi động mùa xuân không ngẫu nhiên ra bên ngoài@SpringBootApplication class MyApplication { @EventListener(ApplicationReadyEvent::class) fun doSomethingAfterStartup() { println("hello world, I have just started up") } }
Ahmed na

5

Cách tốt nhất để thực thi khối mã sau khi ứng dụng Spring Boot bắt đầu là sử dụng chú thích PostConstruct. Ngoài ra, bạn cũng có thể sử dụng trình chạy dòng lệnh cho cùng.

1. Sử dụng chú thích PostConstruct

@Configuration
public class InitialDataConfiguration {

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

}

2. Sử dụng lệnh chạy dòng đậu

@Configuration
public class InitialDataConfiguration {

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

3

chỉ cần thực hiện CommandLineRunner cho ứng dụng khởi động mùa xuân. Bạn cần thực hiện phương thức chạy,

public classs SpringBootApplication implements CommandLineRunner{

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

        }
}

0

Cách tốt nhất bạn sử dụng CommandLineRunner hoặc ApplicationRunner Sự khác biệt duy nhất giữa phương thức run () CommandLineRunner chấp nhận mảng chuỗi và ApplicationRunner chấp nhận ApplicationArugument.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.