Занятие № 6
Spring Core
Кернер
Денис
План занятия
- Что за Spring и Core?
- О Dependency Injection
- Конфигурирование Spring приложения
- Жизненный цикл бина
- SpEL
- AOP
- Итоги
Что за Spring Core?
Нет, погодите. А что за Spring?
Шел 2002 год, господствовала технология JEE
В чем была проблема JEE?
Много инфраструктурного кода приводило к:
- Сложности
- Запутанности бизнес - логики
- Потерям времени
- Большому количеству ошибок
Нужен легковесный фреймворк!
В 2002 вышла книга Рода Джонсона с попыткой пересмотреть подход JEE.
К книге прилагался репозиторий с первой версией фреймворка
Spring вырос очень быстро..
JEE стал проще и легче, копируя Spring :)
В чем легковесность Spring?
Программист работает с POJO
POJO (plain old java object)
Объекты, с которыми вы работаете, должны как можно меньше знать о фреймворке
А где Spring Core?
Что за Spring Core???
Ядро фреймворка отвечает за:
- внедрение зависимостей
- жизненный цикл бинов
Основные элементы Spring Core
- Внедрение зависимостей - DI
- Управление жизненным циклом бинов
- Конфигурирование
- AOP
Dependency Injection
Внедрение зависимостей
Зачем?
Ситуация:
Мы хотим рассчитать цену на товар.
Связи между классами для подсчета
Что не так с данным кодом?
private BigDecimal calculateProductPriceForClient(
Client client, Product product
) {
DataSource dataSource = new DataSource("url to db");
ProductRepository productRepository =
new ProductRepository(dataSource);
ProductPriceRepository productPriceRepository =
new ProductPriceRepository(dataSource);
ProductService productService =
new ProductServiceImpl();
return productService.calculatePrice(product,
productPriceRepository, productRepository);
}
Слишком крепкая связь
Мы жестко контролируем создание нужных классов
Как сделать лучше?
Мы должны использовать уже готовый объект.
private final ProductService productService;
private BigDecimal calculateProductPriceForClient2(Client client,
Product product) {
return productService.calculatePrice(product,
productPriceRepository, productRepository);
}
Переворачиваем цепочку контроля.
DI - форма Inversion of Control (IoC)
Что же такое IoC?
- Реализация класса не должна зависеть от другой реализации.
- Класс сам не должен создавать нужные ему объекты
IoC - инверсия управления
Голливудский принцип
"Не звоните нам, мы сами позвоним вам!"
Виды Dependency Injection
Основными способами являются:
- Конструктор
- Сеттер
Виды Dependency Injection - пример
private ProductService productService;
public void setProductService(ProductService productService) {
this.productService = productService;
}
public PriceCalculator(ProductService productService) {
this.productService = productService;
}
Как думаете, какой способ лучше?
DI - через конструктор!
- Вы можете применить final
- У вас полностью сконфигурированный объект
Циклические зависимости
APPLICATION FAILED TO START
Description:
The dependencies of some of the beans
in the application context form a cycle:
wsConfig defined in file
[C:\gitlab\citizenfeedback\server\build\classes\
java\main\pro\sisit\etalon\citizen\feedback\
config\WsConfig.class]
| syncServiceImpl defined in file
[C:\gitlab\citizenfeedback\server\build\classes
\java\main\pro\sisit\etalon\citizen\
feedback\sync\SyncServiceImpl.class]
Что мы уже изучили?
Dependency Injection- IoC контейнер
- bean
- configuration metadata
Spring IoC контейнер
Объект, отвечающий за хранение бинов и внедрение зависимостей (DI).
Bean (бин)
POJO, создаваемый и управляемый Spring IoC контейнером
Bean можно внедрять через DI
private final ProductService productService;
public PriceCalculator(ProductService productService) {
this.productService = productService;
}
Жизненный цикл бина
Свойства бина.
- Scope (область) время жизни
- Имя
- Хуки на инициализацию и уничтожение
Все это хранится в специальном интерфейсе BeanDefinition и грузится из конфигурации.
Scope (время жизни)
Основные
- Singleton - одиночка
- Prototype - на каждый запрос внедрения зависимости
Только для веб:
- Request (на http запрос)
- Session
- Websocket
Scope - singleton
А мне говорили, что singleton это плохо!
Работу singletone обеспечивает Spring.
Использовать можно, если помнить противопоказания
Scope - singleton. Ограничения
- Используется для сервисов
- Сервисы не должны хранить состояние
- Небезопасен при работе с множеством потоков, синхронизация - на вас.
Scope - singleton. Ограничения потоков
public class ServiceAntiPattern {
private BigDecimal debtSum;
private DebtService debtService;
private InterestService interestService;
public BigDecimal CalculateDebtOnToday(Client client) {
loadCurrentDebt(client);
calculateInterest();
return debtSum;
}
private void loadCurrentDebt(Client client) {
debtSum = debtService.loadDebt(client);
}
private void calculateInterest(Client client) {
debtSum = interestService.calculateInterest(client, debtSum);
}
}
Scope - singleton. Ограничения потоков
private class ServiceAntiPatternState {
private CreateObjectDetails createObjectDetails;
public void initName(String name) {
createObjectDetails.name = name;
}
public void createObject(Long id) {
createObjectDetails.id = id;
return save(createObjectDetails);
}
}
В промежутке между вызовами initName и createObject - могут вызвать еще раз initName
Как хранить состояние?
Если нужно вставлять в класс бины и при этом хранить в классе состояние:
- Используйте scope prototype
- Либо создайте сервис - фабрику классов
Что нужно для старта Spring приложения?
- Создать конфигурацию, описывающую свойства бинов
- Загрузить в IoC контейнер конфигурацию
Какие виды конфигурирования бывают?
- XML файлы
- java классы
- аннотации
Пример: конфигурация XML
Небольшой пример - сервис, выдающий домашних питомцев.
PetService
Итоги XML
Плюсы
- очень мощный инструмент
- минимум воздействия на код
Минусы
- XML для машин
- лавинообразный рост XML для большого приложения
- ошибки видим в runtime
Конфигурация через Java классы
Объявляем бины в коде
Классы с конфигурацией помечаем @Configuration
@Configuration
public class DataConfiguration {
@Bean(name = "laborDataRep")
public LaborDataService laborDataService() {
return new LaborDataServiceImpl();
}
}
Итоги. Конфигурация через Java классы
Плюсы
- Пишем код
- Есть проверка типов
- Минимальное влияние на прикладной код
Минус
- Нельзя на ходу поменять конфигурацию
Конфигурация через аннотации
Аннотации служат для
- Объявления компонентов: @Component, @Service
- Сканирования пакетов для поиска объявленных компонентов @ComponentScan
- Для конфигурирования @Configuration
Аннотации - демонстрация
Рассмотрим приложение для приветствий
Почему оно не работает?
Указание внедрения зависимости
Ранее мы явно определяли зависимости.
Этот код не говорит, что нужно инжектировать бин:
void setGreeterService(GreeterService greeterService) {
this.greeterService = greeterService;
}
Выход - автопривязка @Autowired
Аннотация @Autowired на поле\сеттере\ конструкторе заставляет IoC искать подходящий бин.
На единственный конструктор в объекте спринг всегда вешает аннотацию @Autowired
Итоги - Аннотации
Плюс
- Самый лаконичный способ
Минусы
- Мы добавляем очень много аннотаций.
- Код зависит от фреймворка.
Итоги. Конфигурирование
Мы рассмотрели все существующие способы конфигурирования
На практике мы используем смесь аннотаций и JavaBased
Жизненный цикл бина
Рекомендуемые способы:
- @PostConstruct (JSR 250)
- @Bean’s initMethod
- @PreDestroy (JSR 250)
- @Bean’s destroyMethod
Также см. InitializingBean и DisposableBean
Жизненный цикл бина - демо
- Life Cycle: @onPostConstruct
- Life Cycle: onInited
- Life Cycle: @onPreDestroy
- Life Cycle: onDestroy
Что еще есть в Spring Core
Из полезного
- SpEL
- AOP
- Валидация и привязка данных
SpEL (spring expression language)
Язык выражений Spring Framework
Может использовать методы и классы Spring и Java, может работать с контекстом
Примеры SpEL
Demo
Вывод - мощно.
@PreAuthorize("hasPermission(#contact, 'admin')")
void deletePermission(Contact contact, Sid recipient,
Permission permission);
I'll put a SpEL on you!
Мощно. Непонятно. Волшебно.
Есть уязвимости. Использовать с осторожностью
AOP - aspect oriented programming
Позволяет использовать кросс функционал в вашем коде с помощью аннотаций
Примеры:
- транзакции
- логирование
- кэширование
Зачем нужен?
AOP - пример
@Transactional
public void placeOrder(Order order) {
order.statusOrdered();
shipService.planShipping(order);
orderRepository.saveOrder(order);
}
AOP - что происходит при вызове метода
AOP - а как "вжух" работает?
Proxy
Spring создает класс-обертку вокруг вашего класса, с теми же методами.
При получении бина вы получаете прокси, который и делает сначала магию, а потом вызывает ваш код.
AOP западня
!!!method2 вызовет не прокси!!!
public class MyServiceImpl {
@Transactional
public void method1() {
//do something
method2();
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void method2() {
//do something
}
}
Домашнее задание
Спасибо за внимание!
JavaSIS#19 unit 6 spring core
By Dennis Kerner
JavaSIS#19 unit 6 spring core
- 319