У меня есть ScheduledExecutor
, который работает на @PostConstruct
некоторых моих @Service
. однако эта конфигурация tomcat вызывает запуск 2 копий @Service
, и удаление любой из них приведет к тому, что @Controller
в войнах не будет работать.
как это решить? мне нужно, чтобы @Service
был одноэлементным, чтобы был только один Executor
, который запускает какой-то код.
установка Executor
на static
сделает его только одним Executor
, но есть ли способ добиться этого на уровне конфигурации?
веб.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
servlet-context.xml
<import resource = "applicationContext.xml" />
applicationContext.xml
<bean id = "properties" class = "my.package.common.properties.MyPropertyPlaceholderConfigurer">
<property name = "locations">
<list>
<value>WEB-INF/classes/system.properties</value>
<value>WEB-INF/classes/db.properties</value>
<value>WEB-INF/classes/applicationContext.properties</value>
</list>
</property>
</bean>
<context:annotation-config />
<context:component-scan base-package = "my.package" />
Я распечатываю материалы в Сервисе. когда я прокомментирую либо applicationContext.xml, останется 1 bean-компонент.
Похоже, вы дважды инициализировали контекст приложения.
Во-первых, org.springframework.web.servlet.DispatcherServlet
сервлет создает свой собственный внутренний контекст веб-приложения, вы можете проверить это в java-документах.
/**
* Create a new {@code DispatcherServlet} that will create its own internal web
* application context based on defaults and values provided through servlet
* init-params. Typically used in Servlet 2.5 or earlier environments, where the only
* option for servlet registration is through {@code web.xml} which requires the use
* of a no-arg constructor.
* ...
*/
public DispatcherServlet() {
super();
setDispatchOptionsRequest(true);
}
И вторая инициализация обрабатывается следующей конфигурацией:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Я думаю, что у вас есть ContextLoaderListener
в web.xml
, как в примере выше.
В качестве решения вы можете удалить параметры контекста для DispatcherServlet
.
Проблема, скорее всего, связана со следующим импортом в ваш servlet-context.xml
:
<import resource = "applicationContext.xml" />
Вероятно, в вашем файле web.xml
вы включаете следующий прослушиватель контекста веб-приложения:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
В типичном приложении Spring MVC этот прослушиватель отвечает за инициализацию контекста корневого приложения Spring на основе информации, предоставленной в файлах XML, указанных в параметре контекста веб-приложения contextConfigLocation
:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
С другой стороны, вы инициируете контекст второго дочернего приложения при настройке Spring DispatcherServlet
:
<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
В вашем примере этот второй контекст основан на конфигурации, представленной в servlet-context.xml
.
Очень важно понимать, что оба контекста связаны таким образом, что все bean-компоненты, определенные в корневом контексте приложения, доступны для дочернего контекста, связанного с DispatcherServlet
(обратное неверно).
Это позволяет организовать и обеспечить правильную иерархию для ваших bean-компонентов: например, контекст корневого приложения обычно определяет службы и общие компоненты и конфигурацию (база данных, безопасность, поддержка асинхронности, кеш и т. д.), тогда как дочерний контекст определяет bean-компоненты, связанные с MVC. вещи.
Эта контекстная иерархия очень хорошо объяснена в документации Spring:
Как вы сказали, когда вы включаете следующее import
в свой servlet-context.xml
файл:
<import resource = "applicationContext.xml" />
на самом деле вы дублируете все, что определено в корневом контексте.
Чтобы решить проблему, пожалуйста, удалите эту строку из servlet-context.xml
.
Согласно вашим комментариям, после этого все ваши контроллеры сообщают об ошибке 404
. Вероятно, причина в том, что после удаления applicationContext.xml
Spring не может найти Controller
: пожалуйста, убедитесь, что вы также включили component-scan
в servlet-context.xml
, указав на пакеты, в которых определены ваши контроллеры. В servlet-context.xml
входят:
<context:component-scan base-package = "my.package.controllers" />`
Включите таким образом в servlet-context.xml
любой другой ресурс MVC, который вам нужен (преобразователи представлений и т. д.).
В идеале в applicationContext.xml
необходимо сканировать только такие компоненты, как сервисы, репозитории и тому подобное.
после удаления конфигурации <import resource = "applicationContext.xml" />
из servlet-context.xml все конечные точки возвращают 404.
Привет @theAnonymous, большое спасибо за отзыв. Вероятно, это потому, что любые beans-зависимости. Выдает ли приложение какие-то ошибки в логах?
@theAnonymous Проблема, о которой вы сообщили, может быть связана с тем, что Spring не может найти ваши контроллеры, когда вы удаляете <import resource = "applicationContext.xml" />
из servlet-context.xml,
: пожалуйста, убедитесь, что вы включили component-scan
, чтобы указать на пакеты, в которых ваши контроллеры определены в servlet-context.xml
: <context:component-scan base-package = "my.package.controllers" />
. Я также обновил ответ. Я надеюсь, что это помогает.
Откуда вы знаете, что это связано с конфигурацией tomcat? Не могли бы вы добавить код для вашего сервиса? Также, если возможно, воспроизводимый репозиторий GitHub был бы хорош. Спасибо.