Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 3629 сообщений
Cообщения с меткой

spring - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

[Из песочницы] Разработка telegram бота с использованием Spring

Суббота, 12 Августа 2017 г. 14:43 (ссылка)

Пишите телеграмм ботов? Ваша производительность разработки желает лучшего? Ищите чего-то нового? Тогда прошу под кат.





Идея заключается в следующем: слямзить архитектуру spring mvc и перенести на telegram api.

Выглядеть должно как-то так:



@BotController
public class SimpleOkayController {
@BotRequestMapping(value = "/ok")
public SendMessage ok(Update update) {
return new SendMessage()
.setChatId(update.getMessage().getChatId())
.setText("okay bro, okay!");
}
}


или



Пример с бинами
@BotController
public class StartController {
@Autowired
private Filter shopMenu;

@Autowired
private PayTokenService payTokenService;

@Autowired
private ItemService itemService;

@BotRequestMapping("/shop")
public SendMessage generateInitMenu(Update update) {
return new SendMessage()
.setChatId(update.getMessage().getChatId().toString())
.setText("Товары моего магазинчика!")
.setReplyMarkup(shopMenu.getSubMenu(0L, 4L, 1L)); // <--
}

@BotRequestMapping(value = "/buyItem", method = BotRequestMethod.EDIT)
public List bayItem(Update update) {
....................
Item item = itemService.findById(id); // <--

return Arrays.asList(new EditMessageText()
.setChatId(update.getMessage().getChatId())
.setMessageId(update.getMessage().getMessageId())
.setText("Подтвердите ваш выбор, в форме ниже"),

new SendInvoice()
.setChatId(Integer.parseInt(update.getMessage().getChatId().toString()))
.setDescription(item.getDescription())
.setTitle(item.getName())
.setProviderToken(payTokenService.getPayToken())
........................
.setPrices(item.getPrice())
);
}

}


Это даёт следующие преимущества:




  • Не надо писать кастомную логику для выбора обработчика сообщения от пользователя

  • Возможность инжектить разлиные бины в наш @BotController

  • Как следствие из предыдущих двух пунктов — существенное сокращение объемов кода

  • Потенциально (хотя я этого еще не сделал) аргументы кастомного метода обработчика, могут быть выражены в виде тех аргументов, которые действительно нужны!

  • Возможность создавать серьезные энтерпрайз решения, используя spring



Давайте теперь посмотрим как это можно завести в нашем проекте



Аннотации
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface BotController {
String[] value() default {};
}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BotRequestMapping {
String[] value() default {};
BotRequestMethod[] method() default {BotRequestMethod.MSG};
}


Создаем свой контейнер обработчиков в виде обычной HashMap



Контейнер
public class BotApiMethodContainer {
private static final Logger LOGGER = Logger.getLogger(BotApiMethodContainer.class);

private Map controllerMap;

public static BotApiMethodContainer getInstanse() {
return Holder.INST;
}

public void addBotController(String path, BotApiMethodController controller) {
if(controllerMap.containsKey(path)) throw new BotApiMethodContainerException("path " + path + " already add");
LOGGER.trace("add telegram bot controller for path: " + path);
controllerMap.put(path, controller);
}

public BotApiMethodController getBotApiMethodController(String path) {
return controllerMap.get(path);
}

private BotApiMethodContainer() {
controllerMap = new HashMap<>();
}

private static class Holder{
final static BotApiMethodContainer INST = new BotApiMethodContainer();
}
}


В контейнере будем хранить контроллеры обертки (для пары @BotController и @BotRequestMapping)



