Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

36
Dependency Injection в Java на примере эволюции Spring Guice CDI/Weld Сергей Кошель Ведущий разработчик Java 23 мая 2013 года

description

Открытый семинар для студентов в компании CUSTIS (23 мая 2013). Лектор: Сергей Кошель, ведущий разработчик Java, аналитик. Аннотация: Из этого семинара вы узнаете о практическом применении паттерна Dependency Injection в мире Java и предоставляемых им возможностях на примере развития DI-фреймворков: от Spring и Guice до CDI/Weld. Формат встречи – динамичный с элементами Live Coding и демонстрацией особенностей реализации. Видеозапись семинара: https://vimeo.com/67125102.

Transcript of Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Page 1: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Dependency Injection в Java

на примере эволюции

Spring – Guice – CDI/Weld

Сергей Кошель

Ведущий разработчик Java

23 мая 2013 года

Page 2: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Разрабатываем адаптер

Преобразование

2/36

Page 3: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Первая версия

SimpleConverter

FileSource DatabaseStorage

Adapter

3/36

Page 4: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Первая версия

+ Работать будет

− Невозможно написать unit-тест

public class Adapter { public void processMessage() { final FileSource fileSource = new FileSource(); final SimpleConverter simpleConverter = new SimpleConverter(); final DatabaseStorage databaseStorage = new DatabaseStorage(); final Message inputMessage = fileSource.getMessage(); final Message convertedMessage = simpleConverter.convert(inputMessage); databaseStorage.store(convertedMessage); } }

4/36

Page 5: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Выделяем интерфейсы

MockConverter

MockSource MockStorage

FileSource

SimpleConverter

DatabaseStorage

Source

Converter

Storage

Adapter 5/36

Page 6: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

И добавляем фабрики

public class Adapter { public void processMessage() { final Source source = SourceFactory.getSource(); final Converter converter = ConverterFactory.getConverter(); final Storage storage = StorageFactory.getStorage(); final Message inputMessage = source.getMessage(); final Message convertedMessage = converter.convert(inputMessage); storage.store(convertedMessage); } }

6/36

Page 7: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Пишем тест

