web-dev-qa-db-ja.com

アプリケーションコンテキスト初期化イベントにフックを追加する方法は?

通常のサーブレットの場合、 context listener を宣言できると思いますが、Spring MVCの場合、Springはこれを簡単にしますか?

さらに、コンテキストリスナーを定義し、servlet.xmlまたはapplicationContext.xmlで定義されたBeanにアクセスする必要がある場合、どのようにしてそれらにアクセスできますか?

62
teddy teddy

Springには、処理できるいくつかの標準イベントがあります。

そのためには、次のようなApplicationListenerインターフェースを実装するBeanを作成して登録する必要があります。

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クラスはジェネリック で、関心のあるイベントタイプを宣言できます。イベントはそれに応じてフィルターされます。次のように、Beanコードを少し単純化できます。

public class ApplicationListenerBean implements ApplicationListener<ContextRefreshedEvent> {

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

Spring 4.2以降では、@EventListenerを使用できます(- documentation

@Component
class MyClassWithEventListeners {

    @EventListener({ContextRefreshedEvent.class})
    void contextRefreshedEvent() {
        System.out.println("a context refreshed event happened");
    }
}
81
dgtc

注釈を作成する

  @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() {}
6
Adeptius

複数のデータベースからのデータを含むHashMap(私のWebページで使用)を作成していたURLを入力するのに単一ページのアプリケーションがありました。サーバーの起動時にすべてを読み込むために次のことを行いました-

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を起動すると、この大幅な変更により、startTime中にdataMapが読み込まれ、すべてがservletContextに格納されます。その後、ControllerClassが使用して、すでに読み込まれたservletContextから結果を取得します。

1
Amit Singh

アプリケーションコンテキストが読み込まれた後、つまり、アプリケーションを提供する準備が整ったら、以下の手順に従って処理を行ってください。

  1. 以下の注釈を作成します

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

2.アプリケーションの準備完了状態でコールを取得するリスナーであるBelow Classを作成します。

    @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アプリケーションを開始すると、リスナーが呼び出されます。

0
Dilip Dumania