Контроллер обертка
public abstract class BotApiMethodController {
private static final Logger LOGGER = Logger.getLogger(BotApiMethodController.class);

private Object bean;
private Method method;
private Process processUpdate;

public BotApiMethodController(Object bean, Method method) {
this.bean = bean;
this.method = method;

processUpdate = typeListReturnDetect() ? this::processList : this::processSingle;
}

public abstract boolean successUpdatePredicate(Update update);

public List process(Update update) {
if(!successUpdatePredicate(update)) return null;

try {
return processUpdate.accept(update);
} catch (IllegalAccessException | InvocationTargetException e) {
LOGGER.error("bad invoke method", e);
}

return null;
}

boolean typeListReturnDetect() {
return List.class.equals(method.getReturnType());
}

private List processSingle(Update update) throws InvocationTargetException, IllegalAccessException {
BotApiMethod botApiMethod = (BotApiMethod) method.invoke(bean, update);
return botApiMethod != null ? Collections.singletonList(botApiMethod) : new ArrayList<>(0);
}

private List processList(Update update) throws InvocationTargetException, IllegalAccessException {
List botApiMethods = (List) method.invoke(bean, update);
return botApiMethods != null ? botApiMethods : new ArrayList<>(0);
}

private interface Process{
List accept(Update update) throws InvocationTargetException, IllegalAccessException;
}
}


Теперь когда у нас есть данная кодовая база возникает вопрос: как Spring заставить автоматически наполнять контейнер, чтобы мы могли им пользоваться?



Для этого реализуем специальный бин — BeanPostProcessor. Это дает возможность отлавливать бины во время их инициализации. Наши контроллеры имеют scope по умолчанию — синглтон, значит инициализироваться они будут со стартом контекста!



TelegramUpdateHandlerBeanPostProcessor
@Component
public class TelegramUpdateHandlerBeanPostProcessor implements BeanPostProcessor, Ordered {
private static final Logger LOGGER = Logger.getLogger(TelegramUpdateHandlerBeanPostProcessor.class);

private BotApiMethodContainer container = BotApiMethodContainer.getInstanse();
private Map botControllerMap = new HashMap<>();

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class

https://habrahabr.ru/post/335490/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Обучающий проект: ToDo веб приложения на Srping и ReactJS

Пятница, 11 Августа 2017 г. 18:29 (ссылка)

Srping и ReactJS



Приложение будет предназначено для людей, нуждающихся в удобном средстве организации личных целей и задач. Типичное приложение типа todo-list, но с одной особенностью, которая впрочем понятна из названия. Организация задач будет возможна не только в виде списка, но и в виде дерева подзадач. Tree > List!



Проект будет транслироваться с нуля, то есть вплоть до установки необходимого окружения для разработки (JDK, Eclipse, Atom, Node.js) и подключения необходимых зависимостей (Spring, Hibernate, ReactJS, Redux). Если вы хотели увидеть, как используются следующие технологии: Java, Spring, Hibernate, JavaScript, ReactJS, Redux, то спешу вас обрадовать, у вас будет такая возможность! Конечно, это не весь список, но это то на чем будет делаться акцент.



Но стоит отметить, что для понимания будут необходимы некоторые базовые знания языков Java, JavaScript и SQL. В рамках трансляций будут рассмотрены основные методы работы с технологиями по мере того, как они будут встречаться в ходе реального, но небольшого проекта.



Данный подход отличается от простого всестороннего изучения технологий по документации или книгам и он не претендует на всеобъемлемость. Применение больших технологий зачастую схоже с принципом Парето: 20% использования технологии дает 80% результата, а поэтому можно считать, что даже в небольшом проекте, есть хорошая возможность понять суть технологии. И если вы готовы, то давайте вместе пройдем этот путь по разработке простого приложения ToDo Tree с серьёзными промышленными технологиями под капотом. Так что жду всех желающих на стриме!



Необходимые навыки?




  • HTML/CSS

  • SQL

  • Java (желательно 8)

  • JavaScript (желательно 6)



Изучаемые технологии?




  • Srping

  • Hibernate

  • ReactJS + Redux



Какова целевая аудитория?



Данный проект нацелен на аудиторию, которая имеет базовые навыки разработки на Java и JavaScript и желает продвинуть свои знания в современных, и востребованных технологиях разработки web-приложений.



Когда будут проходить трансляции?



Каждую пятницу в 20-00



План разработки ToDo на Srping и ReactJS:



В рамках обучения будет продемонстрирован процесс разработки современного web-приложения, начиная от установки необходимого окружения и заканчивая последней строчкой рабочего приложения.



Этап 1:




  • Настройка окружения (под Linux)



Этап 2:




  • Разработка страницы регистрации



Этап 3:




  • Разработка страницы создания/редактирования задачи



Этап 4




  • Разработка страницы списка задач



Этап 5




