Spring: вопросы с ответами
97 разобранных вопросов по теме «Spring». Каждый — с правильным ответом и пояснением.
- 401 vs 403 — в чём разница?
401 Unauthorized: не аутентифицирован (нет токена или токен невалидный). 403 Forbidden: аутентифицирован, но нет прав (пытаешься удалить чужой заказ). Ловушка: 401 — про identity, 403 — про permissions.
- В чём разница: AOP: JDK proxy vs CGLIB?
JDK dynamic proxy — если бин реализует интерфейс (через Proxy.newProxyInstance). CGLIB — наследование от класса (final-классы нельзя проксировать). Spring Boot 3 по умолчанию использует CGLIB.
- AOP: кейс «замерить время всех методов сервиса»?
@Aspect + @Around("execution(* com.example.service.*.*(..))"). ProceedingJoinPoint: long start = System.nanoTime(); Object result = pjp.proceed(); long elapsed = System.nanoTime() - start; log.info("{} took {} ms", ...); return result. Только @Around с ProceedingJoinPoint умеет proceed(), и без явного вызова proceed() целевой метод не выполнится.
- @Async — что важно знать?
Работает через прокси (как @Transactional) — не работает при self-invocation. Нужен @EnableAsync. Default executor может быть неподходящим — часто настраивают свой TaskExecutor.
- @Autowired + несколько подходящих бинов — что делать?
@Qualifier("beanName") на точке инжекта, или @Primary на одном из бинов, или имя поля/параметра должно совпадать с именем бина.
- CORS — что это и когда возникает?
Cross-Origin Resource Sharing: браузер блокирует запросы к другому домену. Сервер разрешает через заголовки: Access-Control-Allow-Origin, Access-Control-Allow-Methods. Preflight запрос (OPTIONS) перед POST/PUT с нестандартными заголовками; простые запросы preflight не требуют.
- Что такое Dirty / non-repeatable / phantom read?
Dirty — чтение незакоммиченных данных. Non-repeatable — два чтения одной строки дают разный результат. Phantom — два SELECT с WHERE возвращают разное число строк.
- Dirty / non-repeatable / phantom read — пример каждого?
Dirty: чтение незакоммиченных данных. Non-repeatable: два чтения одной строки дают разный результат. Phantom: два чтения с WHERE возвращают разное число строк.
- В чём разница: HAProxy vs Nginx vs Envoy?
HAProxy: L4/L7, production-proven, простая конфигурация. Nginx: L7, статика + reverse proxy. Envoy: L4/L7, gRPC, service mesh (Istio). В VK — все три в разных сценариях.
- Что такое HTTP-методы и идемпотентность?
GET (получить, идемпотентен), POST (создать, НЕ идемпотентен), PUT (заменить полностью, идемпотентен), PATCH (частично обновить, обычно нет), DELETE (удалить, идемпотентен). Идемпотентность — это когда повторный вызов с тем же запросом даёт тот же эффект на состояние сервера.
- Что такое IoC и DI?
IoC — контейнер управляет жизненным циклом. DI — зависимости внедряются извне. Три способа: конструктор (лучший), сеттер, поле (@Autowired). Constructor injection: поля final, явные зависимости, легко тестировать.
- JSON vs XML — в чём разница?
JSON компактнее, читабельнее, нативно поддерживается в JavaScript. XML — стандартизированный, есть схемы (XSD), namespace, можно валидировать. Для REST почти всегда JSON, для SOAP — обязательно XML.
- OpenAPI / Swagger — для чего?
Спецификация REST API в формате YAML/JSON. Описывает endpoints, методы, параметры, тела запросов/ответов, коды ответов. Swagger UI — интерактивная документация на основе этой спецификации, из которой можно отправлять запросы.
- Что такое Propagation?
REQUIRED (default) — присоединяется к текущей или создаёт новую. REQUIRES_NEW — всегда новая, приостанавливает текущую (аудит/логирование). NESTED — savepoint внутри текущей (откат внешней откатывает и вложенную). SUPPORTS — присоединяется если транзакция есть, нет — работает без неё. MANDATORY требует существующую (иначе исключение), NEVER — наоборот, бросает исключение при наличии транзакции.
- Что такое Propagation-уровни транзакций?
REQUIRED (default) — присоединяется или создаёт. REQUIRES_NEW — всегда новая, приостанавливает текущую. NESTED — savepoint. SUPPORTS, MANDATORY, NOT_SUPPORTED, NEVER.
- PUT vs PATCH — когда что?
PUT: замена ресурса целиком. Отправляешь ВСЕ поля, даже неизменённые. Идемпотентен (повтор даёт тот же результат). PATCH: частичное обновление. Отправляешь ТОЛЬКО изменённые поля. Обычно НЕ идемпотентен.
- REST vs gRPC — когда что?
REST — публичные API, простая отладка. gRPC — внутренний inter-service (быстрее, типизация через protobuf, streaming).
- В чём разница: REST vs SOAP?
REST: HTTP, JSON, легковесный, stateless, гибкий. SOAP: XML, WSDL, строгий контракт, WS- Security. В банках: REST для новых API, SOAP для legacy (АБС, процессинг). Junior AQA: REST — основной, но упомянуть знание SOAP — плюс.
- Scope: prototype в singleton?
Prototype-бин создастся один раз при инжекте. Решение: Provider<T>, ObjectFactory<T>, @Lookup.
- Что такое Scope бинов?
singleton (default), prototype, request, session, application, websocket. Инжект prototype в singleton: через Provider<T>, ObjectFactory<T> или @Lookup. Иначе prototype создастся один раз.
- Что такое Scopes бина?
singleton (default — один на контекст), prototype (новый при каждом запросе), request (один на HTTP-запрос), session, application, websocket. Web-скоупы доступны автоматически в web-контексте. Важно: инжект prototype в singleton через Provider<T> или ObjectFactory<T>; для prototype контейнер не вызывает destroy-колбэки.
- Scopes бина. Какой default?
singleton (default), prototype, request, session, application, websocket.
- Что такое self-invocation?
this.method() минует прокси → @Transactional/@Async/@Cacheable не работают. Решения: вынести метод в другой бин (рекомендуемый), инжектить self через @Lazy или ObjectProvider, использовать AspectJ compile-time weaving.
- Singleton-бин: потокобезопасен ли?
НЕТ. Singleton означает один экземпляр на контекст, но это НЕ делает его потокобезопасным. Если есть mutable state (поля, не final) — race condition, ведь объект делят все потоки. Решения: stateless бины (без полей-состояний), ThreadLocal, synchronized или атомарные типы.
- Что такое Spring Boot 3 + Java 21?
GraalVM native image, Virtual Threads support (spring.threads.virtual.enabled=true), Jakarta EE вместо javax.
- Spring Boot auto-configuration — как устроен?
@EnableAutoConfiguration + @Conditional. Конфиги описаны в META- INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (ранее — spring.factories).
- Spring Data — как работает?
Интерфейс extends JpaRepository<Entity, ID>. Spring создаёт прокси в рантайме. Имя метода парсится: findByEmailAndStatus → JPQL. @Query для сложных запросов (JPQL по умолчанию, native по флагу). Pageable → Page/Slice. JpaRepository добавляет flush() и batch-операции.
- Что такое @SpringBootApplication?
@Configuration + @EnableAutoConfiguration + @ComponentScan. Автоконфигурация через @Conditional: если DataSource в classpath — настроит JPA. Кандидаты берутся из META-INF/spring/...AutoConfiguration.imports (Spring Boot 3+). По умолчанию сканируется пакет класса и подпакеты.
- Что такое @Transactional?
Прокси (JDK/CGLIB). Открывает транзакцию, commit/rollback. self-call минует прокси. Rollback на RuntimeException/Error; checked — нужен rollbackFor.
- @Transactional + долгий REST-вызов внутри — чем опасно?
Транзакция удерживает соединение к БД всё время REST-вызова (секунды). Пул соединений исчерпается → все потоки ждут → сервис висит (connection starvation). Внешние вызовы нужно выносить за пределы транзакции.
- @Transactional на private методе — что произойдёт?
Ничего — транзакция не создастся. CGLIB-прокси наследует класс, но private методы не переопределяемы → прокси их не видит. JDK Dynamic Proxy работает через интерфейс — private вообще не в интерфейсе. Приложение поднимется штатно, метод выполнится, но без транзакции.
- @Transactional — под капотом?
Spring создаёт прокси-обёртку. JDK dynamic proxy (если интерфейс) или CGLIB (наследование). Прокси: открывает транзакцию (PlatformTransactionManager), вызывает метод, commit при успехе, rollback при ошибке. AspectJ weaving — только при явной настройке mode=ASPECTJ.
- @Transactional — прокси?
Spring создаёт прокси (JDK/CGLIB). Прокси открывает транзакцию, вызывает метод, commit/rollback. self-call минует прокси → @Transactional/@Cacheable/@Async не работают. Решение: вынести в другой бин.
- Что такое Автоконфигурация Spring Boot?
@EnableAutoConfiguration + @Conditional. Если DataSource в classpath — автоматически настроит JPA. Список: META-INF/spring/...AutoConfiguration.imports (Spring Boot 3+, раньше spring.factories).
- В какой момент происходит коммит @Transactional?
После успешного завершения метода (без исключений). Прокси: try { beginTransaction; target.method(); commit; } catch { rollback; }. Rollback по умолчанию: только RuntimeException и Error (unchecked); на checked-исключения отката нет — нужен явный rollbackFor.
- В чём плюшка Spring Boot?
Автоконфигурация — Spring Boot сам подключает компоненты в зависимости от того, что есть на classpath. Стартеры — готовые наборы зависимостей (spring-boot-starter-web подтягивает Spring MVC, Tomcat, Jackson). Встроенный сервер (Tomcat/Jetty) — приложение запускается как обычный jar. Boot надстраивается над Spring Framework, а не заменяет его.
- В чём разница 401 и 403?
401 Unauthorized — НЕ АУТЕНТИФИЦИРОВАН (не знаем кто ты — нет токена / он невалидный). Сделай login и приходи снова. 403 Forbidden — АУТЕНТИФИЦИРОВАН, но нет ПРАВ. Знаем кто ты, но эту операцию делать не можешь. Сначала аутентификация (401), затем проверка прав (403).
- В чём разница Proxy и Decorator?
Оба — обёртка над другим объектом, реализуют тот же интерфейс, различие в намерении. Proxy НЕ меняет поведение оригинала, а управляет доступом к нему: контроль доступа, ленивая инициализация, кэширование. Decorator, наоборот, добавляет новое поведение к методам.
- В чём разница PUT и PATCH?
PUT — полная замена ресурса. Передаём весь объект целиком. Если не указать поле — оно станет null. PATCH — частичное обновление. Передаём только изменяемые поля, остальное остаётся.
- В чём разница REST и SOAP?
REST — архитектурный стиль на HTTP, обычно JSON, гибкий, контракт через OpenAPI/Swagger. SOAP — протокол на XML с собственным envelope (Header + Body), строгий контракт через WSDL и поддержкой WS-Security (подпись и шифрование XML).
- Два @Transactional метода в одном классе: A() вызывает B(). Сколько транзакций?
Одна. Вызов this.B() минует прокси → @Transactional на B() не работает. B() выполняется в транзакции A() (Propagation.REQUIRED по умолчанию), которая открывается при внешнем вызове A() через прокси.
- Что такое Жизненный цикл бина?
BeanDefinition → инстанцирование → DI → Aware-интерфейсы → BPP.postProcessBefore → @PostConstruct → InitializingBean → init-method → BPP.postProcessAfter (здесь оборачивание в прокси, AOP) → ГОТОВ → @PreDestroy → DisposableBean → destroy-method.
- Из чего состоит HTTP-запрос?
Стартовая строка: метод + URL + версия (GET /users HTTP/1.1). Headers (Content-Type, Accept, Authorization). Пустая строка. Body (для POST/PUT/PATCH). Ответ — статус + версия + headers + body.
- Как Spring Boot узнаёт, что и как настраивать?
Через @EnableAutoConfiguration (входит в @SpringBootApplication) + @Conditional. Конфигурации перечислены в META-INF/spring/...AutoConfiguration.imports (или старом spring.factories).
- Как Spring Data создаёт реализацию репозитория?
Через прокси, генерируемый в рантайме. Имя метода (findByEmailAndStatus) парсится и превращается в JPQL.
- Как Spring создаёт реализацию репозитория, если ты только написал интерфейс?
Через прокси, который генерируется в рантайме. Имя метода (findByEmail) парсится и превращается в SQL/JPQL.
- Как работает @Autowired?
Spring находит бин подходящего типа в контексте и подставляет. Если в контексте несколько бинов одного типа — нужна @Qualifier или @Primary, иначе ошибка. С Spring 4.3+ @Autowired на единственном конструкторе можно не писать.
- Как работает @ComponentScan?
Сканирует пакеты (по умолчанию — пакет класса с @SpringBootApplication) в поисках @Component, @Service, @Repository, @Controller.
- Как работает @Transactional под капотом?
Spring создаёт прокси (JDK dynamic proxy для интерфейсов или CGLIB для классов). Прокси открывает транзакцию перед методом, коммитит после или откатывает при исключении.
- Как работает автоконфигурация?
@EnableAutoConfiguration (входит в @SpringBootApplication) + @Conditional. Список конфигов в META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (в Spring Boot 3+, раньше был spring.factories).
- Как разрешить конфликт, если бинов несколько?
@Primary — пометить «приоритетный» бин. @Qualifier("name") — точечно выбрать конкретный. @Profile("prod") — активировать в определённом окружении. @ConditionalOnProperty / @ConditionalOnMissingBean — условное создание.
- Как сделать REST-эндпоинт?
Класс с @RestController, метод с @GetMapping/@PostMapping. Параметры — @PathVariable, @RequestParam, @RequestBody. Возвращаемый объект сериализуется в JSON через Jackson.
- Как создать бин в Spring?
(1) Аннотация на классе: @Component, @Service, @Repository, @Controller — Spring найдёт через component scan. (2) Метод с @Bean внутри @Configuration — явное создание (для бинов из чужих библиотек или сложной инициализации).
- Какие HTTP-методы знаешь?
GET — получение данных, тело не предполагается. POST — создание, отправка данных в body (не идемпотентен). PUT — полное обновление / создание (идемпотентен). PATCH — частичное обновление. DELETE — удаление. HEAD — как GET, но без body (только headers).
- Какие propagation знаешь?
REQUIRED (default) — использует существующую транзакцию или создаёт новую. REQUIRES_NEW — приостанавливает текущую и создаёт новую независимую транзакцию. Полезно для аудита: должно записаться, даже если основная транзакция упала.
- Какие scope у зависимостей в Maven?
compile (default) — доступно везде, идёт в финальный jar. test — только для тестов. provided — для компиляции, но не в финальный jar (например, servlet-api — его даёт Tomcat в рантайме). runtime — нужно в рантайме, но не при компиляции.
- Какие scopes у бинов?
singleton (по умолчанию — один на контекст), prototype (новый при каждом запросе), request, session, application, websocket — для веб-приложений.
- Какие аннотации для тестов в Spring Boot?
@SpringBootTest — поднимает весь контекст приложения. Долго, но полноценно. @WebMvcTest — только web-слой (контроллеры), без БД. @DataJpaTest — только JPA-слой, с in-memory БД. MockMvc — имитация HTTP-запросов в тестах.
- Какие группы статус-кодов есть?
1xx — информационные. 2xx — успех (200 OK, 201 Created, 204 No Content). 3xx — редирект (301 постоянный, 302 временный, 304 Not Modified). 4xx — ошибка клиента (400, 401, 403, 404, 409). 5xx — ошибка сервера (500, 503).
- Какие принципы у REST?
Stateless — сервер не хранит сессию клиента, каждый запрос самодостаточен. Cacheable — ответы могут кэшироваться. Uniform interface — единый интерфейс через HTTP-методы и URL. Client-server и layered system дополняют набор.
- Какие скоупы бывают у бинов?
Singleton (default) — один экземпляр на контекст. Prototype — новый объект на каждый запрос get. Request, Session, Application — для веб-приложений, привязаны к жизни HTTP-запроса, сессии и ServletContext соответственно.
- Какие способы инъекции бинов есть?
Field (через @Autowired на поле) — короткий, но плохой. Setter (через @Autowired на сеттере) — гибкий, но позволяет создать неполностью сконфигурированный объект. Constructor — лучший: final-поля, гарантированно полностью сконфигурированный объект и лёгкое тестирование без контейнера.
- Какой DI лучше?
Constructor injection. Поля можно final, явно видны зависимости, легко тестировать без контекста (new Service(mockRepo)), невозможен циклический DI при старте (сигнал о проблеме в дизайне). С Spring 4 при единственном конструкторе @Autowired можно не писать.
- Какой способ DI предпочтительнее?
Constructor injection. Поля можно сделать final, явно видны обязательные зависимости, легко тестировать без Spring-контекста.
- Какой способ внедрения предпочтительнее?
Constructor injection. Поля можно final, явно видны обязательные зависимости, удобно тестировать без контекста, нет скрытых циклических зависимостей.
- Какой способ внедрения предпочтительнее и почему?
Constructor injection: immutable-поля (final), явные обязательные зависимости, удобство в тестах без mock-контейнера, нет циклических зависимостей.
- Когда нужен REQUIRES_NEW?
Когда нужна НЕЗАВИСИМАЯ транзакция, которая сохранится даже при rollback основной: текущая транзакция приостанавливается, открывается новая с отдельным соединением и коммитится сама по себе. Примеры: запись аудит-лога (должна остаться при ошибке), отправка уведомления, счётчик попыток.
- Конфликт зависимостей: как искать?
mvn dependency:tree — показывает дерево зависимостей. Ищем разные версии одной библиотеки. Gradle: ./gradlew dependencies. Решение: exclusion в Maven/Gradle, dependencyManagement/BOM, force в Gradle. Классика: Jackson 2.x разных версий; Maven берёт ближайшую в дереве (nearest-wins).
- Можно ли в GET передать body?
Спецификация не запрещает, но рекомендует не использовать. Многие фреймворки и прокси игнорируют body в GET. Если параметров много — лучше POST с явной семантикой («поиск с телом») или query string.
- Почему constructor injection лучше field injection?
(1) Можно делать поля final — иммутабельность. (2) Видны все зависимости — если их 10, конструктор большой, это сигнал «класс делает слишком много» (SRP). (3) Легко тестировать без Spring — обычный new SomeService(mockA, mockB) без поднятия контекста.
- Почему REST stateless?
Без сессии на сервере любой запрос можно отправить на любой сервер кластера. Это позволяет легко горизонтально масштабироваться: добавил инстанс — балансер начал слать на него. Состояние держит клиент (например, в JWT).
- Почему @Transactional не работает при self-invocation?
Вызов this.method() идёт минуя прокси. Решение: вынести метод в другой бин или инжектить self через ApplicationContext / @Lazy self.
- Почему @Transactional не работает при вызове метода того же класса?
Self-invocation идёт минуя прокси. Решение: вынести метод в отдельный бин или inject self.
- Что такое Расскажи упрощённый жизненный цикл бина?
(1) Spring находит определение (BeanDefinition). (2) Вызывает конструктор. (3) Инжектит зависимости (setter / field). (4) BeanPostProcessor.before. (5) @PostConstruct — пользовательская инициализация (afterPropertiesSet). (6) BeanPostProcessor.after. (7) Готовый бин. (8) @PreDestroy при остановке.
- Чем @Async может удивить?
Не работает self-invocation (как у @Transactional). Нужен @EnableAsync. Default executor может быть неподходящим — часто настраивают свой.
- Чем @RestController отличается от @Controller?
@RestController = @Controller + @ResponseBody. То есть результаты методов сразу сериализуются HttpMessageConverter'ами (по умолчанию Jackson в JSON) и отдаются в body ответа. @Controller сам по себе ожидает имя view (для рендера HTML).
- Что делает @ComponentScan?
Сканирует указанный пакет (по умолчанию — пакет класса, на котором стоит) и все подпакеты на предмет аннотаций @Component, @Service, @Repository, @Controller. Найденные классы регистрируются как бины в контексте.
- Что делает @SpringBootApplication?
Это композиция трёх аннотаций: @Configuration + @EnableAutoConfiguration + @ComponentScan.
- Что произойдёт при вызове @Transactional-метода из того же класса?
Транзакция НЕ откроется. Внутри одного бина вызов this.method() идёт напрямую на объект, в обход AOP-прокси. Это любимая ловушка на собесах — самая частая ошибка. Контекст поднимается нормально, ошибка тихая и чисто рантаймовая.
- Что такое @Autowired и @Qualifier?
@Autowired — внедрение зависимости. Если бинов несколько — Spring не знает какой выбрать нужен @Qualifier("имяБина") или @Primary на одном из бинов.
- Что такое DI?
Dependency Injection — конкретная реализация IoC. Зависимости передаются объекту: через конструктор, сеттер или поле. ApplicationContext в Spring — это, по сути, Map<id, bean>. Spring сам создаёт бины и инжектит их друг в друга.
- Что такое dirty read, non-repeatable read, phantom read?
Dirty: чтение незакоммиченных изменений другой транзакции. Non-repeatable: повторное чтение той же строки даёт другой результат. Phantom: повторный SELECT с WHERE возвращает другое количество строк.
- Что такое IoC?
Inversion of Control — инверсия управления. Вместо того чтобы класс сам создавал свои зависимости (new), он получает их извне — фреймворк сам решает, кого с кем связать.
- Что такое IoC и DI?
IoC — принцип: контейнер сам управляет жизненным циклом объектов. DI — реализация: зависимости в объект внедряются извне (через конструктор / сеттер / поле).
- Что такое Jakarta EE и чем отличается от Spring?
Jakarta EE (раньше Java EE) — набор стандартов-спецификаций корпоративной Java (сервлеты, JPA, JMS, EJB), реализуемых серверами приложений (GlassFish, WildFly). Spring — самостоятельный фреймворк поверх обычной Java, не требующий сервера приложений и не являющийся реализацией спецификаций Jakarta EE.
- Что такое REST?
Архитектурный стиль построения веб-сервисов. Идея: ресурсы (нечто, что имеет URL), действия над ними (через HTTP-методы), без сохранения состояния на сервере. Не протокол, а набор принципов.
- Что такое Spring Boot?
Spring + автоконфигурация + встроенный сервер (Tomcat/Jetty/Undertow) + стартеры. Минимизирует boilerplate.
- Что такое Spring Boot и чем он отличается от Spring?
Spring Boot — это Spring + автоконфигурация + встроенный сервер (Tomcat/Jetty) + удобные стартеры. Минимизирует boilerplate. По сути «Spring без боли с XML».
- Что такое Spring Cloud и для чего он?
Набор инструментов для микросервисов: service discovery (Eureka), конфиг-сервер, API Gateway, Circuit Breaker. На Junior достаточно знать «что это и зачем».
- Что такое Spring и зачем он нужен?
Фреймворк, который берёт на себя «инфраструктурный код»: создание объектов, связи между ними (DI), управление транзакциями, обработку HTTP, доступ к БД. Освобождает от boilerplate и работает поверх обычной JVM, не заменяя её.
- Что такое @SpringBootApplication?
Композиция трёх аннотаций: @SpringBootConfiguration (= @Configuration), @EnableAutoConfiguration (запуск автоконфигурации), @ComponentScan (сканирование пакетов начиная с этого класса).
- Что такое @Transactional?
Аннотация, которая открывает транзакцию вокруг метода. На успешный возврат — COMMIT, на RuntimeException — ROLLBACK. Реализуется через AOP-прокси: вызов идёт через прокси, который и управляет транзакцией.
- Что такое автоконфигурация?
Spring Boot имеет много готовых «конфигурационных рецептов». Они применяются автоматически, если выполнены условия (@Conditional): например, есть нужные классы на classpath, нет своего бина, заданы свойства. Настраивает инфраструктурные бины стартеров.
- Что такое идемпотентность?
Многократный вызов даёт тот же результат, что и однократный. Не путать с «безопасностью» (никак не меняет состояние) — GET и safe, и идемпотентен. PUT и DELETE идемпотентны (повторное PUT того же — тот же результат), а POST — нет.
- Что такое стартер?
Готовый набор зависимостей под задачу. Например, spring-boot-starter-web подтянет Spring MVC, встроенный Tomcat, Jackson. Один импорт — и веб-приложение работает.
- Что такое стартер в Spring Boot?
Готовый набор зависимостей под задачу. Например, spring-boot-starter-web подтянет Spring MVC, Tomcat, Jackson и т.д. Один импорт — и веб-приложение работает.
- Сервис вызывает свой метод с @Transactional через this.save(). Что произойдёт с транзакцией?
Вызов обойдёт Spring proxy, поэтому transactional advice не применится к внутреннему вызову.