Как добавить хук к событию инициализации контекста приложения?

Думаю, для обычного сервлета вы могли бы объявить прослушиватель контекста, но для Spring MVC сделает ли Spring это проще?

Кроме того, если я определю прослушиватель контекста, а затем мне потребуется доступ к bean-компонентам, определенным в моем servlet.xml или applicationContext.xml, как я могу получить к ним доступ?


person teddy teddy    schedule 31.12.2011    source источник


Ответы (5)


Spring имеет некоторые стандартные события, с которыми вы можете справиться. < / strong>

Для этого вы должны создать и зарегистрировать bean-компонент, реализующий интерфейс ApplicationListener, примерно так:

package test.pack.age;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

public class ApplicationListenerBean implements ApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            ApplicationContext applicationContext = ((ContextRefreshedEvent) event).getApplicationContext();
            // now you can do applicationContext.getBean(...)
            // ...
        }
    }
}

Затем вы регистрируете этот bean-компонент в своем servlet.xml или applicationContext.xml файле:

<bean id="eventListenerBean" class="test.pack.age.ApplicationListenerBean" />

и Spring уведомит его при инициализации контекста приложения.

В Spring 3 (если вы используете эту версию) ApplicationListener класс является общим, и вы можете объявить интересующий вас тип события, и событие будет соответствующим образом отфильтровано. Вы можете немного упростить код вашего компонента следующим образом:

public class ApplicationListenerBean implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        // now you can do applicationContext.getBean(...)
        // ...
    }
}
person Bogdan    schedule 31.12.2011
comment
хорошо спасибо . хорошо знать, что spring3 фильтрует события. Раньше я замечал класс applicationlistener. но его хуки также будут вызываться для RequestHandledEvent. - person teddy teddy; 11.01.2012
comment
Есть идеи, что произойдет, если вы воспользуетесь аннотациями и объявите два класса? Без аннотации (XML) и два? Будут ли они стрелять в заявленном порядке? Благодарность ;) - person mmm; 12.06.2012
comment
Просто для информации, событие для начала контекста - это ContextStartedEvent. Документы: - docs.spring.io/spring/docs/3.2.x/javadoc-api/org/ - person Kumar Sambhav; 12.09.2013
comment
@Kumar Sambhav: это правильно, но следует также упомянуть о различиях. См. Здесь: stackoverflow.com/questions/5728376 / и здесь: форум. spring.io/forum/spring-projects/container/ - person Bogdan; 15.09.2013
comment
из документации Spring: Начиная с Spring 3.0, ApplicationListener может в общем объявлять интересующий его тип события. При регистрации в Spring ApplicationContext события будут соответствующим образом отфильтрованы, при этом слушатель будет вызываться только для сопоставления объектов событий. следовательно, вы можете заменить проверку instanceof реализацией ApplicationListener<ContextRefreshedEvent> - person Ahmad Y. Saleh; 11.12.2013
comment
Да должен быть реализован ApplicationListener ‹ContextRefreshedEvent› - person tgkprog; 30.05.2019
comment
@Component необходимо добавить над классом - person Daud; 23.08.2019

Начиная с Spring 4.2 вы можете использовать @EventListener (документация)

@Component
class MyClassWithEventListeners {

    @EventListener({ContextRefreshedEvent.class})
    void contextRefreshedEvent() {
        System.out.println("a context refreshed event happened");
    }
}
person David Groomes    schedule 27.10.2015
comment
как мне распечатать свойства и т. д., этот метод, кажется, не принимает никаких параметров? - person Kalpesh Soni; 02.03.2018
comment
Просто добавьте ContextRefreshedEvent в качестве аргумента вместо параметра аннотации - person Gwaptiva; 23.12.2019

Создайте аннотацию

  @Retention(RetentionPolicy.RUNTIME)
    public @interface AfterSpringLoadComplete {
    }

Создать класс

    public class PostProxyInvokerContextListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    ConfigurableListableBeanFactory factory;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            try {
                BeanDefinition definition = factory.getBeanDefinition(name);
                String originalClassName = definition.getBeanClassName();
                Class<?> originalClass = Class.forName(originalClassName);
                Method[] methods = originalClass.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(AfterSpringLoadComplete.class)){
                        Object bean = context.getBean(name);
                        Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
                        currentMethod.invoke(bean);
                    }
                }
            } catch (Exception ignored) {
            }
        }
    }
}