  • Разработка страницы фильтрации и сортировки





Обучающий проект будет проходит в онлайн режиме на следующем канале, там же можно будет задать интересующие вопросы в чате. Записи всех трансляций доступны на странице канала в разделе «Project Playlist».
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335452/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Археология Live] Стыдный разговор о синглтонах

Воскресенье, 06 Августа 2017 г. 19:18 (ссылка)

Аудитория: Java Junior, любители холиворов, профессиональные написатели синглтонов







Любые замечания и предложения — очень приветствуются. Это мое первое видео, и не совсем понятно, нужен ли тут вообще такой контент. Считайте это закрытым альфа-тестом, только для посетителей хаба Java :)



Ниже дана полная текстовая расшифровка, кому не хочется тратить время на просмотр.



Вступление



Привет, Хабр! Наступил вечер, и нам пора серьезно поговорить. Хочу с тобой обсудить стыдное. Cинглтоны.



Дело в том, что я постоянно слышу от знакомых на попойках, как они на код ревью уничтожают синглтоны и выкашивают спринг, потому что Спринг – шлак. И вот сил моих больше нет терпеть, давайте заясню за всю фигню.



Сначала о том, почему синглтоны – это стыдно.



Старейшая книга в которой говорится о синглтоне (ну по крайней мере, самая старая, какую видел своими глазами) написана в 1994 году. Это книга «Паттерны проектирования» Банды Четырех: Гамма, Хелм, Джонсон, Влиссидес.

Просто задумайтесь, какая это древность. Чем вы занимались в 1994 году? Кое-кто из наших коллег в этом году еще не родился.





Или вот, вторая любовь моей жизни – книга «Test Driven Development» Кента Бека, написанная в 2002 году.





И вот что написано про синглтоны там:





Я рассказываю это не затем, чтобы тряхнуть стариной, а чтобы показать, какой чудовищный баян — вся эта тема с хейтом синглтонов. Синглтоны хейтили, когда ты еще не родился, или учился в начальных классах школы.





Поэтому в принципе, мне кажется, что обсуждать на публике синглтоны – это стыдно. Об этом можно поговорить только с самыми близкими друзьями, со своей любимой женщиной/мужчиной, ну и с тобой, дорогой Хабр.



Светлая сторона



Понятно, что тут все эксперты, и задницей чувствуют опасность синглтонов. Но задница – не лучший индикатор, давайте как-то вербализуем нашу тревогу. Чем конкретно плохи синглтоны?



Глобальное состояние



Синглтон — это в первую очередь глобальное состояние. Вы получаете один неделимый, плохо управляемый глобальный скоуп. Этот недостаток управляемости – сама по себе проблема.



Чтобы как-то это проиллюстрировать, вот представьте что у вас внутри синглтона спряталась какая-то машина состояний или протокол общения. И если вы захотите автоматически тестировать это, то окажется что а) нужно одновременно тестировать всё, что прикасается к синглтону и б) самое ужасное — эти тесты обязаны выполняться в определенном порядке.



Если тесты обязаны выполняться один за другим, из них нельзя выбросить ненужное, то очень быстро мы приходим к ситуации, когда один тестовый прогон делается не менее, чем за 12 часов, и это очень плохо.



Явное и неявное



Существует известный принцип, согласно которому явное лучше неявного. Этот принцип, кстати, заложен в PEP 20, более известный как "дзен языка Python".





Когда ты передаешь объекты не точно по адресу, а переносишь их в глобальное состояние, этим самым ты прячешь связи внутри кода. Это супер неявно. В результате, код быстро превращается в магию. Он как-то работает, но сам по себе, и он уже не в твоей власти.



The Single Responsibility Principle



Принцип единственной ответственности. То есть это буква S в аббревиатуре SOLID. Эта аббревиатура – важнейшая в жизни любого джависта. Я так ее уважаю, что хочу сделать наколку с ней.



Этот принцип когда-то ввел Роберт Мартин (более известный как Дядя Боб).





Он утверждает, что каждый объект должен иметь одну ответственность и эта ответственность должна быть полностью инкапсулирована в класс.



