用于ContextRefreshEvent的Spring ApplicationListener.如何在每个层次结构中仅调用一次? [英] Spring ApplicationListener for ContextRefreshEvent. How to invoke only once per Hierarchy?

查看:166
本文介绍了用于ContextRefreshEvent的Spring ApplicationListener.如何在每个层次结构中仅调用一次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一旦我的spring Web应用程序启动了所有的bean,我就需要执行特定的过程.为此,我创建了

I need to execute a certain procedure once my spring web application starts all of its beans. For this I created a ApplicationListener<ContextRefreshedEvent>.

但是,当我运行该应用程序时,它会被调用多次(因为我们有不同名称空间的上下文,例如mvc-servlet等),但是我需要只在一次且所有上下文都正确初始化时才调用此特定的侦听器

However, when I run the application it gets called several times (as we have contexts for different namespaces e.g. mvc-servlets, etc.) but I need this particular listener to be called only once and when all the context where properly initialized.

有没有办法实现我想要做的事情?

Is there a way to achieve what I'm trying to do?

我正在使用Spring 3.1.0.RELEASE.

I'm using spring 3.1.0.RELEASE.

推荐答案

是的,有一种方法,但是可能有些棘手.您正在讨论的子上下文可能是从DispatcherServlet开始的上下文.如果其中有多个,则每个分派器servlet将获得一个上下文.

Yes, there is a way but it can be a bit tricky. The child contexts you are talking about are probably contexts started for a DispatcherServlet. If you have more than one of those, you'll get one context per dispatcher servlet.

Spring将这些委托给容器,因此就初始化而言,没有单一的管理点.首先,初始化根应用程序上下文,然后由容器初始化各种servlet.对于其中每一个,可能会引入另一个上下文.

Spring delegates to the container for these so there is no single point of management with regards to initialization. First, the root application context is initialized and then the various servlets are initialized by the container. For each of those, another context might kick in.

幸运的是,使用load-on-startup参数

load-on-startup元素指示该servlet应该被加载 (已实例化,并在Web应用程序启动时调用了它的init()).这些的可选内容 元素必须是一个整数,指示顺序 应该加载哪个servlet.如果值 是负整数,或者元素不是 目前,容器可以自由加载servlet 只要它选择.如果该值为正 整数或0,容器必须加载并 在应用程序初始化servlet 部署.容器必须保证 标记有较低整数的servlet被加载 在标有较高整数的servlet之前.这 集装箱可以选择装载的顺序 具有相同的启动时加载值的servlet.

The load-on-startup element indicates that this servlet should be loaded (instantiated and have its init() called) on the startup of the web application. The optional contents of these element must be an integer indicating the order in which the servlet should be loaded. If the value is a negative integer, or the element is not present, the container is free to load the servlet whenever it chooses. If the value is a positive integer or 0, the container must load and initialize the servlet as the application is deployed. The container must guarantee that servlets marked with lower integers are loaded before servlets marked with higher integers. The container may choose the order of loading of servlets with the same load-on-start-up value.

因此,您应该基本上做两件事:

So you should do two things basically:

  1. 在每个servlet上指定一个load-on-startup元素,并确保其具有一个不同的较高编号
  2. 确保您的听众抓住了正确的事件
  1. Specify a load-on-startup element on each servlet and make sure that one has a distinctive, higher number
  2. Make sure that your listener catch the right event

示例

请考虑以下(简化的)web.xml定义

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

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

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>  
    <servlet>
        <servlet-name>anotherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/first/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>anotherServlet</servlet-name>
        <url-pattern>/second/*</url-pattern>
    </servlet-mapping>

</web-app>

检测正确的上下文

此设置将导致对收听者的3个呼叫.在这种情况下,anotherServlet是链中的最后一个,因此您可以按以下方式进行识别:

Detecting the right context

This setup will lead to 3 calls to the listener. In this case, anotherServlet is the last in your chain, so you can identify this as follows:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    ApplicationContext context = event.getApplicationContext();
    if (context instanceof ConfigurableWebApplicationContext) { // sanity check
        final ConfigurableWebApplicationContext ctx =
                (ConfigurableWebApplicationContext) event.getApplicationContext();
        if ("anotherServlet-servlet".equals(ctx.getNamespace())) {
            // Run your initialization business here
        }
    }
}

如果您想了解其来源,请查看FrameworkServlet#initServletBean.

If you're interested to understand where this is coming from, have a look to FrameworkServlet#initServletBean.

不是您现在仍然可以引发异常,这仍将阻止应用程序正确部署.

Not that you can still throw an exception at this point and this will still prevents the application to deploy properly.

最后,如果为该特定事件注册了多个侦听器,您还可以确保最后处理您的事件.为此,您需要实现Ordered接口:

Finally, you can also make sure that your event is processed last in case there are multiple listeners registered for that particular event. To do this, you need to implement the Ordered interface:

public class YourListener implements ApplicationListener<ContextRefreshedEvent>, Ordered {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) { }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    } 
}

这篇关于用于ContextRefreshEvent的Spring ApplicationListener.如何在每个层次结构中仅调用一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