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

Может ли Scope Request существовать в GenericApplicationContext?

1.8 Middle🔥 161 комментариев
#Другое

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

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

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

Request Scope в GenericApplicationContext

Краткий ответ: НЕТ, Request Scope не может существовать в GenericApplicationContext по умолчанию.

Это один из ключевых различий между типами Application Context в Spring. Давайте разберемся подробно.

Разница между Application Context

1. WebApplicationContext (Веб-приложения)

// WebApplicationContext ПОДДЕРЖИВАЕТ Request Scope
@Configuration
public class WebConfig {
    @Bean
    @Scope("request")  // Работает отлично
    public UserService userService() {
        return new UserService();
    }
}

// Использование в контроллере
@RestController
public class UserController {
    @Autowired
    private UserService userService;  // Request-scoped bean
    
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        // userService — новый экземпляр для каждого request
        return userService.getUser(id);
    }
}

Как это работает:

  • WebApplicationContext имеет доступ к ServletRequest
  • Для каждого HTTP запроса создается новый request scope
  • Bean уничтожается в конце обработки запроса

2. GenericApplicationContext (обычные приложения)

// GenericApplicationContext НЕ ПОДДЕРЖИВАЕТ Request Scope
public class MyApplication {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        
        // Регистрируем конфигурацию
        context.registerBean(MyService.class);
        context.refresh();
        
        // Это РАБОТАЕТ (Singleton Scope)
        MyService service1 = context.getBean(MyService.class);
        MyService service2 = context.getBean(MyService.class);
        System.out.println(service1 == service2);  // true (один экземпляр)
        
        // Это НЕ РАБОТАЕТ (Request Scope отсутствует)
        // context.registerBean(UserService.class, "request"); // Ошибка!
    }
}

Почему Request Scope не работает в GenericApplicationContext?

// Request Scope требует WebApplicationContext
public interface Scope {
    Object get(String name, ObjectFactory<?> objectFactory);
    void registerDestructionCallback(String name, Runnable callback);
    Object remove(String name);
    String getConversationId();
}

// Request Scope имплементирует этот интерфейс
public class RequestScope implements Scope {
    // Использует ThreadLocal для хранения request scope beans
    private static final ThreadLocal<Map<String, Object>> requestScope = 
        new ThreadLocal<>();
    
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> scope = requestScope.get();
        // Требует наличия ServletRequest в текущем потоке
        if (scope == null) {
            throw new IllegalStateException(
                "No thread-bound request found");
        }
        // ...
    }
}

Основные проблемы:

  1. GenericApplicationContext не регистрирует Request Scope автоматически
  2. Нет ServletRequest в контексте обычного приложения
  3. Request Scope требует HTTP контекста, которого нет в консольных/батник приложениях

Что нужно использовать вместо GenericApplicationContext для веб-приложений

1. Spring Boot (рекомендуется)

// application.properties
spring.application.name=my-app

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
        // Автоматически создает WebApplicationContext
        // Request Scope работает "из коробки"
    }
}

@Configuration
public class BeanConfig {
    @Bean
    @Scope("request")  // Работает!
    public UserContext userContext() {
        return new UserContext();
    }
}

2. AnnotationConfigWebApplicationContext

public class WebApplicationStartup {
    public static void main(String[] args) {
        // Для веб-приложений БЕЗ Spring Boot
        AnnotationConfigWebApplicationContext context = 
            new AnnotationConfigWebApplicationContext();
        
        context.register(WebConfig.class);
        context.refresh();
        
        // Теперь Request Scope работает!
    }
}

@Configuration
public class WebConfig {
    @Bean
    @Scope("request")  // Работает в WebApplicationContext
    public UserContext userContext() {
        return new UserContext();
    }
}

3. ClassPathXmlApplicationContext (в web.xml)

<!-- web.xml -->
<web-app>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/application-context.xml</param-value>
    </context-param>
</web-app>

<!-- application-context.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- Request Scope работает в web контексте -->
    <bean id="userContext" class="com.example.UserContext" scope="request">
        <aop:scoped-proxy/>
    </bean>
</beans>

Можно ли добавить Request Scope в GenericApplicationContext?

// ТЕХНИЧЕСКИ можно, но это плохая идея
public class GenericContextWithRequestScope {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        
        // Регистрируем Request Scope вручную
        context.getBeanFactory().registerScope("request", 
            new SimpleMapScope());
        
        // Но это не будет работать как надо, потому что:
        // 1. Нет HTTP запросов
        // 2. Нет ServletRequest в потоке
        // 3. Scope не очищается автоматически
    }
}

// SimpleMapScope — простая реализация (из примеров)
public class SimpleMapScope implements Scope {
    private final Map<String, Object> scope = new HashMap<>();
    
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        return scope.computeIfAbsent(name, k -> objectFactory.getObject());
    }
    
    @Override
    public String getConversationId() {
        return null;
    }
    
    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // Ничего не делает
    }
    
    @Override
    public Object remove(String name) {
        return scope.remove(name);
    }
}

Практические примеры

Что работает в GenericApplicationContext

public class GenericContextExample {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        
        // ✅ РАБОТАЕТ: Singleton Scope (по умолчанию)
        context.registerBean("singletonService", SingletonService.class);
        
        // ✅ РАБОТАЕТ: Prototype Scope
        context.registerBean(
            "prototypeService",
            PrototypeService.class,
            bd -> bd.setScope("prototype")
        );
        
        // ✅ РАБОТАЕТ: Custom Scope (если зарегистрирован)
        context.getBeanFactory().registerScope("custom", new CustomScope());
        context.registerBean(
            "customService",
            CustomService.class,
            bd -> bd.setScope("custom")
        );
        
        // ❌ НЕ РАБОТАЕТ: Request Scope
        // context.registerBean(
        //     "requestService",
        //     RequestService.class,
        //     bd -> bd.setScope("request")  // Будет ошибка!
        // );
        
        context.refresh();
    }
}

Что работает в WebApplicationContext

@Configuration
public class WebContextExample {
    // ✅ Singleton (по умолчанию)
    @Bean
    public SingletonService singletonService() {
        return new SingletonService();
    }
    
    // ✅ Prototype
    @Bean
    @Scope("prototype")
    public PrototypeService prototypeService() {
        return new PrototypeService();
    }
    
    // ✅ Session
    @Bean
    @Scope("session")
    public SessionUser sessionUser() {
        return new SessionUser();
    }
    
    // ✅ REQUEST (работает ТОЛЬКО в WebApplicationContext)
    @Bean
    @Scope("request")
    public RequestUser requestUser() {
        return new RequestUser();
    }
    
    // ✅ WebSocket Scope
    @Bean
    @Scope("websocket")
    public WebSocketMessage wsMessage() {
        return new WebSocketMessage();
    }
}

Таблица сравнения

ScopeGenericApplicationContextWebApplicationContextAnnotationConfigApplicationContextAnnotationConfigWebApplicationContext
Singleton
Prototype
Request
Session
WebSocket
Custom

Заключение

Ответ: НЕТ, Request Scope НЕ может существовать в GenericApplicationContext.

Причины:

  1. GenericApplicationContext не регистрирует Request Scope
  2. Request Scope требует ServletRequest в потоке
  3. GenericApplicationContext используется для не-веб приложений

Решение:

  • Используй WebApplicationContext или его подклассы для веб-приложений
  • Для Spring Boot это происходит автоматически
  • Для обычных веб-приложений используй AnnotationConfigWebApplicationContext
  • Если нужен Request Scope, нужен веб контекст