Если мы делаем синглтон, то кроме своих непосредственных обязанностей, он возлагает на себя еще одну задачу: управление своим жизненным циклом. Больше не вызывающий код решает, как и когда появляется объект, не обычное окружение организует доступ к нему, а все это упихано внутрь класса-синглтона. В каком-то смысле он действительно выполняет эту задачу, но криво и убого. Собственно, как и любой код, который старается угнаться за двумя зайцами.



Сильная связанность



Все компоненты, использующие синглтон, или не дай бог – общающиеся через него, мгновенно оказываются жестко связанными. Связанное зачастую приходится развязывать — например, чтобы написать тесты. И, тестировать код, густо обмазанный синглтонами, очень неприятно. Даже один-единственный синглтон может серьезно напрячь вообще всех: и тестировщиков, и программистов с TDD, и подгадить на проведении демонстраций изолированной функциональности.



Специфика Java



В Java нет специального способа записывать синглтон. Поэтому существует множество способов записать его, и все они некрасивые.



Во-первых, все можно впихать в статическое поле или енум.



public class Singleton {
public static final Singleton INSTANCE = new Singleton();
}


public enum Singleton { INSTANCE; }


Но это будет не ленивый вариант, он нам не нужен.



Можно засунуть всё под synchronized:



public class Singleton {
private static Singleton instance;

public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}


Но это будут тормоза, кои собственно и символизирует synchronized, в самом типичном случае использования.



Или приходится использовать double-checked locking вариант:



public class Singleton {
private static volatile Singleton instance;

public static Singleton getInstance() {
Singleton localInstance = instance;
if (localInstance == null) {
synchronized (Singleton.class) {
localInstance = instance;
if (localInstance == null) {
instance = localInstance = new Singleton();
}
}
}
return localInstance;
}
}


Выглядит он еще более мерзко. Плюс отношение к этой форме записи выразили сами разработчики языка Java:



"There exist a number of common but dubious coding idioms, such as the double-checked locking idiom, that are proposed to allow threads to communicate without synchronization. Almost all such idioms are invalid under the existing semantics, and are expected to remain invalid under the proposed semantics."



Темная сторона



И как бы, все эти соображения на виду. Но есть и другая, темная сторона вопроса. Существуют другие люди, более практически настроенные. У них есть собственный стандартный ответ: разработчики, упарывающиеся по Солиду, по принципу Лисков итп – это просто теоретики. В реальной жизни все не так, и на самом деле синглтоны нужны. И для этого у них есть специальная философия. Контр-философия.



Глобальное состояние есть везде



Сложно придумать систему, не являющуюся хэлловорлдом или теоретической иллюстрацией, где в самой предметной области не встречалось бы глобальное состояние.



Как минимум, глобальная переменная – это наш реальный мир, в котором мы все живем. Если у нас какая-то компьютерная игра, MMORPG, мы ее пишем, то единый виртуальный мир игры – это тоже глобальное состояние. У нас не будет другой жизни и другого мира.



Более локально, если у вас в системе есть единственная база данных, и это часть предметной области, то зачастую имеет смысл привязаться к её единственности. Иначе на решение прикладной задачи не останется времени, и все начнут писать систему управления бесконечным количеством баз данных.



Аргумент про порядок тестов тут тоже не сработает: очевидно, что при исполнении, первой должна подниматься наша единственная база (и выполняться тесты относительно базы), и потом уже прикладной код. Если поднять код раньше базы, то все упадет. Большинство проектов, которые я видел, явным образом запускали базу до кода, и не видели в этом вообще никаких практических проблем.



"It just works" лучше явного



Современные программные проекты необычайно сложны. Может быть, это самые структурно сложные вещи, из всех, которые делало человечество за всю свою историю.



И рано или поздно, в вашем проекте оказываются тысячи классов, сотни и тысячи зависимостей на внешние библиотеки. Начиная с какого-то момента, ты не можешь управлять ими вручную, появляются умные системы сборки типа Maven. Maven появился, насколько помню, от ужаса, с которым встретились разработчики при сборке проектов на веб-фреймворке Turbine.



Рано или поздно ты понимаешь, что система становится умонепостигаемой. Ее сложность слишком велика, чтобы поместиться в мозг одному человеку, пусть это даже самый главный важный архитектор. И начинаешь искать такую автоматику, которая будет сама думать о внутренних связях, об индикации и сопровождении ошибок, об автоматическом dataflow, и так далее.



