Чи є якась функція Spring 3 для виконання деяких методів при першому запуску програми? Я знаю, що я можу зробити трюк у встановленні методу з @Scheduled
анотацією, і він виконується відразу після запуску, але тоді він буде виконуватися періодично.
Чи є якась функція Spring 3 для виконання деяких методів при першому запуску програми? Я знаю, що я можу зробити трюк у встановленні методу з @Scheduled
анотацією, і він виконується відразу після запуску, але тоді він буде виконуватися періодично.
Відповіді:
Якщо під "запуском програми" ви маєте на увазі "запуск контексту програми", то так, є багато способів зробити це , найпростіший (для синглів однотонних бобів, як би там не було) - це анотація вашого методу @PostConstruct
. Перегляньте посилання, щоб побачити інші варіанти, але, підсумовуючи, це:
@PostConstruct
afterPropertiesSet()
як визначено InitializingBean
інтерфейсом зворотного дзвінкаТехнічно це гачки в життєвому циклі бобів , а не контекстний життєвий цикл, але в 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 для ванільної весни див. Відповідь Стефана).
Це легко зробити за допомогою 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
}
}
Слухачі додатків весняно працюють синхронно. Якщо ви хочете переконатися, що код виконується лише один раз, просто збережіть деякий стан у своєму компоненті.
ОНОВЛЕННЯ
Починаючи з весни 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
цим, важче додати слухача до початку події.
Навесні 4.2+ і тепер ви можете просто зробити:
@Component
class StartupHousekeeper {
@EventListener(ContextRefreshedEvent.class)
public void contextRefreshedEvent() {
//do whatever
}
}
Якщо ви використовуєте весняний завантаження, це найкраща відповідь.
Я відчуваю, що @PostConstruct
та інші різні заперечення життєвого циклу є неоднозначними. Це може призвести безпосередньо до проблем виконання або спричинити менш очевидні дефекти через несподівані події життєвого циклу фасолі / контексту. Чому б просто не звернутися безпосередньо до вашого квасолі за допомогою простої Java? Ви все ще викликаєте боб "весняним способом" (наприклад: через весняний проксі-сервер 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).
Користувачам Java 1.8, які отримують попередження при спробі посилання на анотацію @PostConstruct, я замість цього піггітував анотацію @Scheduled, яку ви можете зробити, якщо у вас вже є @Scheduled робота з fixRate або fixDelay.
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
метод залежить від повністю ініціалізованого весняного контексту. Наприклад: ви хочете зателефонувати в дао з розмежуванням транзакції
Ви також можете використовувати запланований метод з фіксованим встановленням дуже високим
@Scheduled(fixedDelay = Long.MAX_VALUE)
public void runOnceOnStartup() {
dosomething();
}
Це має перевагу в тому, що вся програма підключена (транзакції, дао, ...)
видно в задачах планування, які потрібно виконати один раз, використовуючи простір імен Spring завдань
@PostConstruct
?
Опублікував ще одне рішення, яке реалізує WebApplicationInitializer і викликається набагато до того, як будь-який весняний боб буде ініційований, якщо хтось має такий випадок використання
Ініціалізуйте локальний і часовий пояс за замовчуванням із налаштуваннями Spring
AppStartListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ApplicationReadyEvent){
System.out.print("ciao");
}
}
}
Якщо ви хочете налаштувати бін перед повноцінним запуском програми, ви можете використовувати @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>