← Назад к вопросам

Почему при наличии контроллера в контексте DispatcherServlet не сможет найти его?

1.3 Junior🔥 201 комментариев
#ORM и Hibernate#Spring Boot и Spring Data#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

DispatcherServlet и контексты Spring: проблема с поиском контроллеров

Это типичная ошибка при конфигурации Spring MVC. Проблема кроется в иерархии контекстов Spring и том, где регистрируются Bean. Давайте разберём механизм и причины.

Иерархия контекстов в Spring MVC

Spring MVC использует два отдельных контекста приложения:

┌─────────────────────────────────────────┐
│    Root Application Context             │
│  (создаётся первым, родительский)      │
│                                         │
│  - Services (@Component, @Service)     │
│  - Repositories (@Repository)          │
│  - Config Beans                        │
│  - Бизнес-логика                       │
└─────────────────────────────────────────┘
            ↓ parent
┌─────────────────────────────────────────┐
│  DispatcherServlet Context              │
│  (дочерний контекст)                    │
│                                         │
│  - Controllers (@Controller)            │
│  - ViewResolvers                        │
│  - HandlerMapping                       │
│  - Может видеть родительский контекст  │
└─────────────────────────────────────────┘

Проблемная конфигурация

Ошибка 1: Контроллер зарегистрирован в Root Context

// web.xml
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <!-- Это пусто или конфигурирует DispatcherServlet контекст -->
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </init-param>
</servlet>
<!-- applicationContext.xml (Root Context) -->
<beans>
    <!-- ПРОБЛЕМА: контроллер здесь! -->
    <bean class="com.example.UserController"/>
    
    <!-- Правильно: сервисы и бизнес-логика -->
    <bean class="com.example.UserService"/>
</beans>
<!-- dispatcher-servlet.xml (DispatcherServlet Context) -->
<beans>
    <!-- Пусто или другие MVC компоненты -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>

Результат: DispatcherServlet не найдёт контроллер!

Почему это происходит?

Механизм поиска контроллеров в DispatcherServlet:

public class DispatcherServlet extends FrameworkServlet {
    
    private HandlerMapping[] handlerMappings;
    
    protected void initHandlerMappings(ApplicationContext context) {
        // DispatcherServlet ищет HandlerMappings в СВОЕМ контексте
        // HandlerMapping содержит информацию о контроллерах
        
        this.handlerMappings = BeanFactoryUtils.beansOfTypeIncludingAncestors(
            context,                    // Ищет в дочернем контексте
            HandlerMapping.class,
            true,
            false
        ).values().toArray(new HandlerMapping[0]);
        
        // RequestMappingHandlerMapping автоматически сканирует
        // контроллеры в ТЕКУЩЕМ контексте (DispatcherServlet контекст)
    }
}

Проблема: если контроллер в Root Context, RequestMappingHandlerMapping его не просканирует!

Корректная конфигурация с использованием аннотаций

Правильно:

// Root Context Configuration
@Configuration
@ComponentScan(
    basePackages = "com.example",
    excludeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        value = Controller.class  // Исключаем контроллеры!
    )
)
public class AppConfig {
    // Services, Repositories, другие Beans
}
// DispatcherServlet Context Configuration
@Configuration
@ComponentScan(
    basePackages = "com.example",
    includeFilters = @ComponentScan.Filter(
        type = FilterType.ANNOTATION,
        value = Controller.class  // Включаем ТОЛЬКО контроллеры
    )
)
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    // Controllers здесь
}

XML вариант:

<!-- applicationContext.xml (Root Context) -->
<context:component-scan base-package="com.example">
    <!-- Исключаем контроллеры из Root Context -->
    <context:exclude-filter type="annotation" 
                           expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- dispatcher-servlet.xml (DispatcherServlet Context) -->
<context:component-scan base-package="com.example">
    <!-- Включаем ТОЛЬКО контроллеры -->
    <context:include-filter type="annotation" 
                           expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<mvc:annotation-driven/>

Реальный пример проблемы и решения

Конфигурация Servlet (web.xml):

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

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:dispatcher-servlet.xml</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Порядок инициализации:

1. ContextLoaderListener инициализирует Root Context
   - Загружает applicationContext.xml
   - Сканирует компоненты (ИСКЛЮЧАЯ @Controller)
   
2. DispatcherServlet инициализируется
   - Создаёт свой контекст
   - Загружает dispatcher-servlet.xml
   - Сканирует компоненты (ВКЛЮЧАЯ ТОЛЬКО @Controller)
   - RequestMappingHandlerMapping регистрирует обработчики

Spring Boot подход (проще!)

В Spring Boot этой проблемы нет, потому что используется один контекст:

@SpringBootApplication
@ComponentScan(basePackages = "com.example")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// Все @Controller, @Service и т.д. регистрируются в одном контексте

Частые ошибки и их решения

Ошибка 1: Контроллер в Root Context

Ошибка: org.springframework.web.servlet.NoHandlerFound
Причина: DispatcherServlet не видит контроллер
Решение: Переместить в dispatcher-servlet контекст или исключить из Root

**Ошибка 2: Component Scanning не настроен

Ошибка: Контроллер зарегистрирован, но не найден
Причина: Нет <context:component-scan> или @ComponentScan
Решение: Добавить сканирование пакетов

**Ошибка 3: Конфликтующие фильтры

// Root Context сканирует ВСЕ, включая контроллеры
@ComponentScan(basePackages = "com.example")  // БЕЗ excludeFilters

// DispatcherServlet тоже их видит
// Результат: контроллеры зарегистрированы в обоих контекстах!
// (обычно работает, но это плохая практика)

Debug: как понять, что контроллер не найден?

public class DebugController {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // Проверим, есть ли контроллер в Root Context
        try {
            UserController controller = context.getBean(UserController.class);
            System.out.println("Контроллер в Root Context: " + controller);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("Контроллер НЕ в Root Context");
        }
        
        // Проверим наличие всех Beans
        String[] beanNames = context.getBeanDefinitionNames();
        for (String name : beanNames) {
            System.out.println("Bean: " + name + " -> " + context.getBean(name).getClass().getSimpleName());
        }
    }
}

Вывод

DispatcherServlet не найдёт контроллер если:

  1. Контроллер зарегистрирован только в Root Context — DispatcherServlet использует свой дочерний контекст
  2. Нет component scanning — контроллер должен быть найден автоматически или явно определён
  3. Конфликтующие настройки фильтров — Root Context включает контроллеры, а DispatcherServlet их не видит

Правило золотое:

  • Root Context → Services, Repositories, Config
  • DispatcherServlet Context → Controllers, ViewResolvers
  • Или Spring Boot → Один контекст, проще!