Очевидно, что такая система в значительной степени состоит из магии. И в данном случае, магия – это не то, от чего стоит бежать. Наоборот, к ней стоит стремиться. Но это должна быть хорошая, качественная, надежная магия.



Жесткое лучше мягкого



Если проект большой, и в нем сложно разобраться, то прямые жесткие связи упрощают понимание кода. В случае ошибки, в стектрейсе будет именно то место, где она возникла. Открываются возможности по построению очень простых графиков, отражающих структуру кода. Сравните это с действительно слабой связанностью кода на основе очередей, когда вылетевшую в одном месте системы ошибку на самом деле нужно искать на другой стороне очереди, или нескольких очередей – такие поиски могут занимать часы.



Что касается тестирования. Если посмотреть, как это в реальности, то во многих проектах тестирование либо вообще не делают, либо делают в очень урезанном виде. В проекте может быть тысячи классов, но всего несколько десятков тестов. И ради этого крошечного количества вообще не имеет смысла беспокоиться, и переписывать систему особым образом. Это касается не только синглтонов, а вообще всего – настоящее полномасштабное тестирование тянет за собой очень много дополнительных условий. На практике ставить во главу угла тестируемость – не всегда хорошая идея.



Специфика Java



Да, написание синглтона выглядит некрасиво. Но в IDE всегда можно создать шаблон «некрасивого» класса, и навсегда забыть о том, чтобы писать этот неприятный код вручную. Как говорится, темнота – друг молодежи.



Решение



И вот, мы оказались на пороге реального конфликта: одни люди хотят синглтоны (на самом деле, просто глобальное состояние, просто они его называют синглтоном), а другие – наоборот сильно против этого.



Отличным решением является переход от настоящих синглтонов к сиглтонам курильщика… ой ой. Singleton Beans из Spring. В чем суть: с помощью аннотации Component и Scope(SCOPE_SINGLETON) вы помечаете некоторые классы как синглтоны.



import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Greeter {
public String hello() {
return "Hello World!";
}
}


Потом в любом месте вы можете сделать поле, пометить его как @Autowired, и при старте приложения в этом поле окажется нужный экземпляр.



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
@Autowired
Greeter greeter;

@RequestMapping(path = "/")
public String home() {
return greeter.hello();
}
}


Заметьте, что этот вариант решает все перечисленные выше проблемы.




  • Оно позволяет использовать глобальное состояние, но при этом не запирает вас в рамках одного контекста. Вы в Спринге можете делать сколько угодно контекстов. Если необходимо сделать отдельный контекст для тестирования, это делается в несколько строк кода. Более того, оно предоставляет удобные инструменты для работы с контекстом, наспример, можно получить список всех существующих сейчас синглтонов. Как это сделать в чистой джаве – наверное, никак.

  • Оно позволяет явно описать все существующие сейчас синглтоны. Но не тратить время на ручное управление зависимостями. То есть, в коде присуствует большая магия, но эта магия полностью контролируется, если нужно.

  • Оно не нарушает S в SOLID, потому что жизненным циклом управляет Spring

  • Оно выглядит красиво и лаконично, так как сводится к нескольким аннотациям, и вообще не заставляет писать boilerplate код.



Резюме



Вопрос синглтонов давным-давно решен. Удвительно, что находятся люди, которые об этом еще не знают. Решение — это Spring и другие системы инверсии контроля и депенденси инжекшена. Синглтоны не нужно выкашивать, не нужно хетить людей, которые их пишут — их нужно переводить на Spring и обращать в нашу Spring-религию.



И еще



И раз уж вы не только посмотрели видос, но и дочитали до конца. Спасибо, за поглощение моего контента, оказали мне этим большую честь. (серьезно.) А теперь что с вас нужно: обязательно оставьте комментарий к этой статье, поставьте лайк, подпишитесь на хаб Java на Хабре, на блог друганов из JUG.ru которые замотивировали меня выложить этот горячечный брейндамп в сеть, и на других видных видеоблоггеров.



Пока!




Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335006/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Внедрение зависимостей через поля — плохая практика

Вторник, 01 Августа 2017 г. 15:13 (ссылка)

