Виконати метод при запуску навесні


176

Чи є якась функція Spring 3 для виконання деяких методів при першому запуску програми? Я знаю, що я можу зробити трюк у встановленні методу з @Scheduledанотацією, і він виконується відразу після запуску, але тоді він буде виконуватися періодично.


1
у чому трюк з @Scheduled? це саме те, що я хочу!
— chrismarx

Відповіді:


185

Якщо під "запуском програми" ви маєте на увазі "запуск контексту програми", то так, є багато способів зробити це , найпростіший (для синглів однотонних бобів, як би там не було) - це анотація вашого методу @PostConstruct. Перегляньте посилання, щоб побачити інші варіанти, але, підсумовуючи, це:

  • Способи, позначені с @PostConstruct
  • afterPropertiesSet()як визначено InitializingBeanінтерфейсом зворотного дзвінка
  • Нестандартний налаштований метод init ()

Технічно це гачки в життєвому циклі бобів , а не контекстний життєвий цикл, але в 99% випадків ці два рівні.

Якщо вам потрібно підключити конкретно до запуску / відключення контексту, тоді ви можете натомість реалізувати Lifecycleінтерфейс , але це, мабуть, непотрібно.


7
Мені ще не доводилося бачити реалізацію життєвого циклу або SmartLifecycle після досить невеликих досліджень. Я знаю, що це рік, але скафман, якщо у вас є щось, що ви можете опублікувати, це було б дуже вдячно.

4
Вищеописані способи викликаються до того, як буде створено весь контекст програми (наприклад, / до / розмежування транзакції).
— Ганс Вестербек

Я отримую дивне попередження, намагаючись використовувати @PostConstruct в java 1.8: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
— encrest

2
Є важливі випадки, коли життєвий цикл квасолі та контексту дуже різняться. Як зазначає @HansWesterbeek, квасоля може бути налаштована до того, як контекст, від якого залежить, повністю готовий. У моїй ситуації квасоля залежала від JMS - вона була повністю побудована, тому її @PostConstructметод називали, але інфраструктура JMS, від якої опосередковано залежала, ще не була повністю задіяна (і, будучи весною, все просто мовчки не вдалося). Про перехід на @EventListener(ApplicationReadyEvent.class)все, що працювало ( ApplicationReadyEventспецифічний Spring Boot для ванільної весни див. Відповідь Стефана).
— Джордж Хокінс

@Skaffman: що робити, якщо на мою квасолю не посилається жодна квасоля, і я хочу ініціалізувати квасоля, не використовуючи її ніде
— Сагар Хараб

104

Це легко зробити за допомогою 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 
  }
}

1
Це працювало і для мене - ідеально підходило для разової ініціалізації.
— Рорі Хантер

9
Зауважте, що для тих, хто спокусився скористатися ContextStartedEventцим, важче додати слухача до початку події.
— OrangeDog

2
Як викликати репозитопію @Autowired JPA у події? сховище є нульовим.
— e-info128

Не працює для мене. Я використовую spring mvc 3. Цей mehod onApplicationEvent (___) не викликається при запуску програми. Будь-яка допомога. Ось мій код @Component публічного класу AppStartListener реалізує ApplicationListener <ContextRefreshedEvent> {public void onApplicationEvent (остаточна подія ContextRefreshedEvent) {System.out.println ("\ n \ n \ nЗамість події програми"); }}
— Vishwas Tiagi

@VishwasTyagi Як запустити контейнер? Ви впевнені, що ваш AppStartListener є частиною сканування компонентів?
— Стефан Хаберль

38

Навесні 4.2+ і тепер ви можете просто зробити:

@Component
class StartupHousekeeper {

    @EventListener(ContextRefreshedEvent.class)
    public void contextRefreshedEvent() {
        //do whatever
    }
}

Чи гарантується, що цей слухач викликає лише один раз після запуску?
— gstackoverflow

Ні, дивіться мою відповідь вище. Тримайте деякий стан свого слухача, щоб перевірити, чи виконується він вперше
— Стефан Хаберль

13

Якщо ви використовуєте весняний завантаження, це найкраща відповідь.

Я відчуваю, що @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();
    }
}

5
Це взагалі гарна ідея, але коли ви запускаєте свій весняний контекст програми з тесту на інтеграцію, головне ніколи не запускається!
— Jonas Geiregat

@JonasGeiregat: Крім того, існують і інші сценарії, де їх взагалі немає main(), наприклад, при використанні рамки програми (наприклад, обличчя JavaServer).
— sleske

9

Користувачам 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");
    }

}

дивіться також stackoverflow.com/questions/3564361/…
— Joram

7

Ми зробили те, 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>

7

За допомогою SpringBoot ми можемо виконати метод при запуску за допомогою @EventListenerанотації

@Component
public class LoadDataOnStartUp
{   
    @EventListener(ApplicationReadyEvent.class)
    public void loadData()
    {
        // do something
    }
}

4

Увага, це рекомендується лише в тому випадку, якщо ваш runOnceOnStartupметод залежить від повністю ініціалізованого весняного контексту. Наприклад: ви хочете зателефонувати в дао з розмежуванням транзакції

Ви також можете використовувати запланований метод з фіксованим встановленням дуже високим

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

Це має перевагу в тому, що вся програма підключена (транзакції, дао, ...)

видно в задачах планування, які потрібно виконати один раз, використовуючи простір імен Spring завдань


Я не бачу жодної переваги перед використанням @PostConstruct?
— Вім Деблауве

@WimDeblauwe залежить від того, що ви хочете зробити в dosomething () виклику Autowired дао з розмежуванням Trasaction потрібно запустити весь контекст, а не лише цей боб
— Joram

5
@WimDeblauwe '@PostConstruct' метод спрацьовує, коли ініціалізується боб, весь контекст може бути не готовий (fe Управління транзакціями)
— Joram

Це більш елегантний imo, ніж пост-конструкція чи будь-які інтерфейси чи події
— aliopi

1

Опублікував ще одне рішення, яке реалізує WebApplicationInitializer і викликається набагато до того, як будь-який весняний боб буде ініційований, якщо хтось має такий випадок використання

Ініціалізуйте локальний і часовий пояс за замовчуванням із налаштуваннями Spring


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

        }
    }
}

2
ApplicationReadyEvent у весняному завантаженні не навесні 3
— Джон Мерсьє

0

Якщо ви хочете налаштувати бін перед повноцінним запуском програми, ви можете використовувати @Autowired:

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

0

Ви можете використовувати @EventListenerдля свого компонента, який буде викликаний після запуску сервера та ініціалізації всіх бобів.

@EventListener
public void onApplicationEvent(ContextClosedEvent event) {

}

0

Для файлу, що 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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.