2010-03-08 48 views
144

Có tính năng Spring 3 nào để thực hiện một số phương thức khi ứng dụng bắt đầu lần đầu tiên không? Tôi biết rằng tôi có thể làm thủ thuật thiết lập một phương thức với chú giải @Scheduled và nó thực hiện ngay sau khi khởi động, nhưng sau đó nó sẽ thực thi định kỳ.Phương thức thực thi khi khởi động vào mùa xuân

Cảm ơn.

+1

mẹo là gì với @Scheduled? đó là chính xác những gì tôi muốn! – chrismarx

Trả lời

162

Nếu bằng "khởi động ứng dụng", bạn có nghĩa là "khởi động ngữ cảnh ứng dụng", thì có, có many ways to do this, dễ nhất (đối với đậu đơn), để chú thích phương pháp của bạn với @PostConstruct. Hãy nhìn vào các liên kết để xem các tùy chọn khác, nhưng trong bản tóm tắt đó là:

  • Phương pháp chú thích với @PostConstruct
  • afterPropertiesSet() theo quy định của giao diện InitializingBean callback
  • Một tùy chỉnh cấu hình init() phương pháp

Về mặt kỹ thuật, đây là các móc vào vòng đời bean, thay vì vòng đời ngữ cảnh, nhưng trong 99% trường hợp, hai trường hợp tương đương nhau.

Nếu bạn cần móc cụ thể vào bối cảnh khởi động/tắt máy, khi đó bạn có thể implement the Lifecycle interface thay thế, nhưng điều đó có thể không cần thiết.

+6

Tôi chưa thấy triển khai vòng đời hoặc SmartLifecycle sau một chút nghiên cứu. Tôi biết đây là một năm tuổi, nhưng skaffman nếu bạn có bất cứ điều gì bạn có thể đăng bài mà sẽ được nhiều đánh giá cao. –

+3

Các phương thức trên được gọi trước khi toàn bộ bối cảnh ứng dụng đã được tạo (ví dụ:/trước/giao dịch phân giới cắm mốc đã được thiết lập). –

+0

Tôi nhận được một cảnh báo lạ khi cố gắng sử dụng @PostConstruct trong java 1.8: 'Giới hạn truy cập: Loại PostConstruct không thể truy cập do hạn chế trên thư viện yêu cầu /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/jre/lib/rt.jar' – encrest

80

Điều này có thể dễ dàng thực hiện với một số ApplicationListener. Tôi có này để làm việc nghe 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 
    } 
} 

nghe Ứng dụng chạy đồng bộ trong mùa xuân. Nếu bạn muốn chắc chắn rằng mã của bạn chỉ được thực hiện một lần, chỉ cần giữ một số trạng thái trong thành phần của bạn.

CẬP NHẬT

Bắt đầu với mùa xuân 4.2 trở lên, bạn cũng có thể sử dụng @EventListener chú thích để quan sát ContextRefreshedEvent (nhờ @bphilipnyc cho trỏ này ra):

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

Điều này cũng làm việc cho tôi - hoàn hảo cho việc khởi tạo một lần không phải là bean. –

+8

N.B. đối với những người bị cám dỗ sử dụng 'ContextStartedEvent' thay vào đó, sẽ khó thêm người nghe trước khi sự kiện xảy ra. – OrangeDog

+1

Làm thế nào để gọi một @Autowired JPA repositopry thành sự kiện? kho lưu trữ là null. –

7

gì chúng ta đã làm được mở rộng org.springframework.web.context.ContextLoaderListener để in nội dung nào đó khi bối cảnh bắt đầu.

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

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

Cấu hình lớp con sau đó trong web.xml:

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

Đối với người dùng Java 1.8 đang nhận được cảnh báo khi cố gắng tham chiếu @PostC onstruct chú thích, tôi đã kết thúc thay vì piggybacking tắt chú thích @ Scheduled mà bạn có thể làm gì nếu bạn đã có một công việc @ Scheduled với fixedRate hoặc 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

xem thêm http://stackoverflow.com/questions/3564361/scheduling-tasks-to-run-once-using-the-spring-task-namespace – Joram

8

Nếu bạn đang sử dụng khởi động mùa xuân, đây là câu trả lời hay nhất.

Tôi cảm thấy rằng @PostConstruct và các vòng lặp khác nhau trong vòng đời là các vòng tròn. Chúng có thể dẫn trực tiếp đến các vấn đề thời gian chạy hoặc gây ra ít lỗi hơn so với các sự kiện vòng đời bean/context bất ngờ. Tại sao không chỉ trực tiếp gọi bean của bạn bằng cách sử dụng Java thuần túy? Bạn vẫn gọi bean là 'spring way' (ví dụ: thông qua proxy AoP mùa xuân). Và tốt nhất của tất cả, đó là java đơn giản, không thể có được bất kỳ đơn giản hơn thế. Không cần cho người nghe ngữ cảnh hoặc lập lịch kỳ lạ.

@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

Đây là một ý tưởng hay nói chung nhưng khi bắt đầu bối cảnh ứng dụng mùa xuân của bạn từ một thử nghiệm tích hợp, chính là không bao giờ chạy! –

+0

@ JonasGeiregat: Ngoài ra, còn có các trường hợp khác không có 'chính()' nào cả, ví dụ khi sử dụng khung ứng dụng (ví dụ: JavaServer Faces). – sleske

0

Nếu bạn muốn cấu hình một bean trước khi ứng dụng của bạn đang chạy đầy đủ, bạn có thể sử dụng @Autowired:

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

Chú ý, đây chỉ đề nghị nếu phương pháp runOnceOnStartup của bạn phụ thuộc vào một đầy đủ bối cảnh mùa xuân khởi tạo. Ví dụ: bạn wan để gọi một dao với ranh giới giao dịch

Bạn cũng có thể sử dụng một phương pháp lên kế hoạch với fixedDelay thiết lập rất cao

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

này có ưu điểm là toàn bộ ứng dụng được dây lên (giao dịch, Dao, ...)

thấy trong Scheduling tasks to run once, using the Spring task namespace

+0

Tôi không thấy lợi thế nào khi sử dụng '@ PostConstruct'? –

+0

@WimDeblauwe phụ thuộc vào những gì bạn muốn làm trong dosomething() gọi một dao tự động với phân giới cắm mốc cần toàn bộ bối cảnh để được bắt đầu lên, không chỉ này đậu – Joram

+0

bạn có thể xây dựng? Phương pháp –

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

     } 
    } 
} 
+0

ApplicationReadyEvent đang khởi động vào mùa xuân không có trong mùa xuân 3 –

25

Vào mùa xuân 4.2+, bạn có thể chỉ cần thực hiện:

@Component 
class StartupHousekeeper { 

    @EventListener(ContextRefreshedEvent.class) 
    void contextRefreshedEvent() { 
     //do whatever 
    } 
} 
Các vấn đề liên quan