Конфигурация tomcat вызывает запуск 2 копий @Service

У меня есть 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" />

Откуда вы знаете, что это связано с конфигурацией tomcat? Не могли бы вы добавить код для вашего сервиса? Также, если возможно, воспроизводимый репозиторий GitHub был бы хорош. Спасибо.

s7vr 15.07.2021 01:41

Я распечатываю материалы в Сервисе. когда я прокомментирую либо applicationContext.xml, останется 1 bean-компонент.

theAnonymous 15.07.2021 01:47
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
2
303
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Похоже, вы дважды инициализировали контекст приложения. Во-первых, 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 18.07.2021 22:43

Привет @theAnonymous, большое спасибо за отзыв. Вероятно, это потому, что любые beans-зависимости. Выдает ли приложение какие-то ошибки в логах?

jccampanero 18.07.2021 23:14

@theAnonymous Проблема, о которой вы сообщили, может быть связана с тем, что Spring не может найти ваши контроллеры, когда вы удаляете <import resource = "applicationContext.xml" /> из servlet-context.xml,: пожалуйста, убедитесь, что вы включили component-scan, чтобы указать на пакеты, в которых ваши контроллеры определены в servlet-context.xml: <context:component-scan base-package = "my.package.controllers" />. Я также обновил ответ. Я надеюсь, что это помогает.

jccampanero 18.07.2021 23:26

Другие вопросы по теме