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


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.