9

我有一个类 InitApp

@Component
public class InitApp implements ServletContextListener {

@Autowired
ConfigrationService weatherConfService;

/** Creates a new instance of InitApp */
public InitApp() {
   }

public void contextInitialized(ServletContextEvent servletContextEvent) {
    System.out.println(weatherConfService);
   }
public void contextDestroyed(ServletContextEvent servletContextEvent) {
   }
}

和 web.xml 中的侦听器:

    <listener>
        <listener-class>com.web.Utils.InitApp</listener-class>
    </listener>

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

confService print --> null 有什么问题?

4

5 回答 5

10

当我遇到同样的问题时,我想到了几个想法。

第一个是使用 Spring utils 从侦听器中的 Spring 上下文中检索 bean:

前任:

@WebListener
public class CacheInitializationListener implements ServletContextListener {

    /**
     * Initialize the Cache Manager once the application has started
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        CacheManager cacheManager = WebApplicationContextUtils.getRequiredWebApplicationContext(
                sce.getServletContext()).getBean(CacheManager.class);
        try {
            cacheManager.init();
        } catch (Exception e) {
            // rethrow as a runtime exception
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub

    }
}

如果你只有一两个豆子,这很好用。否则会变得乏味。另一种选择是显式调用 Spring 的 Autowire 实用程序:

@WebListener
public class CacheInitializationListener implements ServletContextListener {

    @Autowired
    private CacheManager cacheManager;

    /**
     * Initialize the Cache once the application has started
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        try {
            cacheManager.init();
        } catch (Exception e) {
            // rethrow as a runtime exception
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub

    }
}

这两种解决方案的警告是,必须先加载 Spring 上下文,然后才能工作。鉴于无法使用 定义侦听器顺序@WebListener,请确保ContextLoaderListener定义Springweb.xml以强制首先加载它(Web 描述符中定义的侦听器在注释定义的侦听器之前加载)。

于 2015-06-04T14:28:20.537 回答
6

通过声明

<listener>
  <listener-class>com.web.Utils.InitApp</listener-class>
</listener>

在你的 web.xml 中,你告诉你的容器初始化并注册一个InitApp. 因此,该对象不由 Spring 管理,您无法@Autowired对其进行任何操作。

如果您的应用程序上下文设置为对com.web.Utils包进行组件扫描,那么您将拥有一个InitApp未在容器中注册为侦听器的 bean。因此,即使您的其他 bean 将是@Autowired,servlet 容器也不会命中它。

这就是权衡。

如果您对 servlet 3.0使用带有 aServletContainerInitializer或 a的编程配置,则有一些解决方法。WebApplicationInitializer您不会使用@Autowired,您只会拥有用于设置实例的 setter/getter。

这是一个例子

class SpringExample implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        WebApplicationContext context = ...;

        SomeBean someBean = context.getBean(SomeBean.class);
        CustomListener listener = new CustomListener();
        listener.setSomeBean(someBean);

        // register the listener instance
        servletContext.addListener(listener);

        // then register DispatcherServlet and ContextLoaderListener, as appropriate
        ...
    }
}

class CustomListener implements ServletContextListener {
    private SomeBean someBean;

    public SomeBean getSomeBean() {
        return someBean;
    }

    public void setSomeBean(SomeBean someBean) {
        this.someBean = someBean;
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
       // do something with someBean
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

请注意,Spring 有一些WebApplicationInitializer非常复杂的自定义实现。您真的不需要自己直接实现它。这个想法保持不变,只是在继承层次结构中更深。

于 2013-07-15T14:01:21.897 回答
4
@WebListener
public class StartupListener implements ServletContextListener {

    @Autowired
    private MyRepository repository;

    @Override
    public void contextDestroyed(ServletContextEvent event) {
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        AutowireCapableBeanFactory autowireCapableBeanFactory = WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()).getAutowireCapableBeanFactory();
        autowireCapableBeanFactory.autowireBean(this);

        repository.doSomething();
    }
}
于 2018-04-05T09:19:41.690 回答
1

正如其他人所说,此侦听器通过 web servlet(tomcat) 上下文(不是 Spring 容器)进行观察,并收到 servlet 启动/关闭的通知。

由于它是由 Spring 容器外部的 servlet 创建的,因此它不受 Spring 管理,因此不可能使用 @Autowire 成员。

如果您将 bean 设置为托管 @Component,那么 Spring 将创建实例,并且侦听器不会向外部 servlet 注册。

你不能同时拥有它..

一种解决方案是删除 Spring 注释并从 Spring Application 上下文中手动检索您的成员并以这种方式设置您的成员。

IE

    public class InitApp implements ServletContextListener {

        private ConfigrationService weatherConfService;

        private static ApplicationContext   applicationContext  = new ClassPathXmlApplicationContext("classpath:web-context.xml");

        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            weatherConfService = applicationContext.getBean(ConfigrationService.class);
            System.out.println(weatherConfService);
        }

        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
        }
    }
于 2014-12-17T11:18:18.570 回答
0

使用当前版本的 Spring Boot 2,您还可以将 Servlet、过滤器和侦听器注册为 Spring bean 并正常使用自动装配的组件:

将 Servlet、过滤器和侦听器注册为 Spring Bean

任何作为 Spring bean 的 Servlet、Filter 或 servlet *Listener 实例都会向嵌入式容器注册。如果您想在配置期间引用 application.properties 中的值,这会特别方便。

更多信息在这里注册 @WebListeners 以允许他们注册 servlet 和过滤器

这意味着您只需将您的 as 注释ServletContextListener@Comonent.

由于 Spring Boot 2.4 using@WebListener不再起作用,在发行说明中提到。

此更改的副作用是 Servlet 容器现在创建了 WebListener 的实例,因此不能再使用诸如 @Autowired 的依赖注入。在这种情况下,应该改用@Component。

于 2022-01-26T12:10:18.823 回答