@Test public void processMessage() throws Exception { SourceFactory.setSource(new MockSource("Hello from test!")); StorageFactory.setStorage(new MockStorage()); final Adapter adapter = new Adapter(); adapter.processMessage(); // assert that... }

+ Получилось написать тест

− Статический (глобальный) контекст

− Много бойлерплейта

− Зависимости неочевидны 7/36

Page 8: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Избавляемся от фабрик

public class Adapter { private Source source; private Converter converter; private Storage storage; public void setSource(Source source) {…} public void setConverter(Converter converter) {…} public void setStorage(Storage storage) {…} public void processMessage() { final Message inputMessage = source.getMessage(); final Message convertedMessage = converter.convert(inputMessage); storage.store(convertedMessage); } }

8/36

Page 9: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Переписываем тест

@Test public void processMessage() throws Exception { final Adapter adapter = new Adapter(); adapter.setSource(new MockSource("Hello from test!")); adapter.setConverter(new SimpleConverter()); adapter.setStorage(new MockStorage()); adapter.processMessage(); // assert that... }

+ Получилось:

Setter based Dependency Injection by Hand

9/36

Page 10: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Последний штрих

public class Adapter { private final Source source; private final Converter converter; private final Storage storage; public Adapter(Source source, Converter converter, Storage storage) { this.source = source; this.converter = converter; this.storage = storage; } public void processMessage() {…} }

10/36

Page 11: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Dependency Injection (DI)

Паттерн проектирования (design pattern)

О компонентах и их зависимостях

Позволяет отделить объявление

зависимости от разрешения зависимости

(и в пространстве, и во времени)

Является частью более общего принципа

Inversion of Control (Hollywood principle –

«Don't call us, we'll call you».)

11/36

Page 12: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Причем тут тесты?

Тесты не самоцель, но…

С одной стороны, практически,

DI позволяет проще писать

тестопригодный код

А с другой стороны, концептуально,

тесторигодность кода является

индикатором хорошей слабосвязанной

архитектуры (loose coupling)

В конечном итоге DI помогает удобнее

писать слабосвязный код

12/36

Page 13: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Можно было пойти другим путем

public class Adapter { public void processMessage() { final Source source = UniversalFactory.get("source", Source.class); final Converter converter = UniversalFactory.get("converter", Converter.class); final Storage storage = UniversalFactory.get("storage", Storage.class); final Message inputMessage = source.getMessage(); final Message convertedMessage = converter.convert(inputMessage); storage.store(convertedMessage); } }

+ Получилось: Service Locator

13/36

Page 14: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Паттерны DI и SL

часто противопоставляются

DI зависимости определяет статически

Проще разобраться в связях

Компилятор многое может проверить

и подсказать

Но иногда это является ограничением

SL – динамически

Взаимосвязи запутаны, проще ошибиться

Но иногда без этого не обойтись

14/36

Page 15: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

IoC-контейнер

Автоматизирует DI

Разрешает граф зависимостей

Конструирует компоненты по метаописанию

зависимостей

И привносит еще много полезностей

Управление жизненным циклом

Управление конфигурацией

AOP

15/36

Page 16: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Disclaimer

Автор не в коем случае не имеет

цели принизить один фреймворк

за счет другого

16/36

Page 17: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Spring Framework

17/36

Page 18: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Описываем зависимости

<?xml version="1.0" encoding="UTF-8"?> <beans …> <bean id="adapter" class="custis.seminars.diinjava.spring.Adapter" > <property name="source" ref="source"/> <property name="converter" ref="converter"/> <property name="storage" ref="storage"/> </bean> <bean id="source" class="custis.seminars.diinjava.spring.FileSource" /> <bean id="converter" class="custis.seminars.diinjava.spring.SimpleConverter" /> <bean id="storage" class="custis.seminars.diinjava.spring.DatabaseStorage" /> </beans>

* Похоже на императив, но это декларатив 18/36

Page 19: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Запускаем контейнер

final ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml"); adapter = applicationContext.getBean("adapter", Adapter.class);

+ Код адаптера не изменился

+ …нет зависимости от Spring’а

+ …не надо его писать в каком-либо специальном стиле

19/36

Page 20: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Autowiring

<bean id="adapter" class="custis.seminars.diinjava.autowiring.Adapter" autowire="byType" />

20/36

Page 21: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Lifecycle Management

singleton – создается один экземпляр

prototype – создается отдельный экземпляр

при каждом обращении

<bean id="adapter" class="custis.seminars.diinjava.autowiring.Adapter" autowire="byType" scope="singleton" init-method="init" destroy-method="close" /> […] applicationContext.destroy();

21/36

Page 22: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Метаданные в компоненте

@Scope(SCOPE_SINGLETON) public class Adapter { private Source source; private Converter converter; private Storage storage; @Autowired public void setSource(Source source) {…} @Autowired public void setConverter(Converter converter) {…} @Autowired(required = false) public void setStorage(Storage storage) {…} @PostConstruct public void init() {…} @PreDestroy public void close() {…} } 22/36

Page 23: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Выбор

между несколькими реализациями

23/36

<bean id="source" class="custis.seminars.diinjava.autowiring.FileSource" /> @Autowired @Qualifier("source") public void setSource(Source source) {…}

− Легко ошибиться, и проявится это только в рантайме

Page 24: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Google Guice

24/36

Page 25: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Pure Java config

public class AdapterModule extends AbstractModule { @Override protected void configure() { bind(SimpleConverter.class); bind(Source.class).to(FileSource.class); bind(Storage.class).toInstance(new DatabaseStorage()); } } final Injector injector = Guice.createInjector(new AdapterModule()); adapter = injector.getInstance(Adapter.class);

25/36

Page 26: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Annotation based

@Singleton public class Adapter { private final Source source; private final Converter converter; private final Storage storage; @Inject public Adapter(Source source, Converter converter, Storage storage) { this.source = source; this.converter = converter; this.storage = storage; } public void processMessage() {…} }

* Зависимость от аннотаций, но они стандартные

26/36

Page 27: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Pure Java config

public class AdapterModule extends AbstractModule { @Override protected void configure() {…} @Provides Source fileSource() { return new FileSource(); } }

* В Spring 3.0 появился JavaConfig

27/36

Page 28: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Provider interface

public interface Provider <T> { T get(); } bind(Validator.class).to(SimpleValidator.class); public class Adapter { private Provider<Validator> validator; @Inject public void setValidator(Provider<Validator> validator) {…} public void processMessage() { ... validator.get().validate(inputMessage); ... } } 28/36

Page 29: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Provider interface

Когда нужно…

отложить создание (тяжелое, условное)

много экземпляров (the new «new»)

вложить более узкий скоуп в широкий

29/36

Page 30: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Выбор

между несколькими реализациями

30/36

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) public @interface FileBased {} @Inject public Adapter(@FileBased Source source, Converter converter, Storage storage) {…} bind(Source.class).annotatedWith(FileBased.class).to(FileSource.class);

+ Typesafe – компилятор проверит

Page 31: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

CDI/Weld

JSR 299: Contexts and Dependency Injection

for the Java EE platform

Weld – reference implementation

for JSR-299

31/36

Page 32: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

CDI

Конфигурация похожа на Guice

Нет DSL — используется @Produce и сканирование classpath

Стандартизирует @Inject, @Sengleton,

Provider<T> и т. д.

Тесно интегрируется с EJB-контейнером

32/36

Page 33: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Instance – Provider на стероидах

Расширяет возможности Provider

Instance<T> extends Provider<T>

…опциональные зависимости

if (instance.isUnsatisfied()) {…}

…многозначные зависимости

if (instance.isAmbiguous()) {

for (T t : instance) {...}

}

…динамическое разрешение зависимостей (SL)

adapter = instance.select(Adapter.class).get();

33/36

Page 34: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Event<T>

public class Adapter { @Inject Event<AdapterStarted> adapterStartedEvent; @PostConstruct public void init() { adapterStartedEvent.fire(new AdapterStarted()); } } public class AnyOtherManagedBean{ public void onAdapterStart( @Observes AdapterStarted adapterStarted) {…} }

34/36

Page 35: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

О чем не рассказал

AOP и method intercepting

Генерализованные типы зависимостей

Многозначные зависимости

Scopes

35/36

Page 36: Dependency Injection в Java на примере эволюции Spring — Guice — CDI/Weld

Спасибо!

Вопросы?

Сергей Кошель

[email protected]

36/36