← Назад к вопросам
Что делает @Controller с точки зрения контекста?
1.3 Junior🔥 91 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что делает аннотация @Controller с точки зрения контекста Spring
Аннотация @Controller — это bean аннотация, которая регистрирует класс в Spring контексте с особыми правилами обработки HTTP запросов. Разберу детально что происходит.
Базовое определение
@Controller
public class UserController {
@GetMapping("/users")
public String getUsers(Model model) {
model.addAttribute("users", userService.getAll());
return "users"; // Возвращает имя view (JSP, Thymeleaf)
}
}
@Controller говорит Spring:
- Создай singleton bean из этого класса
- Регистрируй в ApplicationContext
- Обработай методы с @RequestMapping / @GetMapping / @PostMapping
- Возвращаемое значение — имя view (не JSON)
Жизненный цикл при запуске Spring
1. Spring сканирует @ComponentScan
2. Находит класс с @Controller
3. Создаёт BeanDefinition
4. Instantiates объект (singleton)
5. Инъектирует зависимости (@Autowired)
6. Вызывает @PostConstruct методы
7. Регистрирует в ApplicationContext
8. Регистрирует URL handlers в DispatcherServlet
Как Spring регистрирует @Controller
// Spring видит эту аннотацию
@Controller
public class UserController {
// И создаёт это в контексте:
}
// Эквивалент в XML:
<bean id="userController" class="com.example.UserController">
<property name="userService" ref="userService" />
</bean>
// Или программно:
ApplicationContext context = new AnnotationConfigApplicationContext();
UserController controller = context.getBean(UserController.class);
@Controller vs @RestController
@Controller:
@Controller
public class UserController {
@GetMapping("/users")
public String getUsers(Model model) {
return "users"; // Возвращает имя view
}
}
// HTTP Response:
// 200 OK
// Content-Type: text/html; charset=UTF-8
// <html>...</html> (Thymeleaf template)
@RestController:
@RestController
public class UserController {
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAll(); // Возвращает JSON
}
}
// HTTP Response:
// 200 OK
// Content-Type: application/json
// [{"id": 1, "name": "Alice"}, ...]
@RestController = @Controller + @ResponseBody на все методы
Что происходит в Spring контексте
1. Регистрация в ApplicationContext
ApplicationContext context = SpringApplication.run(App.class);
// UserController создан и зарегистрирован
UserController controller = context.getBean(UserController.class);
System.out.println(controller); // Выведет бин
// Проверить все беины
String[] beanNames = context.getBeanDefinitionNames();
for (String name : beanNames) {
System.out.println(name);
}
// Включит "userController"
2. Инъекция зависимостей
@Controller
public class UserController {
@Autowired // Spring инъектирует зависимость
private UserService userService;
@Autowired // Также инъектируется
private UserRepository userRepository;
// Spring поймёт эти зависимости и инъектирует
}
3. Регистрация URL handlers
@Controller
public class UserController {
@GetMapping("/users") // Spring регистрирует эту URL
public String getUsers() { return "users"; }
@PostMapping("/users") // И эту
public String createUser() { return "user-created"; }
}
// DispatcherServlet знает:
// GET /users -> userController.getUsers()
// POST /users -> userController.createUser()
Полный жизненный цикл
@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostConstruct
public void init() {
System.out.println("Controller initialized");
}
@GetMapping
public String list(Model model) {
model.addAttribute("users", userService.getAll());
return "users-list"; // View name
}
@PreDestroy
public void cleanup() {
System.out.println("Controller destroyed");
}
}
// Порядок выполнения при старте Spring:
// 1. Создание экземпляра UserController
// 2. Инъекция userService
// 3. Вызов @PostConstruct (init())
// 4. Регистрация в контексте
// 5. Регистрация URL handlers (GET /users -> list())
// 6. "Controller initialized" вывод
// При запросе GET /users:
// 1. DispatcherServlet получает запрос
// 2. Ищет handler для /users
// 3. Находит UserController.list()
// 4. Вызывает метод
// 5. list() возвращает "users-list"
// 6. ViewResolver ищет template/users-list.html
// 7. Renders HTML и отправляет клиенту
// При shutdown Spring:
// 1. Вызов @PreDestroy (cleanup())
// 2. "Controller destroyed" вывод
// 3. Удаление бина из контекста
Области видимости (Scopes)
// По умолчанию @Controller — Singleton
@Controller
public class UserController {
// Этот объект создаётся ОДИН раз
// И переиспользуется для всех запросов
}
// Но можно изменить scope
@Controller
@Scope("prototype") // Новый объект для каждого запроса
public class UserController {
// Требует особой конфигурации для работы с контекстом
}
// Или request scope
@Controller
@Scope("request") // Новый объект для каждого HTTP запроса
public class UserController {
// Специфично для web приложений
}
Получение доступа к контексту из Controller
@Controller
public class UserController {
@Autowired
private ApplicationContext context; // Инъектируем контекст
@GetMapping("/info")
public String info(Model model) {
// Получить другой бин из контекста
UserService service = context.getBean(UserService.class);
// Получить все беины определённого типа
Map<String, UserService> services =
context.getBeansOfType(UserService.class);
// Проверить наличие бина
boolean exists = context.containsBean("userService");
return "info";
}
}
Обработка исключений в контексте
@Controller
public class UserController {
@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id, Model model) {
User user = userService.getUser(id);
if (user == null) {
throw new UserNotFoundException("User not found");
}
model.addAttribute("user", user);
return "user-detail";
}
@ExceptionHandler(UserNotFoundException.class)
public String handleUserNotFound(UserNotFoundException e, Model model) {
// Spring контекст знает о всех @ExceptionHandler
model.addAttribute("error", e.getMessage());
return "error";
}
}
Прокси создание (AOP)
Если используешь @Transactional или другие аспекты, Spring создаёт прокси:
@Controller
public class UserController {
@Transactional // Spring создаёт прокси
public void saveUser(User user) {
userService.save(user);
}
}
// Spring на самом деле создаёт:
// UserController proxy = new Cglib$UserController$$EnhancedByCGLIB() {
// public void saveUser(User user) {
// // Начать транзакцию
// try {
// super.saveUser(user);
// // Commit
// } catch (Exception e) {
// // Rollback
// }
// }
// }
Отладка контекста
@Component
public class ContextDebugger implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext context) {
// Получаем контекст
String[] beanNames = context.getBeanDefinitionNames();
System.out.println("=== Registered Beans ===");
for (String name : beanNames) {
if (name.contains("Controller")) {
BeanDefinition def =
((DefaultListableBeanFactory) context)
.getBeanDefinition(name);
System.out.println("Bean: " + name);
System.out.println("Class: " + def.getBeanClassName());
System.out.println("Scope: " + def.getScope());
}
}
}
}
Итог
@Controller с точки зрения контекста:
- Регистрация в ApplicationContext — бин создаётся при startup
- Singleton по умолчанию — переиспользуется для всех запросов
- Инъекция зависимостей — все @Autowired поля инъектируются
- Регистрация URL handlers — методы с @GetMapping/PostMapping регистрируются в DispatcherServlet
- Обработка View — возвращаемое значение — имя view, не JSON
- AOP прокси — если используются аспекты (@Transactional), создаётся прокси
- Lifecycle методы — @PostConstruct и @PreDestroy вызываются
- Scope управляется — по умолчанию singleton, но можно изменить на request или prototype