Перевод статьи Field Dependency Injection Considered Harmful за авторством Vojtech Ruzicka



image



Внедрение зависимостей через поля является очень популярной практикой в DI-фреймворках, таких как Spring. Тем не менее, у этого метода есть несколько серьезных компромиссов и поэтому стоит чаще избегать его.



Типы внедрений



Есть три основных способа внедрить ваши зависимости в класс: через конструктор, сеттер и поле. Давайте бегло сравним код с одними и теми же зависимостями, внедренными с помощью каждого из подходов.



Конструктор



private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;

@Autowired
public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
this.dependencyA = dependencyA;
this.dependencyB = dependencyB;
this.dependencyC = dependencyC;
}


Сеттер



private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;

@Autowired
public void setDependencyA(DependencyA dependencyA) {
this.dependencyA = dependencyA;
}

@Autowired
public void setDependencyB(DependencyB dependencyB) {
this.dependencyB = dependencyB;
}

@Autowired
public void setDependencyC(DependencyC dependencyC) {
this.dependencyC = dependencyC;
}


Поле



@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;


Что не так?



Как можно наблюдать, вариант внедрения через поле выглядит очень привлекательным. Он очень лаконичен, выразителен, отсутствует шаблонный код. По коду легко перемещаться и читать его. Ваш класс может просто сфокусироваться на основной функциональности и не загромождается шаблонным DI-кодом. Вы просто помещаете аннотацию @Autowired над полем — и все. Не надо писать специальных конструкторов или сеттеров только для того, чтобы DI-контейнер предоставил необходимые зависимости. Java довольно многословна сама по себе, так что стоит использовать любую возможность, чтобы сделать код короче, верно?



Нарушение принципа единственной ответственности



Добавлять новые зависимости просто. Возможно даже слишком просто. Нет никакой проблемы добавить шесть, десять или даже более зависимостей. При использовании конструкторов для внедрения, после определенного момента число аргументов конструктора становится слишком большим и тут же становится очевидно, что что-то не так. Наличие слишком большого количества зависимостей обычно означает, что у класса слишком много зон ответственности. Это может быть нарушением принципов единственной ответственности (single responsibility) и разделения ответственности (ориг.: separation of concerns) и является хорошим индикатором, что класс возможно стоит более внимательно изучить и подвергнуть рефакторингу. При использовании внедрения через поля такого явного тревожного индикатора нет, и таким образом происходит неограниченное разрастание внедренных зависимостей.



Сокрытие зависимостей



Использование DI-контейнера означает, что класс более не ответственен за управление его зависимостями. Ответственность за их получение выносится из класса во вне и теперь кто-то другой ответственен за их предоставление: это может быть DI-контейнер или ручное предоставление их через тесты. Когда класс более не отвечает за получение зависимостей, он должен явно взаимодействовать с ними, используя публичные интерфейсы — методы или конструкторы. Таким образом становится четко понятно, что требует класс, а также опциональные ли это зависомости (через сеттеры) или обязательные (конструктор)



Зависимость от DI-контейнера



Одна из ключевых идей DI-фреймворков заключается в том, что управляемый класс не должен зависеть от конкретного используемого контейнера. Другими словами, это должен быть простой POJO-класс, экземпляр которого может быть создан самостоятельно, если вы передадите ему все необходимые зависимости. Таким образом, вы можете создать его в юнит-тесте без запуска контейнера и протестировать его отдельно (с контейнером это будет скорее интеграционный тест). Если нет завязки на контейнер, вы можете использовать класс как управляемый или неуправляемый, или даже переключиться на другой DI-фреймворк.



Однако при внедрении прямо в поля вы не предоставляете прямого способа создания экземпляра класса со всеми необходимыми зависимостями. Это означает, что:




  • Существует способ (путем вызова конструктора по-умолчанию) создать объект с использованием new в состоянии, когда ему не хватает некоторых из его обязательных зависимостей, и использование приведет к NullPointerException

  • Такой класс не может быть использован вне DI-контейнеров (тесты, другие модули) и нет способа кроме рефлексии предоставить ему необходимые зависимости



Неизменность