Зарегистрируйте этот класс с помощью аннотации @Component или в xml

<bean class="ua.adeptius.PostProxyInvokerContextListener"/>

и используйте аннотацию, где вы хотите использовать любой метод, который вы хотите запустить после инициализации контекста, например:

   @AfterSpringLoadComplete
    public void init() {}
person Adeptius    schedule 03.08.2017

У меня было одностраничное приложение при вводе URL-адреса, которое создавало HashMap (используемое моей веб-страницей), которое содержало данные из нескольких баз данных. Я сделал следующие вещи, чтобы загрузить все во время запуска сервера -

1- Создан ContextListenerClass

public class MyAppContextListener implements ServletContextListener
    @Autowired

    private  MyDataProviderBean myDataProviderBean; 

    public MyDataProviderBean getMyDataProviderBean() {

        return MyDataProviderBean;

    }

    public void setMyDataProviderBean(MyDataProviderBean MyDataProviderBean) {

        this.myDataProviderBean = MyDataProviderBean;

    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {

        System.out.println("ServletContextListener destroyed");

    }


    @Override

    public void contextInitialized(ServletContextEvent context) {

        System.out.println("ServletContextListener started");

        ServletContext sc = context.getServletContext();

        WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(sc);

        MyDataProviderBean MyDataProviderBean = (MyDataProviderBean)springContext.getBean("myDataProviderBean");

        Map<String, Object> myDataMap = MyDataProviderBean.getDataMap();

        sc.setAttribute("myMap", myDataMap);

    }

2- Добавлена ​​ниже запись в web.xml

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> 
<listener>
    <listener-class>com.context.listener.MyAppContextListener</listener-class>
</listener>

3- В моем классе контроллера обновлен код, чтобы сначала проверить карту в servletContext

    @RequestMapping(value = "/index", method = RequestMethod.GET)
        public String index(@ModelAttribute("model") ModelMap model) {

            Map<String, Object> myDataMap = new HashMap<String, Object>();
            if (context != null && context.getAttribute("myMap")!=null)
            {

                myDataMap=(Map<String, Object>)context.getAttribute("myMap");
            }

            else
            {

                myDataMap = myDataProviderBean.getDataMap();
            }

            for (String key : myDataMap.keySet())
            {
                model.addAttribute(key, myDataMap.get(key));
            }
            return "myWebPage";

        }

С этим большим изменением, когда я запускаю свой tomcat, он загружает dataMap во время startTime и помещает все в servletContext, который затем используется классом контроллера для получения результатов из уже заполненного servletContext.

person Amit Singh    schedule 20.04.2016

Выполните следующий шаг, чтобы выполнить некоторую обработку после загрузки контекста приложения, т. Е. Приложение готово к работе.

  1. Создайте аннотацию ниже, т.е.

    @Retention (RetentionPolicy.RUNTIME) @Target (value = {ElementType.METHOD, ElementType.TYPE}) public @interface AfterApplicationReady {}

2.Создайте ниже класс, который является слушателем, который получает вызов в состоянии готовности приложения.

    @Component
    public class PostApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {

    public static final Logger LOGGER = LoggerFactory.getLogger(PostApplicationReadyListener.class);
    public static final String MODULE = PostApplicationReadyListener.class.getSimpleName();

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        try {
            ApplicationContext context = event.getApplicationContext();
            String[] beans = context.getBeanNamesForAnnotation(AfterAppStarted.class);

            LOGGER.info("bean found with AfterAppStarted annotation are : {}", Arrays.toString(beans));

            for (String beanName : beans) {
                Object bean = context.getBean(beanName);
                Class<?> targetClass = AopUtils.getTargetClass(bean);
                Method[] methods = targetClass.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(AfterAppStartedComplete.class)) {

                        LOGGER.info("Method:[{} of Bean:{}] found with AfterAppStartedComplete Annotation.", method.getName(), beanName);

                        Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());

                        LOGGER.info("Going to invoke method:{} of bean:{}", method.getName(), beanName);

                        currentMethod.invoke(bean);

                        LOGGER.info("Invocation compeleted method:{} of bean:{}", method.getName(), beanName);
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.warn("Exception occured : ", e);
        }
    }
}

Наконец, когда вы запускаете приложение Spring непосредственно перед запуском приложения в журнале, будет вызван ваш слушатель.

person Dilip    schedule 04.07.2018