มีฟีเจอร์ Spring 3 ใดบ้างที่จะดำเนินการวิธีการบางอย่างเมื่อแอปพลิเคชันเริ่มต้นเป็นครั้งแรก? ฉันรู้ว่าฉันสามารถทำเคล็ดลับในการตั้งค่าวิธีการด้วย@Scheduled
คำอธิบายประกอบและจะดำเนินการหลังจากเริ่มต้น แต่ก็จะดำเนินการเป็นระยะ
มีฟีเจอร์ Spring 3 ใดบ้างที่จะดำเนินการวิธีการบางอย่างเมื่อแอปพลิเคชันเริ่มต้นเป็นครั้งแรก? ฉันรู้ว่าฉันสามารถทำเคล็ดลับในการตั้งค่าวิธีการด้วย@Scheduled
คำอธิบายประกอบและจะดำเนินการหลังจากเริ่มต้น แต่ก็จะดำเนินการเป็นระยะ
คำตอบ:
ถ้าโดย "เริ่มต้นโปรแกรมประยุกต์" คุณหมายถึง "การเริ่มต้นบริบทแอพลิเคชัน" จากนั้นใช่มีหลายวิธีที่จะทำเช่นนี้ที่ง่ายที่สุด (สำหรับถั่ว singletons แล้ว) @PostConstruct
เป็นอธิบายวิธีการของคุณด้วย ลองดูที่ลิงค์เพื่อดูตัวเลือกอื่น ๆ แต่สรุปได้ว่า:
@PostConstruct
afterPropertiesSet()
ตามที่กำหนดโดยInitializingBean
อินเตอร์เฟสการติดต่อกลับในทางเทคนิคแล้วสิ่งเหล่านี้คือ hooks ลงในlifecycle ของถั่วแทนที่จะเป็น lifecycle บริบท แต่ใน 99% ของทั้งสองกรณีนั้นเทียบเท่ากัน
หากคุณต้องการเชื่อมโยงกับการLifecycle
เริ่มต้น / ปิดบริบทโดยเฉพาะคุณสามารถใช้อินเทอร์เฟซแทนได้ แต่นั่นอาจไม่จำเป็น
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
@PostConstruct
วิธีการของมันจึงถูกเรียก แต่ JMS โครงสร้างพื้นฐานที่ขึ้นอยู่กับทางอ้อมนั้นยังไม่ได้เชื่อมต่ออย่างเต็มรูปแบบ เมื่อเปลี่ยนไปใช้@EventListener(ApplicationReadyEvent.class)
ทุกอย่างที่ทำงาน ( ApplicationReadyEvent
เป็น Spring Boot เฉพาะสำหรับวานิลลาสปริงดูคำตอบของ Stefan)
ApplicationListener
นี้จะกระทำได้อย่างง่ายดายด้วยการ ฉันได้สิ่งนี้เพื่อฟังการทำงานของ 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
}
}
แอปพลิเคชั่นตัวรับฟังทำงานพร้อมกันใน Spring หากคุณต้องการตรวจสอบให้แน่ใจว่าคุณใช้รหัสเพียงครั้งเดียวให้คงสถานะไว้ในองค์ประกอบ
UPDATE
เริ่มต้นด้วย Spring 4.2+ คุณสามารถใช้@EventListener
คำอธิบายประกอบเพื่อสังเกตContextRefreshedEvent
(ขอบคุณ@bphilipnycสำหรับการชี้เรื่องนี้):
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
}
}
ContextStartedEvent
แทนมันเป็นการยากที่จะเพิ่มผู้ฟังก่อนที่เหตุการณ์จะเริ่ม
ใน Spring 4.2+ ตอนนี้คุณสามารถทำได้ง่ายๆ
@Component
class StartupHousekeeper {
@EventListener(ContextRefreshedEvent.class)
public void contextRefreshedEvent() {
//do whatever
}
}
หากคุณใช้สปริงบูตนี่คือคำตอบที่ดีที่สุด
ฉันรู้สึกว่า@PostConstruct
และคำอุทานต่าง ๆ ของวงจรชีวิตอื่น ๆ เป็นวิธีรอบด้าน สิ่งเหล่านี้สามารถนำไปสู่ปัญหาโดยตรงของรันไทม์หรือทำให้เกิดข้อบกพร่องน้อยกว่าที่เห็นได้ชัดเนื่องจากเหตุการณ์รอบการทำงานของ bean / บริบทที่ไม่คาดคิด ทำไมไม่ลองใช้ bean ของคุณโดยตรงด้วย Java ธรรมดา คุณยังคงเรียกใช้ถั่วว่า 'spring way' (เช่นผ่านพร็อกซี AoP ของสปริง) และที่ดีที่สุดคือ java ธรรมดาไม่สามารถทำได้ง่ายกว่านั้น ไม่จำเป็นสำหรับผู้ฟังตามบริบทหรือตัวกำหนดเวลาแปลก ๆ
@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();
}
}
main()
เลยตัวอย่างเช่นเมื่อใช้เฟรมเวิร์กแอปพลิเคชัน (เช่น JavaServer Faces)
สำหรับผู้ใช้ Java 1.8 ที่ได้รับคำเตือนเมื่อพยายามอ้างอิงคำอธิบายประกอบ @PostConstruct ฉันลงเอยด้วยการแทนที่คำอธิบายประกอบ @Scheduled ซึ่งคุณสามารถทำได้หากคุณมีงาน @Scheduled ที่ fixedRate หรือ 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");
}
}
สิ่งที่เราทำคือการขยายorg.springframework.web.context.ContextLoaderListener
การพิมพ์บางอย่างเมื่อบริบทเริ่มต้นขึ้น
public class ContextLoaderListener extends org.springframework.web.context.ContextLoaderListener
{
private static final Logger logger = LoggerFactory.getLogger( ContextLoaderListener.class );
public ContextLoaderListener()
{
logger.info( "Starting application..." );
}
}
กำหนดค่าคลาสย่อยจากนั้นในweb.xml
:
<listener>
<listener-class>
com.mycomp.myapp.web.context.ContextLoaderListener
</listener-class>
</listener>
ด้วย SpringBoot เราสามารถดำเนินการวิธีการในการเริ่มต้นผ่าน@EventListener
คำอธิบายประกอบ
@Component
public class LoadDataOnStartUp
{
@EventListener(ApplicationReadyEvent.class)
public void loadData()
{
// do something
}
}
ข้อควรระวังนี่จะแนะนำก็ต่อเมื่อ
runOnceOnStartup
วิธีการของคุณขึ้นอยู่กับบริบทสปริงที่เริ่มต้นอย่างสมบูรณ์ ตัวอย่างเช่น: คุณต้องการเรียก dao ด้วยการแบ่งเขตธุรกรรม
นอกจากนี้คุณยังสามารถใช้วิธีการตั้งเวลากับ fixedDelay ตั้งค่าสูงมาก
@Scheduled(fixedDelay = Long.MAX_VALUE)
public void runOnceOnStartup() {
dosomething();
}
นี่เป็นข้อดีที่แอปพลิเคชันทั้งหมดเชื่อมต่อกัน (ธุรกรรม, Dao, ... )
เห็นได้ในการจัดตารางงานให้ทำงานครั้งเดียวโดยใช้เนมสเปซ Spring
@PostConstruct
?
โพสต์โซลูชันอื่นที่ใช้ WebApplicationInitializer และเรียกว่ามากก่อนสปริงถั่วใด ๆ จะถูกยกตัวอย่างในกรณีที่มีคนใช้กรณีที่
เริ่มต้นสถานที่เริ่มต้นและเขตเวลาเริ่มต้นด้วยการกำหนดค่าสปริง
AppStartListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ApplicationReadyEvent){
System.out.print("ciao");
}
}
}
หากคุณต้องการกำหนดค่า bean ก่อนที่แอปพลิเคชันของคุณจะทำงานเต็มที่คุณสามารถใช้@Autowired
:
@Autowired
private void configureBean(MyBean: bean) {
bean.setConfiguration(myConfiguration);
}
คุณสามารถใช้@EventListener
กับส่วนประกอบของคุณซึ่งจะถูกเรียกใช้หลังจากเซิร์ฟเวอร์เริ่มทำงานและถั่วทั้งหมดเริ่มต้นได้
@EventListener
public void onApplicationEvent(ContextClosedEvent event) {
}
สำหรับไฟล์ที่StartupHousekeeper.java
อยู่ในแพคเกจcom.app.startup
,
ทำสิ่งนี้ในStartupHousekeeper.java
:
@Component
public class StartupHousekeeper {
@EventListener(ContextRefreshedEvent.class)
public void keepHouse() {
System.out.println("This prints at startup.");
}
}
และทำสิ่งนี้ในmyDispatcher-servlet.java
:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<mvc:annotation-driven />
<context:component-scan base-package="com.app.startup" />
</beans>