В отличие от способа с использованием конструктора, внедрение через поля не может использоваться для присвоения зависимостей final-полям, что приводит к тому, что ваши объекты становятся изменяемыми



Внедрение через конструктор vs сеттер



Таким образом, инъекция через поля может не быть хорошим способом. Что остается? Сеттеры и конструкторы. Какой из них следует использовать?



Сеттеры



Сеттеры следует использовать для инъекций опциональных зависимостей. Класс должен быть способен функционировать, даже если они не были предоставлены. Зависимости могут быть изменены в любое время после создания объекта. Это может быть, а может и не быть преимуществом в зависимости от обстоятельств. Иногда предпочтительно иметь неизменяемый объект. Иногда же полезно менять составные части объекта во время выполнения — например управляемые бины MBean в JMX.

Официальная рекомендация из документации по Spring 3.x поощряет использование сеттеров над конструкторами:

Команда Spring главным образом выступает за инъекцию через сеттеры, потому что большое количество аргументов конструктора может стать громоздким, особенно если свойства являются необязательными. Сеттеры также делают объекты этого класса пригодными для реконфигурации или повторной инъекции позже. Управление через JMX MBeans является ярким примером



Некоторые пуристы предпочитают инъекцию на основе конструктора. Предоставление всех зависимостей означает, что объект всегда возвращается в вызывающий код в полностью инициализированном состоянии. Недостатком является то, что объект становится менее поддающимся реконфигурации и повторной инъекции


Конструкторы



Инъекция через конструкторы хороша для обязательных зависимостей — тех, которые требуются для корректной функциональности объекта. Передавая их через конструктор, вы можете быть уверенными в том, что объект полностью готов к использованию с момента создания. Поля, присвоенные в конструкторе, также могут быть final, что позволяет объекту быть полностью неизменным или как минимум защищает необходимые поля.



Одно из следствий использования внедрения через конструктор — это то что теперь невозможна циклическая зависимость между двумя объектами, созданными таким образом (в отличие от внедрения через сеттер). Это скорее плюс, чем ограничение, поскольку следует избегать циклических зависимостей, что обычно является признаком плохой архитектуры. Таким образом предотвращается подобная практика.



Еще одним преимуществом является то, что при использовании Spring версий 4.3+ вы можете полностью отвязать ваш класс от конкретного DI-фреймворка. Причина в том, что Spring теперь поддерживает неявное внедрение через конструктор для сценариев использования с одним конструктором. Это означает, что вам больше не нужны DI-аннотации в вашем классе. Конечно, вы можете достигнуть того же результата с помощью явного конфигурирования DI в настройках Spring для данного класса; просто сейчас это сделать гораздо проще.



Что касается Spring 4.x, официальная рекомендация из документации изменилась и теперь инъекция через сеттер более не предпочтительна над конструктором:

Команда Spring главным образом выступает за инъекцию через конструктор, поскольку она позволяет реализовывать компоненты приложения как неизменяемые объекты и гарантировать, что требуемые зависимости не null. Более того, компоненты, внедренные через через конструктор, всегда возвращаются в клиентский код в полностью инициализированном состоянии. Как небольшое замечание, большое число аргументов конструктора является признаком «кода с запашком» и подразумевает, что у класса, вероятно, слишком много обязанностей, и его необходимо реорганизовать, чтобы лучше решать вопрос о разделении ответственности.



Инъекция через сеттер должна использоваться в первую очередь для опциональных зависимостей, которым могут быть присвоены значения по-умолчанию внутри класса. В противном случае, проверки на not-null должны быть использованы везде, где код использует эти зависимости. Одно из преимуществ использования внедрения через сеттеры заключается в том, что они делают объекты класса поддающимися реконфигурации и повторному инжектированию позже


Заключение



В основном стоит избегать внедрения через поля. Как альтернативу для внедрения следует использовать сеттеры или конструкторы. У каждого из них есть свои преимущества и недостатки в зависимости от ситуации. Однако так как эти подходы можно смешивать, это не выбор «или-или» и вы можете в одном классе комбинировать инъекцию и через сеттер, и через конструктор. Конструкторы больше подходят для обязательных зависимостей и при нужде в неизменяемых объектах. Сеттеры лучше подходят для опциональных зависимостей.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/334636/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<spring - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda