Spring #
1. Зачем мы используем Spring? Почему его так любят? #
Spring — это один из самых популярных фреймворков для разработки приложений на Java. Его используют благодаря широкому набору возможностей, гибкости и модульности, что упрощает разработку корпоративных приложений.
2. Inversion of Control #
Это принцип, при котором мы передаем управление созданием и настройкой объектов Spring-у
3. Dependency Injection #
Dependency Injection (внедрение зависимостей) — это способ предоставления объекту его зависимостей извне, вместо того чтобы объект создавал их самостоятельно. DI упрощает управление зависимостями и делает код более модульным, тестируемым и легко расширяемым.
Dependency Injection vs dependency lookup
Критерий | Dependency Injection | Dependency Lookup |
---|---|---|
Суть | Зависимости предоставляются извне (контейнером или фреймворком). | Объект сам запрашивает зависимости (чаще из контекста). |
Простота тестирования | Легче тестировать, зависимости можно заменять mock-объектами. | Требует мокирования контекста или фабрики для тестов. |
Уровень контроля | Контейнер управляет зависимостями, объект их не создаёт. | Объект сам решает, как получить зависимости. |
Пример | Spring внедряет зависимость через аннотации или XML-конфигурацию. | Вручную вызов метода ApplicationContext.getBean(...) . |
Какие способы внедрения зависимостей знаешь?
Существует три способа внедрения зависимостей:
- Через конструктор
- Через сеттер
- Через поля
Способ | Описание | Плюсы | Минусы | Примеры использования |
---|---|---|---|---|
Через конструктор | Зависимости передаются при создании объекта. | - Гарантия неизменяемости. - Обязательные зависимости явно видны. - Высокая тестируемость. | - Неудобно для большого числа зависимостей. - Не подходит для опциональных зависимостей. | Когда зависимости обязательны и не изменяются после создания. |
Через setter | Зависимости устанавливаются с помощью метода после создания объекта. | - Подходит для опциональных зависимостей. - Зависимости можно изменять в runtime. | - Возможна ситуация, когда объект используется без установленной зависимости. - Менее явный. | Когда зависимости опциональны или могут изменяться. |
Через поля (field) | Зависимости внедряются напрямую в поля объекта, обычно с помощью аннотаций. | - Минимум кода. - Простота при использовании фреймворков (Spring, CDI). | - Сложнее тестировать. - Нарушение принципа явности зависимостей. - Отсутствие контроля. | Быстрое внедрение в Spring через @Autowired . |
Почему лучше использовать конструктор?
- Неизменяемость: Зависимости устанавливаются один раз при создании объекта, и их невозможно изменить.
- Обязательность зависимостей: Устанавливает чёткое требование, что объект не может существовать без переданных зависимостей.
- Тестируемость: Проще создавать mock-объекты для тестов.
- Явность: Видно, какие зависимости требуются для работы объекта.
Когда пригодится способ внедрения через setter?
- Опциональные зависимости: Зависимость не обязательна для работы объекта и используется только в некоторых сценариях.
- Динамическое изменение зависимостей: Когда требуется сменить зависимость во время выполнения программы.
- Библиотеки или старый код: Для интеграции с кодом, где нельзя контролировать процесс создания объектов.
4. @Autowired #
Автоматически внедряет бины. Аннотацией @Autowired
помечают:
- Сеттер
- Поле
- Конструктор
5. @Qualifier vs @Primary #
Если есть два одинаковых бина (по типу и имени) Spring не знает какой именно использовать и выдает exception. Если над одним из этих бинов установлена @Primary
, то его использовать предпочтительнее. Но если нам нужно использовать в работе оба этих бина, можно над каждым поставить @Qualifier
и задать имя, для идентификации этих бинов (работает только с @Autowired
)
Аннотация | Описание | Когда использовать |
---|---|---|
@Primary | Указывает бин по умолчанию при выборе среди нескольких. | Когда нужен бин, который используется чаще всего. |
@Qualifier | Явно указывает, какой бин использовать среди нескольких. | Когда нужно внедрить конкретный бин, несмотря на наличие @Primary . |
6. Что такое Bean? #
Bean в Spring — это объект, управляемый контейнером Spring IoC. Это основной строительный блок приложения, который регистрируется и конфигурируется контейнером.
@Bean
Аннотация @Bean
используется для указания метода, который возвращает объект(бин) и эти бины в дальнейшем можно внедрять в другие компоненты.
Аннотация @Bean
используется для явного объявления бина в Java-конфигурации (в классе, помеченном как @Configuration
).
Также можно использовать методы по умолчанию для определения бинов. Это позволяет создавать конфигурации бинов путем реализации интерфейсов с определениями бинов в методах по умолчанию.
public interface BaseConfig {
@Bean
default TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
public class AppConfig implements BaseConfig {
}
Что такое BeanDefinition?
BeanDefinition
— это метаданные, описывающие, как должен быть создан, конфигурирован и управляем конкретный бин.
Он содержит:
- Класс бина.
- Его скоуп (например, singleton или prototype).
- Зависимости.
- Методы инициализации и уничтожения.
Как Spring создает бины?
- Чтение конфигурации:
- Spring считывает конфигурацию из
@Configuration
, XML или аннотаций на классах.
- Spring считывает конфигурацию из
- Создание BeanDefinition:
- На основе конфигурации Spring формирует
BeanDefinition
для каждого бина.
- На основе конфигурации Spring формирует
- Инициализация бина:
- Вызывается конструктор или фабричный метод.
- Выполняется внедрение зависимостей (через конструктор, поля или сеттеры).
- Обработка бина:
- Постобработка через
BeanPostProcessor
(например, для работы с@Autowired
или@PostConstruct
).
- Постобработка через
- Добавление в контейнер:
- Готовый бин помещается в
ApplicationContext
.
- Готовый бин помещается в
Как конфигурируется бин?
- Аннотации (
@Bean
,@Component
,@Value
,@Scope
). - XML-конфигурацию.
- Java-код (в классе с
@Configuration
). - Средства типа
@PostConstruct
и@PreDestroy
для управления жизненным циклом.
В чем проблема того, что мы не можем final поле сделать в Bean-е?
Если в бине есть final
поле, то оно должно быть инициализировано в момент создания объекта.
Spring может не знать значения для этого поля на этапе создания объекта, так как внедрение зависимостей происходит после вызова конструктора (например, через @Autowired
).
Можно вставить Bean в статическое поле?
Нет, Spring не поддерживает внедрение зависимостей в статические поля напрямую.
Решение: Используйте вручную сеттер или вызовите контекст через ApplicationContext
.
Как сделать ленивую инициализацию бина?
Используйте аннотацию @Lazy
.
Какие есть способы донастройки бина?
Spring предоставляет несколько способов для настройки (customization
) бинов перед их использованием в приложении.
1️⃣ Способы донастройки бина
Метод | Описание |
---|---|
@PostConstruct | Метод вызывается после создания бина и внедрения зависимостей |
@PreDestroy | Метод вызывается перед удалением бина |
InitializingBean#afterPropertiesSet() | Метод вызывается после установки свойств |
DisposableBean#destroy() | Метод вызывается перед уничтожением бина |
@Bean(initMethod, destroyMethod) | Указывает методы инициализации и удаления |
BeanPostProcessor | Позволяет изменять бины до и после инициализации |
ApplicationContextAware | Дает доступ к ApplicationContext внутри бина |
📌 1. @PostConstruct
и @PreDestroy
Аннотации, которые позволяют выполнить код перед инициализацией бина и перед его уничтожением.
@Component
public class MyBean {
@PostConstruct
public void init() {
System.out.println("Бин создан!");
}
@PreDestroy
public void destroy() {
System.out.println("Бин будет удален!");
}
}
📌 Вывод:
Бин создан!
Бин будет удален!
📌 2. Реализация InitializingBean
и DisposableBean
Интерфейсы, которые позволяют управлять жизненным циклом бина.
@Component
public class MyBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() {
System.out.println("Бин инициализирован!");
}
@Override
public void destroy() {
System.out.println("Бин уничтожается!");
}
}
✅ Используется редко, так как @PostConstruct
и @PreDestroy
удобнее.
📌 3. @Bean(initMethod, destroyMethod)
В @Configuration
можно указать методы инициализации и уничтожения бина.
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
}
class MyBean {
public void init() {
System.out.println("Инициализация через initMethod");
}
public void cleanup() {
System.out.println("Удаление через destroyMethod");
}
}
📌 Вывод:
Инициализация через initMethod
Удаление через destroyMethod
📌 4. BeanPostProcessor
Позволяет изменять бины до и после их инициализации.
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("Before init: " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("After init: " + beanName);
return bean;
}
}
📌 Вывод:
Before init: myBean
After init: myBean
📌 5. ApplicationContextAware
Дает доступ к ApplicationContext
внутри бина.
@Component
public class MyBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
System.out.println("ApplicationContext установлен!");
}
}
✅ Полезно для получения других бинов внутри класса.
Как создать несколько бинов одного типа Singleton?
По умолчанию в Spring бины создаются в единственном экземпляре (Singleton). Однако, можно создать несколько бинов одного типа.
📌 1. Разные имена в @Bean
@Configuration
public class AppConfig {
@Bean("bean1")
public MyBean myBean1() {
return new MyBean("Первый бин");
}
@Bean("bean2")
public MyBean myBean2() {
return new MyBean("Второй бин");
}
}
class MyBean {
private String name;
public MyBean(String name) {
this.name = name;
}
}
📌 Теперь можно получить два разных бина:
@Autowired
@Qualifier("bean1")
private MyBean myBean1;
📌 2. Использование @Primary
и @Qualifier
Если есть несколько бинов одного типа, можно указать приоритетный бин с @Primary
.
@Component
@Primary
public class FirstService implements MyService {}
@Component
public class SecondService implements MyService {}
📌 Теперь @Autowired
будет использовать FirstService.
Если нужен SecondService, то используем @Qualifier
:
@Autowired
@Qualifier("secondService")
private MyService myService;
📌 3. Использование @Profile
Можно загружать разные бины в зависимости от профиля окружения.
@Component
@Profile("dev")
public class DevBean implements MyBean {}
@Component
@Profile("prod")
public class ProdBean implements MyBean {}
📌 Запуск с -Dspring.profiles.active=dev
создаст DevBean, а с -Dspring.profiles.active=prod
— ProdBean.
Где хранятся бины?
📌 Контейнер Spring – ApplicationContext
Spring создает и хранит бины в контейнере IoC (ApplicationContext
).
📌 Когда Spring загружается, он:
- Сканирует классы с аннотациями
@Component
,@Service
,@Repository
. - Создает бины и помещает их в
ApplicationContext
. - Управляет их жизненным циклом (инициализация, использование, удаление).
📌 Как получить бин из ApplicationContext
?
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyBean myBean = context.getBean(MyBean.class);
📌 Как вывести все бины?
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
✅ Выведет все бины, зарегистрированные в контейнере.
7. Скоуп бинов. Web скоуп #
Singleton
- Для каждого бина создается только один экземпляр на весь контекст приложения и переиспользуется везде, где он требуется
- Используется по-умолчанию
- Жизненный цикл бина начинается при загрузке контекста, и заканчивается при его завершении
Prototype
- Каждый раз, когда запрашивается бин, создается новый экземпляр
- Бины со скоупом
prototype
не уничтожаются Spring, так как они выходят из его контроля сразу после создания - НЕ ХРАНЯТСЯ в контексте Spring-а
Web скоуп:
Скоуп | Описание | Жизненный цикл |
---|---|---|
request | Создаётся один экземпляр бина на каждый HTTP-запрос. | Бин существует только в рамках одного HTTP-запроса. |
session | Создаётся один экземпляр бина на каждую HTTP-сессию. | Бин живёт в течение одной HTTP-сессии. |
application | Создаётся один экземпляр бина на весь ServletContext (аналогично синглтону, но для веб-приложения). | Бин существует в рамках всего времени работы веб-приложения. |
websocket | Создаётся один экземпляр бина на каждую WebSocket-сессию. | Бин живёт в течение времени существования WebSocket-сессии. |
Как создать свой Скоуп
- Реализуйте интерфейс Scope:
public class CustomScope implements Scope {
private Map<String, Object> beans = new HashMap<>();
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return beans.computeIfAbsent(name, k -> objectFactory.getObject());
}
@Override
public Object remove(String name) {
return beans.remove(name);
}
// Реализуйте другие методы...
}
- Зарегистрируйте его в контексте:
@Bean
public static CustomScopeConfigurer customScopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("customScope", new CustomScope());
return configurer;
}
8. Жизненный цикл Bean. Вопросы связанные с классами входящими в жизненный цикл бина #
Spring управляет созданием, инициализацией и уничтожением бинов в рамках контейнера IoC. Этот жизненный цикл можно разделить на несколько этапов.
Создание бина -> Внедрение зависимостей -> Постобработка -> Инициализация -> Использование -> Уничтожение

- Сначала мы предоставляем наши Bean Definitions IoC контейнеру
- IoC-контейнер сортирует Bean Definitions чтобы сначала создавать те, которые не имеют зависимостей, и затем те, которые зависят от других бинов
- Затем итерируется по всем Bean Definitions и инициализирует их
- Далее вызываются PostConstruct методы
- Перед и после инициализации вызываются методы BeanPostProcessor
- На выходе получаем готовый бин. Если у этого бина скоуп singleton - храним его в ассоциативном массиве IoC контейнера. Если же другой скоуп - бин сразу же возвращается
- Для всех бинов, которые находятся в IoC контейнере, перед его завершением вызывается
@PreDestroy
метод
Этап | Описание | Методы/Аннотации |
---|---|---|
Создание бина | Spring создает экземпляр бина, используя конструктор или фабричный метод. | Конструктор бина (или фабричный метод). |
Внедрение зависимостей | После создания, Spring внедряет все необходимые зависимости (через конструктор, поля, или сеттеры). | @Autowired , @Inject , конструкторы, сеттеры. |
Постобработка | Бин проходит через постобработчики перед инициализацией. Это этап, на котором можно изменить состояние бина. | BeanPostProcessor — postProcessBeforeInitialization , postProcessAfterInitialization . |
Инициализация | Если бин реализует интерфейсы InitializingBean или использует аннотации @PostConstruct , вызываются методы инициализации. | afterPropertiesSet() , @PostConstruct , пользовательские методы инициализации через @Bean(initMethod) . |
Использование | Бин доступен для использования в приложении. Это этап, когда бин полностью готов для работы. | Использование бина через @Autowired или прямые вызовы методов. |
Уничтожение | Когда контейнер Spring уничтожает бин (особенно для синглтонов), вызываются методы очистки. | destroy() , @PreDestroy , пользовательские методы уничтожения через @Bean(destroyMethod) . |
9. @ComponentScan #
Аннотация @ComponentScan
указывает Spring где искать классы, помеченные аннотацией @Component
или его производной (@RestController
, @Controller
, @Repository
, @Service
и т.д)
10. @Conditional в Spring #
Аннотация @Conditional
в Spring используется для того, чтобы условно подключать бины или выполнять конфигурации только при соблюдении определённых условий. Она позволяет гибко управлять поведением приложения, включая или исключая определенные компоненты на основе внешних или внутренних условий.
Что такое @ConditionalOnBean, @ConditionalOnProperty?
1. @ConditionalOnBean
Аннотация @ConditionalOnBean
указывает, что бин или конфигурация должны быть зарегистрированы в контексте только если определённый бин уже существует.
Основное использование
Вы хотите зарегистрировать бин или выполнить конфигурацию только в том случае, если в контексте приложения уже есть бин определённого типа или с определённым именем.
@Configuration
public class MyConfig {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
@ConditionalOnBean(MyService.class)
public DependentService dependentService() {
return new DependentService();
}
}
- Если бин
MyService
существует в контексте, будет создан бинDependentService
. - Если бина
MyService
нет,DependentService
не будет создан.
2. @ConditionalOnProperty
Аннотация @ConditionalOnProperty
указывает, что бин или конфигурация должны быть зарегистрированы в контексте только если определённое свойство в файле конфигурации имеет нужное значение.
Основное использование
Вы хотите активировать или деактивировать бин в зависимости от свойства, определённого в файле конфигурации (application.properties
или application.yml
).
# application.properties
feature.enabled=true
@Configuration
public class MyConfig {
@Bean
@ConditionalOnProperty(prefix = "feature", name = "enabled", havingValue = "true", matchIfMissing = false)
public FeatureService featureService() {
return new FeatureService();
}
}
- Если
feature.enabled=true
в конфигурации, бинFeatureService
будет зарегистрирован. - Если
feature.enabled=false
или свойство отсутствует, бин не будет создан (еслиmatchIfMissing=false
).
Аннотация | Условие регистрации бина | Тип проверки |
---|---|---|
@ConditionalOnBean | Проверяет наличие другого бина в контексте | Проверка наличия бина |
@ConditionalOnProperty | Проверяет значение свойства из конфигурации (application.properties или application.yml ) | Проверка значения свойства |
11. @Service, @Repository, @Component #
Аннотация | Описание | Использование | Семантика |
---|---|---|---|
@Component | Общая аннотация для обозначения компонента, который будет управляться контейнером Spring. | Для обычных бинов. | Может быть использована для любых типов компонентов. Обычно используется, когда нет более точного типа. |
@Service | Специализация @Component , указывающая, что бин представляет собой сервисный слой. | Для сервисов. | Используется для классов, реализующих бизнес-логику. По сути, это более семантическое обозначение, чем @Component . |
@Repository | Специализация @Component , указывающая, что бин представляет собой слой доступа к данным (DAO). | Для классов, работающих с данными. | Используется для классов, которые инкапсулируют логику доступа к данным (например, через JDBC или JPA). Аннотация также может активировать обработку исключений, специфичных для DAO. |
Singleton-антипаттерн. Если мы на класс поставим @Service, это будет Singleton?
Если вы ставите аннотацию @Service
, Spring создаёт бин с по умолчанию синглтон. Однако это не является Singleton-антипаттерном, поскольку Spring управляет жизненным циклом и внедрением зависимостей, что решает многие проблемы, присущие обычному Singleton.
@Bean vs @Component
1️⃣ @Bean
- Используется в конфигурационных классах (
@Configuration
). - Определяет бин программно (через метод).
- Позволяет настраивать бин перед его созданием.
📌 Пример:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
✅ Когда использовать?
- Если создание бина требует сложной логики.
- Если бин не может быть помечен
@Component
(например, библиотечный класс).
2️⃣ @Component
- Используется для автоматического сканирования компонентов (
@ComponentScan
). - Позволяет Spring автоматически обнаруживать бины.
📌 Пример:
@Component
public class MyServiceImpl implements MyService {
}
✅ Когда использовать?
- Если класс управляется Spring и его можно автоматически найти.
🔹 Основные отличия
Характеристика | @Bean | @Component |
---|---|---|
Где используется? | В @Configuration классе | На классе |
Как создается бин? | Программно | Автоматически (@ComponentScan ) |
Гибкость настройки? | Высокая (можно передавать параметры) | Меньшая |
Где применяется? | Когда нужен сложный бин | Когда класс управляется Spring |
12. @Controller и @RestController #
@Controller
В основном используется для возврата веб-страниц. Если нужно вернуть данные (например, JSON), нужно добавить@ResponseBody
к методу@RestContoller
Это комбинация@Controller
и@ResponseBody
. Используется в REST. Всегда возвращает данные (например, JSON или XML) в теле HTTP-ответа. Нет необходимости добавлять@ResponseBody
к каждому методу.
13. @ResponseBody vs ResponseEntity #
- ResponseEntity — это объект, который содержит как тело ответа, так и HTTP-статус (например, 200 OK, 404 Not Found) и заголовки. Используется для более гибкого управления HTTP-ответами
@ResponseBody
Аннотация, которая указывает, что результат метода контроллера должен быть возвращен как тело HTTP-ответа (например, JSON, XML)
14. Spring MVC. Что это? Какие проблемы решает? #
Spring MVC — это один из модулей Spring Framework, предназначенный для разработки веб-приложений на основе шаблона Model-View-Controller. Решает следующие проблемы
- Чёткое разделение логики. Разбиваем логику на MVC
- Поддержка различных шаблонизаторов. JSP, Thymeleaf и т.д
- Упрощает обработку HTTP-запросов через контроллеры
15. Парадигма AOP #
AOP - это способ добавить в программу дополнительные функции, не трогая основной код программы. Например, у нас есть метод, который что-то делает. Но нам нужно, чтобы перед началом и в конце работы этого метода писался лог. Вместо того чтобы добавлять этот код логирования в каждый метод руками - можно вынести его в отдельное место (аспект), а Spring сам “вставит” его туда, где нужно, без изменений основного кода.
16. Циклическая зависимость #
Циклическая зависимость - это когда два класса взаимно ссылаются друг на друга через аннотацию @Autowired
. Это может вызвать проблему циклической зависимости, которую Spring Framework не сможет разрешить. Для решения этой проблемы можно использовать один из следующих подходов:
- Внедрять зависимости через set-ер
- Использование
@Lazy
. Аннотация@Lazy
откладывает создание бина до момента его использования, что позволяет разорвать круг
@Component
public class BeanA {
private final BeanB beanB;
@Autowired
public BeanA(@Lazy BeanB beanB) {
this.beanB = beanB;
}
}
@Component
public class BeanB {
private final BeanA beanA;
@Autowired
public BeanB(@Lazy BeanA beanA) {
this.beanA = beanA;
}
}
17. Spring Boot #
Spring Boot — это фреймворк на основе Spring, который позволяет быстрее и проще создавать готовые к работе приложения. Ключевые преимущества:
- Стартеры — это готовые наборы библиотек (модулей), которые включают все необходимые зависимости для работы с конкретными технологиями
- Встроенный сервер - В Spring Boot сервер встроен прямо в приложение. Например, когда вы добавляете стартер
spring-boot-starter-web
, Spring Boot автоматически включает Tomcat или Jetty - Автоматическая конфигурация - Spring Boot использует механизм автоматической конфигурации, который анализирует, какие библиотеки находятся в вашем проекте, и автоматически настраивает необходимые компоненты.
- Упрощение сборки - В обычном Spring для развертывания приложения вам нужно собрать WAR файл и развернуть его на сервере. В Spring Boot приложение собирается в исполняемый JAR файл, который содержит все зависимости и сервер.
Spring Boot vs Spring
- Spring — это основной фреймворк с полной конфигурацией и гибкостью.
- Spring Boot — это расширение Spring, которое делает процесс разработки проще, автоматизируя конфигурацию и позволяя быстро запускать приложение с минимальной настройкой.
Критерий | Spring | Spring Boot |
---|---|---|
Цель | Мощный фреймворк для создания Java-приложений. | Упрощение разработки с использованием Spring. Автоматизация конфигурации. |
Конфигурация | Требуется вручную настроить XML или Java-конфигурацию. | Автоконфигурация, минимальные настройки для старта проекта. |
Запуск | Нужно вручную настроить веб-сервер (например, Tomcat). | Встроенные серверы (Tomcat, Jetty, Undertow) для простого запуска. |
Гибкость | Большая гибкость, полная настройка каждого компонента. | Меньше гибкости, но намного быстрее и проще для старта проекта. |
Сложность | Более высокая сложность настройки и конфигурации. | Упрощенная настройка и конфигурация. |
Использование | Используется, когда нужна полная настройка и контроль. | Используется для быстрого старта и простоты разработки. |
Конфигурационные файлы | Использует XML или Java-конфигурацию для настройки бинов. | Использует application.properties или application.yml для настройки. |
Как Spring Boot работает под капотом
Автоконфигурация (Auto Configuration):
- Spring Boot анализирует классы и зависимости в вашем проекте и пытается настроить приложение так, чтобы оно работало “из коробки”.
- Например, если в проекте есть зависимость от
spring-boot-starter-web
, то Spring Boot автоматически настраиваетDispatcherServlet
, который обрабатывает HTTP-запросы.
Сканирование компонентов:
- При запуске приложения Spring Boot сканирует пакеты для поиска аннотированных классов (например, с аннотациями
@Component
,@Service
,@Repository
,@Controller
и т. д.), чтобы зарегистрировать бины. - Это сканирование начинается с класса, аннотированного
@SpringBootApplication
, который обычно находится в корне проекта. Пакеты и подпакеты, расположенные ниже этого класса, будут сканироваться на наличие компонентов.
- При запуске приложения Spring Boot сканирует пакеты для поиска аннотированных классов (например, с аннотациями
Встроенный сервер:
- Spring Boot может использовать встроенные сервера, такие как Tomcat, Jetty или Undertow, что позволяет запустить приложение как самодостаточный исполнимый JAR или WAR файл.
Процесс инициализации:
- Когда приложение запускается, Spring Boot автоматически запускает и настраивает все необходимые компоненты и зависимости. Весь процесс инициализации централизован и автоматизирован.
Основной класс с
@SpringBootApplication
:- Этот класс является точкой входа в приложение. Он выполняет несколько задач:
- Включает автоматическое конфигурирование с помощью
@EnableAutoConfiguration
. - Разрешает сканирование компонентов с помощью
@ComponentScan
. - Включает настройку конфигурации Spring с помощью
@Configuration
.
- Включает автоматическое конфигурирование с помощью
- Этот класс является точкой входа в приложение. Он выполняет несколько задач:
Где Spring Boot ищет бины?
Основной класс с
@SpringBootApplication
:- Spring Boot начинает сканировать пакеты с местоположения класса, аннотированного
@SpringBootApplication
. - Все классы в этом пакете и его подпакетах будут автоматически обработаны на наличие аннотаций для создания бинов (например,
@Component
,@Service
,@Repository
и т. д.).
- Spring Boot начинает сканировать пакеты с местоположения класса, аннотированного
Пакет с
@SpringBootApplication
:- По умолчанию Spring Boot сканирует только текущий пакет и его подпакеты. Поэтому важно, чтобы основной класс с аннотацией
@SpringBootApplication
находился в корне пакета, чтобы гарантировать, что все компоненты будут найдены.
- По умолчанию Spring Boot сканирует только текущий пакет и его подпакеты. Поэтому важно, чтобы основной класс с аннотацией
Дополнительные настройки сканирования:
- Можно явно указать, какие пакеты нужно сканировать, с помощью аннотации
@ComponentScan
:
- Можно явно указать, какие пакеты нужно сканировать, с помощью аннотации
@SpringBootApplication
@ComponentScan(basePackages = "com.example.myapp")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Это полезно, если компоненты находятся в другом пакете или если вы хотите ограничить область сканирования.
Использование профилей:
- Если в проекте используются разные профили (например, для разных сред), Spring Boot будет также искать компоненты, соответствующие активному профилю, при условии что они аннотированы как
@Profile
.
- Если в проекте используются разные профили (например, для разных сред), Spring Boot будет также искать компоненты, соответствующие активному профилю, при условии что они аннотированы как
18. Spring JDBC #
Spring JDBC — это модуль Spring, который упрощает взаимодействие с базами данных, используя JDBC (Java Database Connectivity). Он предлагает упрощённый подход к выполнению SQL-запросов, обработке результатов и управлению ресурсами.
19. Spring Data JPA #
Spring Data JPA — это модуль Spring, который упрощает работу с JPA (Java Persistence API) и предоставляет удобные способы доступа к данным. Он позволяет разработчикам сосредоточиться на бизнес-логике, не заботясь о низкоуровневых деталях доступа к данным.
20. Блокировка по умолчанию в Spring #
В Spring, по умолчанию, используется оптимистичная блокировка для управления конкурентным доступом к данным. Это означает, что при обновлении сущностей предполагается, что конфликты не произойдут, и изменения вносятся в базу данных без блокировок.
21. @Transactional. Как работает? #
@Transactional
— это аннотация, которая позволяет автоматически управлять транзакциями. Когда метод аннотирован @Transactional
, Spring берет на себя начало, коммит или откат транзакции
Что можно пометить аннотацией @Transactional?
- Класс. Все его методы станут
@Transactional
- Метод
Дефолтный propagation
Propagation.REQUIRED
Атрибут propagation у @Transactional
- REQUIRED — применяется по умолчанию. При входе в
@Transactional
метод будет использована уже существующая транзакция или создана новая транзакция, если никакой еще нет - REQUIRES_NEW — для внутреннего метода создается своя отдельная транзакция. Пока выполняется внутренний метод, внешняя транзакция приостанавливается. Результат выполнения внутренней - не повлияет на результат выполнения внешней
- NESTED — создаёт под транзакцию внутри основной транзакции. Эта под транзакция может быть откатана отдельно от основной, но зависит от успешного завершения основной транзакции. Если основная транзакция откатится, под транзакция тоже откатится, даже если она завершилась успешно
- MANDATORY — требует внешнюю транзакцию, а иначе выбрасывается исключение
- SUPPORTS — метод будет выполняться в рамках транзакции, если она существует. Если ее нет, метод выполнится без транзакции
- NOT_SUPPORTED — метод всегда выполняется вне транзакции, даже если существует текущая транзакция. Текущая транзакция приостанавливается
- NEVER — запрещает выполнение метода в транзакции
Какие способы управления транзакциями в Spring вы знаете?
- Аннотация
@Transactional
- TransactionManager
- TransactionTemplate
TransactionManager и TransactionTemplate
- Когда требуется точный контроль над транзакциями (например, управлять несколькими транзакциями в одном потоке)
@Transactional
- Когда нужно простое управление транзакциями
- Когда транзакция не требует сложной логики управления
@Transactional повесить на private метод?
Если вы поместите аннотацию @Transactional
на private
метод, то транзакция не будет создана или применена, потому что Spring использует прокси-механизм для управления транзакциями.
Какие есть атрибуты у @Transactional?
Аннотация @Transactional
управляет транзакциями в Spring. Она может быть установлена на классе или методе.
📌 Основные атрибуты
Атрибут | Описание | Значение по умолчанию |
---|---|---|
propagation | Как ведет себя транзакция (создавать новую или использовать существующую) | REQUIRED |
isolation | Уровень изоляции транзакции (как управлять конкурентным доступом) | DEFAULT |
readOnly | Только для чтения (не позволяет изменять данные) | false |
timeout | Максимальное время выполнения (в секундах) | -1 (бесконечно) |
rollbackFor | Перечисляет исключения, при которых делается rollback | Пусто |
noRollbackFor | Исключения, при которых rollback не делается | Пусто |
📌 1. propagation
– управление транзакциями
📌 Варианты:
Значение | Описание |
---|---|
REQUIRED | Использует существующую транзакцию или создает новую (по умолчанию) |
REQUIRES_NEW | Всегда создает новую транзакцию |
SUPPORTS | Использует существующую, но если ее нет — выполняется без транзакции |
NOT_SUPPORTED | Всегда выполняется без транзакции |
MANDATORY | Использует существующую, если ее нет — выбрасывает ошибку |
NEVER | Никогда не выполняется в транзакции |
📌 Пример:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveUser() {
// Эта транзакция создаст новую, даже если уже есть другая
}
📌 2. isolation
– уровень изоляции
📌 Варианты:
Значение | Описание |
---|---|
DEFAULT | Использует уровень БД |
READ_UNCOMMITTED | Позволяет читать неподтвержденные изменения (грязное чтение) |
READ_COMMITTED | Не позволяет читать неподтвержденные изменения |
REPEATABLE_READ | Не позволяет изменять данные, которые уже были прочитаны |
SERIALIZABLE | Полная изоляция (максимальная защита, но низкая производительность) |
📌 Пример:
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processOrder() {
// Защита от "фантомных" чтений
}
📌 3. readOnly
– транзакция только для чтения
📌 Если метод только читает данные (например, findById
), то readOnly = true
ускорит работу.
@Transactional(readOnly = true)
public User getUser(Long id) {
return userRepository.findById(id).orElseThrow();
}
✅ Преимущества:
- Не блокирует ресурсы для обновления
- Повышает производительность
📌 4. rollbackFor
– откат при ошибке
По умолчанию Spring откатывает транзакции только при RuntimeException
, но можно указать другие классы исключений.
@Transactional(rollbackFor = Exception.class)
public void saveData() throws Exception {
throw new Exception("Ошибка!"); // Теперь произойдет rollback
}
@Transaction Что будет если вызвать метод из метода в одном классе?
Если вы вызовете метод, помеченный @Transactional
, из другого метода того же класса, то транзакция не начнётся или не будет соблюдена, если вызов происходит напрямую.
Почему?
Вызов внутри класса не проходит через прокси, который отвечает за управление транзакцией. Таким образом, аннотация @Transactional
не будет обработана.
@Service
public class MyService {
@Transactional
public void transactionalMethod() {
// Транзакция активируется, если вызов через прокси
}
public void callTransactionalMethod() {
transactionalMethod(); // Прямой вызов, транзакция НЕ активируется
}
}
Как исправить?
1. Используйте вызов через другой бин
Если транзакционный метод вызывается из другого бина, вызов пройдёт через прокси, и транзакция будет активирована.
@Service
public class MyService {
@Transactional
public void transactionalMethod() {
// Транзакция активируется
}
}
@Service
public class AnotherService {
@Autowired
private MyService myService;
public void callMethod() {
myService.transactionalMethod(); // Вызов через прокси, транзакция активируется
}
}
2. Используйте self-injection
Вы можете внедрить бин самого себя, чтобы обойти проблему с прямыми вызовами.
@Service
public class MyService {
@Autowired
private MyService self;
@Transactional
public void transactionalMethod() {
// Транзакция активируется
}
public void callTransactionalMethod() {
self.transactionalMethod(); // Вызов через прокси, транзакция активируется
}
}
22. @Async #
Аннотация @Async
используется в Spring для выполнения методов асинхронно, то есть в отдельном потоке, чтобы не блокировать основной поток выполнения.
Как работает @Async
Асинхронное выполнение:
- Метод, помеченный аннотацией
@Async
, выполняется в отдельном потоке. - Возвращаемый тип может быть
void
,Future
, илиCompletableFuture
.
- Метод, помеченный аннотацией
Spring AOP:
- Подобно
@Transactional
,@Async
работает через прокси. - Вызов асинхронного метода должен проходить через Spring контейнер (бин), чтобы
@Async
обработалась.
- Подобно
Пул потоков:
- Асинхронные вызовы используют пул потоков, определённый в настройках. По умолчанию используется пул
SimpleAsyncTaskExecutor
, но вы можете настроить свой.
- Асинхронные вызовы используют пул потоков, определённый в настройках. По умолчанию используется пул
23. Starter в Spring. Плюсы и минусы #
Starters в Spring — это специальные артефакты, которые облегчают конфигурацию и настройку приложений. Они предоставляют преднастроенные зависимости и настройки для различных функциональностей, что позволяет разработчикам быстро и просто начать работу с определёнными аспектами Spring-приложения.
Плюсы:
- Упрощение конфигурации
- Согласованность
- Сокращение времени разработки
- Модульность
- Документация и поддержка
Минусы:
- Избыточные зависимости
- Меньшая гибкость
Какие отличия стартера от библиотеки?
📌 1. Что такое starter
?
Spring Boot Starter – это набор зависимостей, который включает все необходимые библиотеки для работы с определенной технологией.
✅ Пример:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Этот starter
включает:
Spring MVC
Tomcat
Jackson
📌 Преимущества starter
:
✔ Упрощает конфигурацию
✔ Автоматически подключает все нужные зависимости
📌 2. Чем отличается от обычной библиотеки?
Характеристика | Starter | Library |
---|---|---|
Состав | Включает набор библиотек | Одиночная библиотека |
Настройка | Автоматическая | Требует ручной настройки |
Пример | spring-boot-starter-data-jpa | hibernate-core |
📌 Пример библиотеки:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
✅ Starter включает библиотеку + нужные настройки.
24. Spring Criteria API #
Spring Criteria API (часть Hibernate) предоставляет гибкий способ построения запросов к базе данных. Она основана на использовании объектно-ориентированных принципов, позволяя составлять SQL-запросы с использованием Java-классов вместо ручного написания SQL.
Ключевые особенности Criteria API
Динамическое построение запросов:
- Удобно для ситуаций, когда структура запроса может меняться в зависимости от условий.
Безопасность типов:
- Ошибки в синтаксисе запросов обнаруживаются на этапе компиляции.
Ленивость:
- Запрос выполняется только при обращении к результатам.
Поддержка сложных запросов:
- Подзапросы, группировки, сортировки, фильтрации и агрегатные функции.
Пример базового использования
- Установка сущности
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double salary;
// getters и setters
}
- Использование Criteria API
@Repository
public class EmployeeRepository {
@PersistenceContext
private EntityManager entityManager;
public List<Employee> findEmployeesWithSalaryGreaterThan(double minSalary) {
// Получаем CriteriaBuilder
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
// Создаём CriteriaQuery
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
// Корневой объект (FROM Employee)
Root<Employee> root = criteriaQuery.from(Employee.class);
// Условие (WHERE salary > :minSalary)
criteriaQuery.select(root)
.where(criteriaBuilder.gt(root.get("salary"), minSalary));
// Выполняем запрос
return entityManager.createQuery(criteriaQuery).getResultList();
}
}
25. @Scheduled #
Аннотация @Scheduled
используется для выполнения методов по расписанию. Она позволяет запускать задачи с фиксированным интервалом времени, фиксированной задержкой или на основе cron-выражений.
26. Понятие Controller и Servlet в Java. Это одно и то же? Если разное, в чем отличия? #
Servlet
и Controller
— это разные концепции, хотя оба они используются для обработки HTTP-запросов в Java веб-приложениях. Однако их роли и области применения различаются.
Servlet — это Java-класс, который обрабатывает HTTP-запросы (HTTP GET, POST и т.д.) и возвращает HTTP-ответы.
Он является основой для создания серверных приложений в Java.
Controller — это часть архитектуры MVC (Model-View-Controller). В контексте Java это обычно класс, аннотированный, например, @Controller
в Spring MVC, который обрабатывает HTTP-запросы и управляет их маршрутизацией.
Контроллер абстрагирует разработчика от низкоуровневых деталей (например, работы с HTTP-запросами и ответами), позволяя сосредоточиться на бизнес-логике.
Основные отличия Servlet и Controller
Критерий | Servlet | Controller |
---|---|---|
Область применения | Низкоуровневая обработка HTTP-запросов. | Высокоуровневый компонент архитектуры MVC. |
Ручная работа | Требуется больше ручной работы для маршрутизации и обработки данных. | Маршрутизация и обработка запросов автоматизированы. |
Инструменты | Использует контейнер сервлетов (например, Tomcat). | Использует веб-фреймворки (например, Spring MVC). |
Гибкость | Полный контроль над HTTP-запросами и ответами. | Сосредоточен на бизнес-логике, делегируя детали фреймворку. |
Уровень абстракции | Низкий: работа напрямую с HttpServletRequest и HttpServletResponse . | Высокий: аннотации и абстрагирование низкоуровневой логики. |
Пример | HttpServlet , GenericServlet . | @Controller в Spring, @RestController . |
Когда использовать?
Ситуация | Servlet | Controller |
---|---|---|
Вам нужен полный контроль над HTTP-запросами | Да | Нет |
Вы используете современные фреймворки | Редко | Да |
Требуется простота разработки | Нет | Да |
Вы пишете сложное веб-приложение | Непрактично | Предпочтительно |
27. Что такое объект Filter? В какой момент вызывается? #
Filter — это интерфейс из Java Servlet API, который позволяет перехватывать и изменять HTTP-запросы и ответы. Фильтры часто используются для задач, которые необходимо выполнить до или после обработки запроса в сервлете, например:
- Аутентификация и авторизация.
- Логирование запросов.
- Сжатие ответов.
- Кэширование.
- Добавление или изменение заголовков запросов и ответов.
Когда вызывается фильтр?
Фильтры вызываются до попадания HTTP-запроса к сервлету (или контроллеру) и после возвращения ответа от сервлета. Они служат промежуточным уровнем между клиентом и сервлетом.
- До сервлета: Обрабатывают входящий HTTP-запрос.
- После сервлета: Могут изменить HTTP-ответ перед отправкой клиенту.
28. Что такое ApplicationContext? #
ApplicationContext
— это центральный интерфейс контейнера Spring, который управляет жизненным циклом бинов, их зависимостями и предоставляет дополнительные возможности для разработки приложений. Это расширение интерфейса BeanFactory
, добавляющее множество дополнительных функций, таких как обработка событий, поддержка интернационализации и интеграция с AOP.
Основные функции ApplicationContext
- Управление бин-компонентами:
- Автоматическое создание, связывание и уничтожение бинов.
- Инжекция зависимостей:
- Поддерживает механизмы Dependency Injection (DI).
- Обработка событий:
- Поддерживает публикацию и прослушивание событий (например, ContextRefreshedEvent).
- Интернационализация:
- Предоставляет механизмы для работы с сообщениями и локалями.
- Интеграция с AOP:
- Позволяет внедрять аспекты в приложение.
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyBean myBean = context.getBean(MyBean.class);
myBean.doSomething();
}
}
Расскажи про разницу между ApplicationContext и BeanFactory
BeanFactory
— это базовый контейнер Spring для управления объектами (бинами).
Он отвечает за инициализацию, связывание и предоставление бинов по запросу.
Однако BeanFactory
менее функционален, чем ApplicationContext
, так как предоставляет только базовые возможности управления бинами.
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MyApp {
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
MyBean myBean = (MyBean) factory.getBean("myBean");
myBean.doSomething();
}
}
- ApplicationContext — более мощный и функциональный контейнер, подходящий для полноценных приложений.
- BeanFactory — легковесный контейнер, используется редко и преимущественно в ограниченных задачах или для оптимизации ресурсов.
Различия между ApplicationContext и BeanFactory
Критерий | ApplicationContext | BeanFactory |
---|---|---|
Область применения | Полноценный контейнер для разработки современных приложений. | Базовый контейнер для управления бинами. |
Загрузка бинов | Загружает все бины при инициализации контекста (eager loading). | Загружает бины по запросу (lazy loading). |
Поддержка событий | Поддерживает публикацию и обработку событий (ApplicationEvent). | Не поддерживает события. |
Интернационализация | Встроенная поддержка интернационализации (I18N). | Отсутствует. |
Интеграция с AOP | Поддержка AOP для внедрения аспектов. | Ограниченная интеграция с AOP. |
Преимущество производительности | Менее производителен при большом количестве бинов (загрузка всех бинов при старте). | Производителен, так как загружает бины только по запросу. |
Поддержка аннотаций | Полная поддержка аннотаций (например, @Component , @Autowired ). | Ограниченная поддержка. |
Расширенные возможности | Поддержка профилей, автоконфигурации, REST и других функций. | Только базовая работа с бинами. |
Когда использовать
Ситуация | ApplicationContext | BeanFactory |
---|---|---|
Полноценное приложение | Да | Нет |
Требуется работа с событиями | Да | Нет |
Экономия памяти при большом количестве бинов | Нет | Да |
Тестовые приложения | Возможен, но избыточен | Да |
Можно поднять несколько контекстов?
Да, в Spring можно создать несколько контекстов. Это может быть полезно для разделения конфигурации, областей ответственности или работы с вложенными контекстами.
Примеры использования нескольких контекстов
1. Вложенные контексты
Можно создать основной (parent) и дочерний (child) контексты. Основной контекст может предоставлять бины, доступные дочерним контекстам, но не наоборот.
Пример:
ApplicationContext parentContext = new AnnotationConfigApplicationContext(ParentConfig.class);
AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();
childContext.setParent(parentContext);
childContext.register(ChildConfig.class);
childContext.refresh();
2. Несколько независимых контекстов
Если контексты не связаны друг с другом, они не делятся бинами и настройками.
Пример:
ApplicationContext context1 = new AnnotationConfigApplicationContext(AppConfig1.class);
ApplicationContext context2 = new AnnotationConfigApplicationContext(AppConfig2.class);
Преимущества использования нескольких контекстов
- Разделение конфигурации между модулями приложения.
- Изоляция модулей для тестирования.
- Улучшение модульности и читаемости кода.
Недостатки
- Увеличенная сложность управления.
- Возможные конфликты бинов между контекстами.
29. Зачем нужны аннотации @RequestParam и @PathVariable? #
Эти аннотации используются в Spring MVC для извлечения параметров из HTTP-запросов. Они применяются в контроллерах для получения входных данных.
@RequestParam
Аннотация, которая позволяет извлекать параметры запроса (Query Parameters) из URL.
- Используется для параметров, указанных после
?
в строке запроса. - Может задавать значения по умолчанию.
Пример: Запрос:GET /api/users?name=John&page=1
@GetMapping("/api/users")
public String getUser(@RequestParam String name, @RequestParam(defaultValue = "0") int page) {
return "Name: " + name + ", Page: " + page;
}
// Результат: Name: John, Page: 1
@PathVariable
Аннотация, которая позволяет извлекать значения из пути запроса (Path Variables).
- Используется для параметров, включенных в шаблон URL.
Пример: Запрос:GET /api/users/42
@GetMapping("/api/users/{id}")
public String getUserById(@PathVariable int id) {
return "User ID: " + id;
}
// Результат: User ID: 42
30. @ConfigurationProperties? ConfigurationProperties vs Value #
Аннотация @ConfigurationProperties
используется для привязки свойств из файла конфигурации (например, application.properties
или application.yml
) к Java-классу.
Как работает @ConfigurationProperties?
- Привязывает свойства по префиксу к полям класса.
- Удобна для работы с группами связанных настроек.
- Требует аннотации
@EnableConfigurationProperties
или компонентов типа@Component
.
app:
name: MyApp
version: 1.0
Класс конфигурации:
@ConfigurationProperties(prefix = "app")
@Component
public class AppProperties {
private String name;
private String version;
// Getters and setters
}
Использование:
@Component
public class MyService {
private final AppProperties appProperties;
public MyService(AppProperties appProperties) {
this.appProperties = appProperties;
}
public void printConfig() {
System.out.println("App Name: " + appProperties.getName());
System.out.println("Version: " + appProperties.getVersion());
}
}
Сравнение @ConfigurationProperties и @Value
Критерий | @ConfigurationProperties | @Value |
---|---|---|
Привязка групп свойств | Удобна для групп связанных настроек (с префиксом). | Подходит для одиночных свойств. |
Приведение типов | Автоматически поддерживает сложные типы (например, списки). | Поддерживает только базовые типы. |
Работа с файлами YAML | Полностью поддерживается, включая сложные структуры. | Менее удобна для сложных структур. |
Гибкость | Удобна для крупных конфигураций. | Быстрее для мелких настроек. |
Пример использования | @ConfigurationProperties(prefix = "app") | @Value("${app.name}") |
Аннотации | Требует @EnableConfigurationProperties или @Component . | Не требует дополнительных аннотаций. |
Тестирование и валидация | Поддерживает валидацию через @Validated . | Не поддерживает валидацию. |
Когда использовать?
Ситуация | @ConfigurationProperties | @Value |
---|---|---|
Большое количество связанных свойств | Да | Нет |
Одиночное свойство | Нет | Да |
Сложные структуры (списки, объекты) | Да | Нет |
Простые, статические значения | Нет | Да |
31. @Value. Что это? #
@Value
— это аннотация Spring, которая позволяет внедрять значения из конфигурационных файлов (application.properties
, application.yml
), системных переменных или других внешних источников в поля, методы или конструкторы класса.
Основное назначение: получить значение переменной или настроек из конфигурации.
Как использовать @Value?
app.name=MyApp
app.version=1.0
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
@Value("${app.description:Default Description}") // Значение по умолчанию
private String appDescription;
public void printConfig() {
System.out.println("Name: " + appName);
System.out.println("Version: " + appVersion);
System.out.println("Description: " + appDescription);
}
}
// Результат:
// Name: MyApp
// Version: 1.0
// Description: Default Description
32. Как работает DispatcherServlet? #
DispatcherServlet
— это основной компонент Spring MVC, который обрабатывает входящие HTTP-запросы, направляет их к соответствующим контроллерам, обрабатывает ответы и возвращает их клиенту.
Это центральный обработчик HTTP-запросов в Spring MVC.
Основной цикл работы DispatcherServlet
Получение HTTP-запроса:
DispatcherServlet
обрабатывает все запросы, поступающие в приложение (если настроен как фронт-контроллер вweb.xml
или через Java Config).
Определение подходящего контроллера:
- С помощью
HandlerMapping
находит контроллер, который должен обработать запрос, основываясь на URL и методе HTTP.
- С помощью
Выбор метода обработчика:
- С помощью аннотаций контроллера (
@RequestMapping
,@GetMapping
,@PostMapping
и др.) определяется, какой метод должен быть вызван.
- С помощью аннотаций контроллера (
Обработка запроса:
- Контроллер выполняет бизнес-логику и возвращает
ModelAndView
или объект в формате JSON/XML (например, с@ResponseBody
).
- Контроллер выполняет бизнес-логику и возвращает
Обработка ответа:
- Результат передается в компонент
ViewResolver
, который определяет, какое представление (HTML, JSP, JSON и др.) нужно отрендерить.
- Результат передается в компонент
Возврат клиенту:
- Готовый HTTP-ответ отправляется обратно клиенту.
Компоненты в работе DispatcherServlet
Компонент | Описание |
---|---|
HandlerMapping | Определяет, какой обработчик (контроллер) будет вызван для конкретного запроса. |
HandlerAdapter | Определяет, как вызывать метод обработчика (например, метод контроллера). |
ViewResolver | Определяет, какое представление (JSP, Thymeleaf, JSON) нужно использовать для ответа. |
ExceptionResolver | Обрабатывает исключения и может возвращать пользовательские ответы в случае ошибок. |
LocaleResolver | Обеспечивает поддержку локализации, определяя текущий язык и регион пользователя. |
33. Варианты обработки Exceptions в Spring #
1. @ExceptionHandler
- Используется для обработки исключений на уровне контроллера.
- Позволяет привязать обработку конкретных исключений к определённому методу в контроллере.
Пример:
@RestController
public class MyController {
@GetMapping("/test")
public String test() {
throw new IllegalArgumentException("Invalid input");
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException e) {
return ResponseEntity.badRequest().body("Error: " + e.getMessage());
}
}
2. @ControllerAdvice
- Обрабатывает исключения для всех контроллеров в приложении.
- Удобен для создания глобальной обработки ошибок.
Пример:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException e) {
return ResponseEntity.badRequest().body("Global Error: " + e.getMessage());
}
}
3. ResponseStatusException
- Позволяет выбрасывать исключения с указанием HTTP-статуса и сообщения.
Пример:
@GetMapping("/error")
public void error() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Resource not found");
}
4. @ResponseStatus
- Устанавливает HTTP-статус для указанного исключения.
Пример:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Использование в контроллере:
@GetMapping("/resource")
public void getResource() {
throw new ResourceNotFoundException("Resource not found");
}
5. HandlerExceptionResolver
- Низкоуровневый подход для полного контроля над обработкой исключений.
- Требует реализации интерфейса
HandlerExceptionResolver
.
Пример:
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
6. RestTemplate и WebClient
- Для обработки ошибок при выполнении REST-запросов можно использовать кастомные обработчики ошибок, такие как
ResponseErrorHandler
дляRestTemplate
.
Пример:
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
// Обработка ошибки
}
});
Как транзакционная логика обрабатывает исключения?
Spring использует транзакционный менеджер для обработки транзакций. Исключения в транзакционной логике влияют на коммит или откат (rollback) транзакции.
Основные принципы обработки исключений в транзакциях:
Rollback для
RuntimeException
иError
:- По умолчанию, Spring откатывает транзакцию при возникновении
RuntimeException
илиError
. - Исключения типа
Checked Exception
(например,SQLException
) не приводят к откату транзакции, если это явно не указано.
- По умолчанию, Spring откатывает транзакцию при возникновении
Настройка rollback: Можно указать, какие исключения должны приводить к откату.
Пример:
@Transactional(rollbackFor = Exception.class)
public void performTransaction() throws Exception {
// Если выбрасывается Exception, транзакция откатывается
throw new Exception("Transaction failed");
}
Сохранение состояния после исключения: Если транзакция откатывается, изменения, внесённые в базу данных в рамках транзакции, не сохраняются.
Работа с
@Transactional
:- Если транзакция завершается с ошибкой, контейнер Spring вызывает метод
rollback
у используемого транзакционного менеджера. - Для методов
private
или вызовов внутри того же класса обработка транзакций может не сработать.
- Если транзакция завершается с ошибкой, контейнер Spring вызывает метод
Пример:
@Transactional
public void process() {
try {
performAction();
} catch (Exception e) {
// Исключение обработано, транзакция откатывается
throw e;
}
}
private void performAction() {
// Действия, которые вызывают исключение
throw new RuntimeException("Error in action");
}
Основные проблемы и решения:
Проблема | Решение |
---|---|
Внутренний вызов метода с @Transactional . | Использовать прокси или вызывать метод через другой бин. |
Исключение Checked Exception не приводит к rollback. | Указать rollbackFor в аннотации @Transactional . |
Повторное использование транзакции. | Указать параметр propagation , например, Propagation.REQUIRES_NEW для открытия новой транзакции. |
34. @Configuration. Является Configuration компонентом? Какую доп логику Configuration реализует? #
Аннотация @Configuration
является специализированным компонентом Spring. Она указывает, что класс используется для определения конфигурации приложения, включая создание бинов. Этот класс обрабатывается так же, как и класс с аннотацией @Component
.
Дополнительная логика, реализуемая @Configuration
Определение бинов (@Bean): Классы с
@Configuration
позволяют определять методы, помеченные как@Bean
, которые возвращают объекты, управляемые контейнером Spring.Пример:
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyService(); } }
Поддержка прокси (proxyBeanMethods = true): По умолчанию, Spring оборачивает класс с
@Configuration
в прокси для обеспечения корректного создания и управления зависимостями. Это позволяет ссылаться на один и тот же экземпляр бина.Пример:
@Configuration public class AppConfig { @Bean public ServiceA serviceA() { return new ServiceA(serviceB()); } @Bean public ServiceB serviceB() { return new ServiceB(); } }
При вызове
serviceB()
внутри методаserviceA()
контейнер Spring возвращает тот же экземплярServiceB
.Логика для импорта конфигурации: Класс с
@Configuration
может импортировать другие классы конфигурации через@Import
.Пример:
@Configuration @Import(AnotherConfig.class) public class AppConfig {}
Поддержка дополнительных аннотаций: В классах с
@Configuration
можно использовать такие аннотации, как@ComponentScan
,@PropertySource
, и другие для настройки приложения.
@Configuration vs @Component
Аннотации @Configuration
и @Component
в Spring используются для регистрации бинов в контейнере, но они имеют разные цели и поведение.
Критерий | @Configuration | @Component |
---|---|---|
Назначение | Используется для создания конфигурационных классов и бинов. | Используется для обозначения обычного Spring-компонента. |
Создание бинов | Определяет бины через методы с аннотацией @Bean . | Бины создаются автоматически (через сканирование классов). |
Прокси (proxyBeanMethods) | По умолчанию true , что обеспечивает singleton для бинов. | Прокси не используется. |
Семантика | Обозначает, что класс содержит настройки для приложения. | Описывает логический компонент приложения. |
Импорт конфигураций | Поддерживает @Import для подключения других конфигураций. | Не поддерживает импорт. |
Использование вместе с @Bean | Методы с @Bean гарантируют создание одного экземпляра (singleton). | Не имеет встроенной поддержки для @Bean . |
Где использовать | В конфигурационных классах для определения настроек и бинов. | В служебных классах, сервисах, репозиториях и т. д. |
1. @Configuration
Аннотация @Configuration
обозначает, что класс содержит определения бинов и может быть использован Spring-контейнером для генерации и управления этими бинами.
- Используется для конфигурации приложения.
- Прокси: Spring создает прокси для обеспечения правильного вызова методов внутри класса конфигурации.
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA(serviceB());
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
Если метод serviceA()
вызывает serviceB()
, Spring использует прокси, чтобы вернуть тот же экземпляр ServiceB
, созданный контейнером.
2. @Component
Аннотация @Component
регистрирует класс как бин в Spring-контейнере. Она более общего назначения и применяется для различных типов классов (сервисы, репозитории, контроллеры и т. д.).
- Используется для описания компонентов, которые выполняют определенную логику.
- Не создает прокси: методы внутри класса с
@Component
вызываются напрямую
@Component
public class AppConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA(serviceB());
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}
В данном случае метод serviceB()
вызывается каждый раз напрямую, и создается новый экземпляр ServiceB
.
Когда использовать @Configuration и @Component?
@Configuration:
- Если нужно определить сложные зависимости.
- Когда требуется управление экземплярами через прокси.
- Для работы с бинами, определенными вручную через
@Bean
.
@Component:
- Для обозначения служебных классов, выполняющих конкретные задачи.
- Для автоматического сканирования компонентов.
35. @PostConstruct #
Аннотация @PostConstruct
из пакета javax.annotation
указывает метод, который должен быть выполнен после того, как бин был полностью создан и его зависимости внедрены (инициализация). Этот метод вызывается один раз для каждого экземпляра бина.
Цель: Выполнение логики, требующей полностью сконфигурированный бин (например, начальная настройка).
Сколько @PostConstruct можно задекларировать в бине?
Можно задекларировать только один метод с @PostConstruct в одном бине. Если попытаться добавить несколько методов с этой аннотацией, Spring выбросит ошибку.
36. Dependency management в Spring boot #
Dependency Management — это управление зависимостями приложения. В Spring Boot используется механизм Maven или Gradle для автоматического добавления, обновления и совместимости библиотек.
1. Spring Boot Starter Dependencies:
- Группы зависимостей (например,
spring-boot-starter-web
), которые включают всё необходимое для работы. - Упрощают настройку: вместо добавления каждой библиотеки вручную, добавляется “стартер”.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. Spring Boot Dependency Management:
- Использует BOM (Bill of Materials) для управления версиями зависимостей.
- Версии зависимостей определяются в родительском POM (
spring-boot-dependencies
), что гарантирует их совместимость.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
</parent>
3. Профили зависимостей: Можно включать зависимости в зависимости от среды выполнения (например, dev, test, prod) с помощью профилей Maven.
4. Эксплицитные зависимости: Spring Boot позволяет переопределить версию зависимости, если требуется особая версия.
37. Spring Security, как хранить пароль пользователя? #
Правила безопасного хранения паролей:
- Не хранить пароли в виде открытого текста.
- Использовать хэширование: Пароль перед сохранением в базе данных должен быть преобразован в хэш (необратимое преобразование).
Как хранить пароль в Spring Security?
Хэширование с помощью BCrypt: Spring Security предоставляет поддержку алгоритма BCrypt, который подходит для хэширования паролей.
Пример:
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
При сохранении пароля:
@Autowired private PasswordEncoder passwordEncoder; public void saveUser(String rawPassword) { String hashedPassword = passwordEncoder.encode(rawPassword); // Сохранение hashedPassword в базе данных }
Проверка пароля: При аутентификации введённый пароль сравнивается с хэшированным значением в базе данных:
boolean matches = passwordEncoder.matches(rawPassword, hashedPassword);
Где хранить пароли?
В базе данных:
- Пароли сохраняются в виде хэшей, используя безопасные алгоритмы (например, BCrypt, PBKDF2, Argon2).
- Используйте столбцы с ограничением по длине, чтобы избежать уязвимости типа переполнения.
Пример схемы таблицы:
CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL );
Почему BCrypt?
- Солирование: BCrypt автоматически добавляет случайную “соль” к каждому паролю, чтобы одинаковые пароли имели разные хэши.
- Замедление: У BCrypt есть возможность настройки сложности (work factor), что увеличивает время вычисления хэша, защищая от атак перебором.
38. Spring Profiles как с ними работать? #
Spring Profiles позволяют загружать разные настройки в зависимости от окружения (разработка, тестирование, прод).
1️⃣ Как задать профиль?
📌 1. В application.properties/yml
📌 Указываем профиль в application.properties
:
spring.profiles.active=dev
📌 Или в application.yml
:
spring:
profiles:
active: dev
📌 2. Через аргументы JVM (-Dspring.profiles.active=prod
)
Можно передавать профиль при запуске:
java -jar myapp.jar --spring.profiles.active=prod
2️⃣ Как создать разные настройки для профилей?
📌 Способ 1: Разные файлы application-{profile}.properties
application-dev.properties
(локальная разработка)application-prod.properties
(продакшен)application-test.properties
(тестирование)
Пример application-dev.properties
:
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/dev_db
Пример application-prod.properties
:
server.port=80
spring.datasource.url=jdbc:mysql://prod-db:3306/prod_db
📌 Способ 2: Использование @Profile
в коде
@Component
@Profile("dev")
public class DevDataSource implements DataSourceConfig {
@Override
public void setup() {
System.out.println("DEV database setup");
}
}
@Component
@Profile("prod")
public class ProdDataSource implements DataSourceConfig {
@Override
public void setup() {
System.out.println("PROD database setup");
}
}
Если активен профиль dev
, то создастся DevDataSource
, иначе — ProdDataSource
.
📌 Способ 3: Использование @IfProfileValue
Этот метод работает только для простых значений (не динамические профили).
@IfProfileValue(name = "spring.profiles.active", notValue = "prod")
public void runOnlyInDev() {
System.out.println("Этот код выполняется НЕ в проде!");
}
39. Виды proxy в Spring #
Spring использует динамическое проксирование (Proxy
) для создания AOP-оберток над бинами.
Тип Proxy | Описание | Как создается? |
---|---|---|
JDK Dynamic Proxy | Использует java.lang.reflect.Proxy | Для интерфейсов |
CGLIB Proxy | Использует net.sf.cglib.proxy | Для классов |
AspectJ Proxy | Используется в Spring AOP | Нужно доп. подключение |
1️⃣ JDK Dynamic Proxy (java.lang.reflect.Proxy
)
- Используется только для интерфейсов.
- Создает прокси-класс, который реализует интерфейс.
📌 Пример:
public interface Service {
void doWork();
}
public class ServiceImpl implements Service {
public void doWork() {
System.out.println("Работа выполнена!");
}
}
Создание динамического прокси вручную:
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
(proxyObj, method, args) -> {
System.out.println("Логирование перед вызовом метода...");
return method.invoke(new ServiceImpl(), args);
}
);
proxy.doWork();
📌 Вывод:
Логирование перед вызовом метода...
Работа выполнена!
2️⃣ CGLIB Proxy (net.sf.cglib.proxy
)
- Используется для классов (даже если нет интерфейса).
- Создает подкласс оригинального класса.
📌 Пример:
public class Service {
public void doWork() {
System.out.println("Работа выполнена!");
}
}
Создание CGLIB-прокси вручную:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("Логирование перед вызовом метода...");
return proxy.invokeSuper(obj, args);
});
Service proxy = (Service) enhancer.create();
proxy.doWork();
📌 Вывод:
Логирование перед вызовом метода...
Работа выполнена!
Spring cglib vs dynamic proxy
Функция | JDK Dynamic Proxy | CGLIB Proxy |
---|---|---|
Где работает? | Только с интерфейсами | Работает с классами |
Как создается? | java.lang.reflect.Proxy | net.sf.cglib.proxy.Enhancer |
Наследование | Нельзя наследовать логику класса | Создает подкласс |
Производительность | Чуть быстрее | Немного медленнее |
Какое proxy по-умолчанию в Spring?
📌 Spring использует JDK Dynamic Proxy
по умолчанию.
Когда используется CGLIB?
- Если класс не реализует интерфейс.
- Если явно указать:
@EnableAspectJAutoProxy(proxyTargetClass = true)
📌 Как проверить, какой Proxy используется?
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Object proxy = context.getBean(MyService.class);
System.out.println(proxy.getClass());
✅ Выведет com.sun.proxy.$Proxy...
для JDK Dynamic Proxy или MyService$$EnhancerByCGLIB$$...
для CGLIB.
40. @Lookup #
@Lookup
— это аннотация в Spring, позволяющая в singleton-бине (одиночке) получать новый экземпляр prototype-бина при каждом вызове метода. Без @Lookup
при внедрении prototype-бина в singleton он создаётся только один раз.
Как это работает
- Ты помечаешь метод в singleton-классе аннотацией
@Lookup
. - Spring динамически переопределяет этот метод (через CGLIB).
- При каждом вызове метода Spring запрашивает новый prototype-бин из контекста.
Пример кода
@Component
@Scope("prototype")
public class PrototypeBean {
public void doSomething() {
System.out.println("PrototypeBean: " + this);
}
}
@Component
public abstract class SingletonBean {
public void process() {
// Каждый раз получаем новый экземпляр PrototypeBean
PrototypeBean prototypeBean = getPrototypeBean();
prototypeBean.doSomething();
}
@Lookup
protected abstract PrototypeBean getPrototypeBean();
}
Что здесь происходит?
SingletonBean
— бин с scope = singleton (по умолчанию).PrototypeBean
— бин с scope = prototype.- Метод
getPrototypeBean()
аннотирован@Lookup
, и Spring подменяет его реализацию: при вызовеgetPrototypeBean()
возвращается новыйPrototypeBean
.
Зачем нужно
- Когда в singleton-бине надо часто создавать prototype-объекты.
- Чтобы не использовать вручную
ApplicationContext.getBean()
. - Для избежания утечек памяти (прототипы не живут вечно внутри синглтона).