Сибирские интеграционные системы
AngularSIS #1.18 - https://www.youtube.com/playlist?list=PLmEQRj1_Mt5fkeBYOw1o8k_o8-7POFZJN JavaSIS #2.19 - https://www.youtube.com/playlist?list=PLmEQRj1_Mt5f5MlXGlf5kzldb9Rl8Pwuo
Михаил
Павлов
Нет, погодите.
А что такое Spring?
*играет гнетущая музыка*
Это приводило к следующим проблемам:
Общая сложность кода
Запутанность бизнес-логики
Потеря времени на реализацию служебных методов
Большое количество ошибок
Много инфраструктурного (служебного) кода.
В октябре 2002 вышла книга Рода Джонсона с попыткой пересмотреть подход Java EE.
К книге прилагался репозиторий с первой версией фреймворка Spring
*Легковесность - невмешательство в бизнес-логику
Bean и Spring IoC контейнер
Dependency Injection (DI)
Жизненный цикл бинов
Конфигурирование
Aspect Oriented Programming (AOP)
Spring IoC контейнер - объект, отвечающий за хранение бинов и внедрение зависимостей (DI).
Spring IoC контейнер
Конфигурация
Бизнес-логика
Готовое приложение
*IoC - Invertion of Control (инверсия управления)
(Инверсия управления)
Класс сам не должен создавать нужные ему объекты
Суть принципа - убрать управление зависимостями из бизнес-логики, что позволяет делать более модульные приложения
"Не звоните нам, мы сами позвоним вам!"
(Внедрение зависимостей)
Паттерн, реализующий принцип Invertion of Control
Варианты внедрения:
private BigDecimal calculateProductPrice(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);
}
Инфраструктурный код
"Полезный" код
Пусть зависимости подключит кто-нибудь другой
Например Spring
(с помощью DI)
private ProductService productService;
public void setProductService(ProductService productService) {
this.productService = productService;
}
private ProductService productService;
public PriceCalculator(ProductService productService) {
this.productService = productService;
}
Setter
Конструктор
private BigDecimal calculateProductPrice(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;
public PriceCalculator(ProductService productService) {
this.productService = productService;
}
private BigDecimal calculateProductPrice2(Product product) {
return productService.calculatePrice(product,
productPriceRepository, productRepository);
}
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]
private final Class2 class2;
public Class1(Class2 class2) {
this.class2 = class2;
}
-----------------------------------
private final Class1 class1;
public Class2(Class1 class1) {
this.class1 = class1;
}
Class1
Class2
Class1
Class2
ClassLink
Class1
Class2
public ClassLink() {}
-----------------------------------
private final ClassLink classLink;
private final Class2 class2;
public Class1(ClassLink classLink,
Class2 class2) {
this.classLink = classLink;
this.class2 = class2;
}
-----------------------------------
private final ClassLink classLink;
public Class2(ClassLink classLink) {
this.classLink = classLink;
}
Class1
Class2
ClassLink
public EntityRepository() {}
-----------------------------------
private final SynService synService;
private final EntityRepository entityRepository;
public EntityService(SynService synService,
EntityRepository entityRepository) {
this.synService = synService;
this.entityRepository = entityRepository;
}
-----------------------------------
private final EntityRepository entityRepository;
public SynService(EntityRepository entityRepository) {
this.entityRepository = entityRepository;
}
private final SynService synService;
public EntityService(SynService synService) {
this.synService = synService;
}
-----------------------------------
private final EntityService entityService;
public SynService(EntityService entityService) {
this.entityService = entityService;
}
Bean и Spring IoC контейнер
Dependency Injection (DI)
Жизненный цикл бинов
Конфигурирование
Aspect Oriented Programming (AOP)
Основные стадии:
* На самом деле стадий больше
Хранится в специальном интерфейсе BeanDefinition и грузится из конфигурации (о ней чуть позже)
Помним, что бин - это экземпляр класса бизнес-логики
*Помним про легковесность - нельзя хранить всё это в POJO
Основные виды бинов (по времени жизни):
* Подробнее по ссылке
"- А мне говорили, что singleton это плохо!
- It depends..."
Потенциальные проблемы:
*За singleton следит сам Spring
Предпочтительный вариант сервиса - менеджер, который передает задачу другим классам
public class ServiceAntiPattern {
private BigDecimal debtSum;
public BigDecimal сalculateDebtOnToday(Client client) {
loadCurrentDebt(client);
calculateNewDebt(client);
return debtSum;
}
private void loadCurrentDebt(Client client) {
debtSum = debtService.loadDebt(client);
}
private void calculateNewDebt(Client client) {
debtSum = debtService.calculateDebtForClient(client, debtSum);
}
}
поток 2
поток 1
Если нужно вставлять в класс бины и при этом хранить в классе состояние:
Основные методы:
*также см. интерфейсы InitializingBean и DisposableBean (ссылка)
Bean и Spring IoC контейнер
Dependency Injection (DI)
Жизненный цикл бинов
Конфигурирование
Aspect Oriented Programming (AOP)
Spring IoC контейнер
Конфигурация
Бизнес-логика
Готовое приложение
Сервис - выдающий домашних питомцев с их игрушками
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="PetServiceCat"
class="com.github.siberianintegrationsystems.xml.CatShop" primary="true" >
<constructor-arg type="com.github.siberianintegrationsystems.xml.Toy"
ref="toy" />
</bean>
<bean id="PetServiceDog" class="com.github.siberianintegrationsystems.xml.DogShop" />
<bean name="toy" class="com.github.siberianintegrationsystems.xml.Toy">
<constructor-arg name="name" value="mouse" />
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("ApplicationXMLConfig.xml");
Плюсы
Минусы
Объявляем бины в коде
Классы с конфигурацией помечаем @Configuration
@Configuration
public class JavaClassConfiguration {
@Bean
@Primary
public PetService petServiceCat() {
return new CatShop(new Toy("mouse"));
}
}
Плюсы
Минус
Аннотации служат для
Рассмотрим приложение для приветствий
Почему оно не работает?
Ранее мы явно определяли зависимости.
Этот код не говорит, что нужно инжектировать бин:
void setGreeterService(GreeterService greeterService) {
this.greeterService = greeterService;
}
Аннотация @Autowired на поле\сеттере\ конструкторе заставляет IoC искать подходящий бин.
На единственный конструктор в объекте спринг всегда вешает аннотацию @Autowired
Плюс
Минусы
* в Java EE добавили аннотацию @Inject, которую поддерживает и Spring, можно использовать ее вместо @Autowired.
Но на практике смена фреймворка трудна и затратна
Мы рассмотрели все существующие способы конфигурирования
На практике мы используем смесь аннотаций и JavaBased
Мозг
Информация
Bean и Spring IoC контейнер
Dependency Injection (DI)
Жизненный цикл бинов
Конфигурирование
Aspect Oriented Programming (AOP)
Позволяет использовать общий (типовой, повторяющийся) функционал в вашем коде с помощью аннотаций
Примеры:
public void doSomething() {
final String METHODNAME = "doSomething";
logger.trace("entering " + CLASSNAME + "." + METHODNAME);
TransactionStatus tx = transacationManager.getTransaction(
new DefaultTransactionDefenition());
try {
// Business logic
transactionManager.commit(tx);
} catch (RuntimeException ex) {
logger.error("exception in " + CLASSNAME + "." + METHODNAME, ex);
tx.setRollbackOnly();
} finally {
logger.trace("exiting " + CLASSNAME + "." + METHODNAME);
}
}
@Transactional
public void placeOrder(Order order) {
order.statusOrdered();
shipService.planShipping(order);
orderRepository.saveOrder(order);
}
Ответ - Proxy
Spring создает класс-обертку вокруг вашего класса, с теми же методами.
При получении бина вы получаете прокси, который и делает сначала магию, а потом вызывает ваш код.
!!!method2 вызовет не прокси!!!
public class MyServiceImpl {
@Transactional
public void method1() {
//do something
method2();
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void method2() {
//do something
}
}
Bean и Spring IoC контейнер
Dependency Injection (DI)
Жизненный цикл бинов
Конфигурирование
Aspect Oriented Programming (AOP)
Несколько ссылок про SpEL:
Сегодня задания не будет
By Сибирские интеграционные системы
Введение в Spring