Занятие № 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
  }
}

Домашнее задание

Спасибо за внимание!

Made with Slides.com