Ну и самые кишочки... )))
7) Internal - память под внутренние нужны JVM, не упомянутые выше.
В частности здесь выделяются Direct ByteBuffers - https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html В случае объекта в heap, содержащего данные файла, ОС вначале копирует данные в свой низкоуровневый буфер, а потом JVM копирует данные к себе, то в случае Direct ByteBuffers этого можно избежать. Используются во многих высоконагруженных системах, в частности Kafka.
Размер настраивается через XX:MaxDirectMemorySize=N. Нехватку памяти можно определить по "OutOfMemoryError: Direct buffer memory".
Т.к. ByteBuffer - объект, то сборщик мусора также умеет убирать и данные, выделенные в Direct ByteBuffers. Правда с некоторой задержкой, т.к. сами данные все же находятся не в куче, и механизм уборки получается чуть более сложный.
Сравнение скорости работы прямого и heap буфера с комментарием о том, что это всего лишь один из возможных случаев использования: https://elizarov.livejournal.com/20381.html
А увидеть все эти области можно запустив java процесс со специальным ключом XX:NativeMemoryTracking и используя утилиту jcmd из состава JDK.
Cм. детали тут https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html
Запускать в ПРОДе с этой опцией нужно с осторожностью, overhead может быть 5-10%
Может показаться, что главное - это heap, все остальное по сравнению с ним - мелочи. Это не всегда так, см. исследование https://shipilev.net/jvm/anatomy-quarks/
#java #interview_question
7) Internal - память под внутренние нужны JVM, не упомянутые выше.
В частности здесь выделяются Direct ByteBuffers - https://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html В случае объекта в heap, содержащего данные файла, ОС вначале копирует данные в свой низкоуровневый буфер, а потом JVM копирует данные к себе, то в случае Direct ByteBuffers этого можно избежать. Используются во многих высоконагруженных системах, в частности Kafka.
Размер настраивается через XX:MaxDirectMemorySize=N. Нехватку памяти можно определить по "OutOfMemoryError: Direct buffer memory".
Т.к. ByteBuffer - объект, то сборщик мусора также умеет убирать и данные, выделенные в Direct ByteBuffers. Правда с некоторой задержкой, т.к. сами данные все же находятся не в куче, и механизм уборки получается чуть более сложный.
Сравнение скорости работы прямого и heap буфера с комментарием о том, что это всего лишь один из возможных случаев использования: https://elizarov.livejournal.com/20381.html
А увидеть все эти области можно запустив java процесс со специальным ключом XX:NativeMemoryTracking и используя утилиту jcmd из состава JDK.
Cм. детали тут https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html
Запускать в ПРОДе с этой опцией нужно с осторожностью, overhead может быть 5-10%
Может показаться, что главное - это heap, все остальное по сравнению с ним - мелочи. Это не всегда так, см. исследование https://shipilev.net/jvm/anatomy-quarks/
#java #interview_question
Livejournal
Детали реализации имеют значение или direct ByteBuffer vs heap ByteBuffer
При написании кода для максимальной производительности особое внимание необходимо уделять используемым библиотекам, даже если эти библиотеки являются частью вашего языка программирования. Необходимо подробно изучать не только контракты, описанные в их API…
Всем привет!
Я довольно часто провожу собесы Java разработчиков. Хотел бы написать про свои "черные метки", сильно снижающие шанс получить предложение о работе.
Ясно, что оценка будет комплексная - по теории, практике, софт-скилам, соответствию знаний и запрашиваемого "золота", но перечисленные ниже вещи сильно влияют на результат.
Поехали.
1) нет понимания основ Java, которые я ожидаю даже от джуна. Пример: переменную нельзя инициализировать результатом метода, обязательно должна быть инициализация через конструктор, а уже потом вызов метода с присвоением.
Для сравнения - не относится к основам незнание, когда можно не инициализировать переменную, а когда нет. Это приходит с опытом.
Еще примеры: непонимание иммутабельности строк или разницы между == и equals. Подчеркну - именно незнание. Можно не заметить этот баг в коде, такое к сожалению бывает, лечится подсказками IDEA и опытом
2) непонимание или отсутствие опыта работы со Spring Core, он же Spring Ioc. Spring сейчас - это наше все. Говорим Java приложение, подразумеваем Spring) Можно его не использовать: знаю компанию, где это запрещено по причине того, что Spring под капотом сложный и многие его используют не разбираясь в деталях. Это допустимый, хотя и спорный подход. Но он не отменяет того, что основы Spring нужно знать.
3) незнание модульных тестов. На мой взгляд это базовая практика, в отличие от более сложных вещей типа TDD, DDD, паттернов и ее нужно изучать вместе с Java и Spring
4) отсутсвие навыков работы с чужим кодом. На собес я предлагаю задачку типа код-ревью. Хотя бы 25% заложенных туда багов надо найти) Отдельный момент - есть баги логические, есть неоптимальный код, есть плохой код-стайл. Первые выглядят как более важные, но как правило их находят при тестировании - модульном или функциональном. Естественно, избавившись от них раньше, мы уменьшим время тестирования, Shift Left, Lead Time и все такое) Но вторые и третьи тоже важны, потому что практически любой код не является одноразовым. Его будут править - исправлять баги, добавлять новые фичи, рефакторить. Возможно даже другие люди. И разбираться в неоптимальном коде, где много boiler plate, отсутствует code стайл - то еще удовольствие. И куча потраченного времени, желание выкинуть все нафиг и переписать с нуля, а это еще куча потраченного времени...
5) Возвращаясь к задачке с код-ревью на собесе. В ней есть работа с данными, содержащимися в локальных переменных. Данные не передаются в метод через параметры - создаются в методе или читаются из файла. Но есть соискатели, которые начинают беспокоится за сохранность этих данных при многоточном вызове метода. Предлагают использовать StringBuffer для манипуляция со строками, или отказаться от static метода и хранить все в полях. Незачет) Локальные объекты хранятся в куче вместе со всеми, но ссылка на них есть только у одного потока, беспокоится незачем. Вообще это основы Java, но вынес в отдельный пункт, т.к. прямо бесит)
6) Для Middle + обязательно знание принципов SOLID. Можно не знать все паттерны или скептически относится к их практическому применению. Тут можно обсудить, поспорить. Но SOLID - это как аксиомы в математике. Основа разработки. Для джуна тоже будет огромным плюсом.
7) односложные ответы без попыток рассуждения. Все знать нельзя, да и не нужно, но рассуждая ты показываешь, на что способен. Сюда же я бы отнес проблемы с логикой. Например: "я первый раз вижу конструкцию do .. while, значит код работать не будет". Чтобы утверждать, что код не работает, нужно хорошо знать Java. Подход - я этого не знаю, поэтому оно работать не будет - плохой))) Везде, не только в Java. Правильно: я этого не знаю, поэтому я бы использовал другую конструкцию.
8) ну и очевидное - уход от прямых ответов, разливание воды ведрами, явная ложь.
#java #interview #solid
Я довольно часто провожу собесы Java разработчиков. Хотел бы написать про свои "черные метки", сильно снижающие шанс получить предложение о работе.
Ясно, что оценка будет комплексная - по теории, практике, софт-скилам, соответствию знаний и запрашиваемого "золота", но перечисленные ниже вещи сильно влияют на результат.
Поехали.
1) нет понимания основ Java, которые я ожидаю даже от джуна. Пример: переменную нельзя инициализировать результатом метода, обязательно должна быть инициализация через конструктор, а уже потом вызов метода с присвоением.
Для сравнения - не относится к основам незнание, когда можно не инициализировать переменную, а когда нет. Это приходит с опытом.
Еще примеры: непонимание иммутабельности строк или разницы между == и equals. Подчеркну - именно незнание. Можно не заметить этот баг в коде, такое к сожалению бывает, лечится подсказками IDEA и опытом
2) непонимание или отсутствие опыта работы со Spring Core, он же Spring Ioc. Spring сейчас - это наше все. Говорим Java приложение, подразумеваем Spring) Можно его не использовать: знаю компанию, где это запрещено по причине того, что Spring под капотом сложный и многие его используют не разбираясь в деталях. Это допустимый, хотя и спорный подход. Но он не отменяет того, что основы Spring нужно знать.
3) незнание модульных тестов. На мой взгляд это базовая практика, в отличие от более сложных вещей типа TDD, DDD, паттернов и ее нужно изучать вместе с Java и Spring
4) отсутсвие навыков работы с чужим кодом. На собес я предлагаю задачку типа код-ревью. Хотя бы 25% заложенных туда багов надо найти) Отдельный момент - есть баги логические, есть неоптимальный код, есть плохой код-стайл. Первые выглядят как более важные, но как правило их находят при тестировании - модульном или функциональном. Естественно, избавившись от них раньше, мы уменьшим время тестирования, Shift Left, Lead Time и все такое) Но вторые и третьи тоже важны, потому что практически любой код не является одноразовым. Его будут править - исправлять баги, добавлять новые фичи, рефакторить. Возможно даже другие люди. И разбираться в неоптимальном коде, где много boiler plate, отсутствует code стайл - то еще удовольствие. И куча потраченного времени, желание выкинуть все нафиг и переписать с нуля, а это еще куча потраченного времени...
5) Возвращаясь к задачке с код-ревью на собесе. В ней есть работа с данными, содержащимися в локальных переменных. Данные не передаются в метод через параметры - создаются в методе или читаются из файла. Но есть соискатели, которые начинают беспокоится за сохранность этих данных при многоточном вызове метода. Предлагают использовать StringBuffer для манипуляция со строками, или отказаться от static метода и хранить все в полях. Незачет) Локальные объекты хранятся в куче вместе со всеми, но ссылка на них есть только у одного потока, беспокоится незачем. Вообще это основы Java, но вынес в отдельный пункт, т.к. прямо бесит)
6) Для Middle + обязательно знание принципов SOLID. Можно не знать все паттерны или скептически относится к их практическому применению. Тут можно обсудить, поспорить. Но SOLID - это как аксиомы в математике. Основа разработки. Для джуна тоже будет огромным плюсом.
7) односложные ответы без попыток рассуждения. Все знать нельзя, да и не нужно, но рассуждая ты показываешь, на что способен. Сюда же я бы отнес проблемы с логикой. Например: "я первый раз вижу конструкцию do .. while, значит код работать не будет". Чтобы утверждать, что код не работает, нужно хорошо знать Java. Подход - я этого не знаю, поэтому оно работать не будет - плохой))) Везде, не только в Java. Правильно: я этого не знаю, поэтому я бы использовал другую конструкцию.
8) ну и очевидное - уход от прямых ответов, разливание воды ведрами, явная ложь.
#java #interview #solid
Всем привет!
Сегодня расскажу про такую штуку, как rate limiters.
Как следует из названия это компонент для ограничения числа запросов в интервал времени.
Может возникнуть вопрос - причем здесь Java? Да, обычно такие штуки разворачивают на инфраструктуре - например, в k8s или nginx.
Если так можно сделать - так и нужно делать)
Когда так сделать не получится:
1) алгоритм ограничения числа запросов нестандартный, поэтому нужна Java, чтобы его запрограммировать)
2) нужна возможность продвинутого мониторинга числа пропущенных и отбитых запросов
3) при изменении параметров rate limiting нужна умная ребалансировка. Т.е. нельзя просто сбросить счетчики в ноль, т.к. это приведет к падению сервиса, который защищает rate limiter.
4) ну и наконец нет подходящей инфраструктуры
Второй вопрос - в каких кейсах это может понадобиться:
1) не уронить стоящий за вами сервис
2) приоритизировать какие-то запросы вашего API по сравнению с другими чтобы не упасть самому
3) ограничить число запросов по тарифному плану клиента
Вот неплохая статья про существующие алгоритмы rate limiting https://habr.com/ru/post/448438
Неплохая библиотека для Java - Bucket4J https://github.com/bucket4j/bucket4j
Развивается порядка 8 лет, к слову разработчики из России.
Вот хорошее видео от разработчиков с примерам настройки - https://www.youtube.com/watch?v=OSNFNxgZZ3A
В простых случаях можно использовать Resilience4j, удобно со Spring Boot посредством аннотаций https://www.baeldung.com/spring-boot-resilience4j Resilience4j библиотека более широкого профиля, отвечает за отказоустойчивость, см. детали в статье
#java #libraries #highload #interview_question #rate_limiters
Сегодня расскажу про такую штуку, как rate limiters.
Как следует из названия это компонент для ограничения числа запросов в интервал времени.
Может возникнуть вопрос - причем здесь Java? Да, обычно такие штуки разворачивают на инфраструктуре - например, в k8s или nginx.
Если так можно сделать - так и нужно делать)
Когда так сделать не получится:
1) алгоритм ограничения числа запросов нестандартный, поэтому нужна Java, чтобы его запрограммировать)
2) нужна возможность продвинутого мониторинга числа пропущенных и отбитых запросов
3) при изменении параметров rate limiting нужна умная ребалансировка. Т.е. нельзя просто сбросить счетчики в ноль, т.к. это приведет к падению сервиса, который защищает rate limiter.
4) ну и наконец нет подходящей инфраструктуры
Второй вопрос - в каких кейсах это может понадобиться:
1) не уронить стоящий за вами сервис
2) приоритизировать какие-то запросы вашего API по сравнению с другими чтобы не упасть самому
3) ограничить число запросов по тарифному плану клиента
Вот неплохая статья про существующие алгоритмы rate limiting https://habr.com/ru/post/448438
Неплохая библиотека для Java - Bucket4J https://github.com/bucket4j/bucket4j
Развивается порядка 8 лет, к слову разработчики из России.
Вот хорошее видео от разработчиков с примерам настройки - https://www.youtube.com/watch?v=OSNFNxgZZ3A
В простых случаях можно использовать Resilience4j, удобно со Spring Boot посредством аннотаций https://www.baeldung.com/spring-boot-resilience4j Resilience4j библиотека более широкого профиля, отвечает за отказоустойчивость, см. детали в статье
#java #libraries #highload #interview_question #rate_limiters
Хабр
Ограничение скорости обработки запросов, или как не устроить DDoS-атаку на своего клиента
Иногда при разработке highload-продукта возникает ситуация, когда надо обработать не максимально большое количество запросов, а наоборот — ограничить количество запросов в единицу времени. В нашем...
Всем привет!
Хочу разбавить тему микросервисов. Сегодня поговорим про Spring и Dependency Injection (DI).
Есть три способа внедрения бина:
1) через конструктор
2) через поле
3) через setter
Практически все слышали, что предпочтительнее внедрять через конструктор. Вопрос почему?
Первая и самая главная причина: внедрение зависимостей - это часть инициализации объекта. А конструктор - это простой и естественный способ инициализировать объект.
Что из этого следует:
1) список внедряемых бинов легко читается. Через setter-ы очень плохо читается, через поля получше, но конструктор вне конкуренции.
2) становится проще создать полностью инициализированный объект т.к. для этого есть готовый конструктор. Полезно в тестах, можно написать тест на чистом JUnit, без @SpringBootTest и других инструментов для инициализации контекста из Spring Test.
3) если классу нужно слишком много бинов для работы - SonarQube будет ругаться, что у конструктора слишком много полей. Считаю это плюсом, если ругается - это повод задуматься над рефакторингом
4) если у бинов циклические ссылки - инициализация контекста Spring упадет. Многие считают это минусом, но я не согласен: чем раньше найдешь циклическую зависимость - тем быстрее исправишь. См. https://t.me/javaKotlinDevOps/53
5) если с внедряемыми бинами надо что-то делать, то у нас есть место, где гарантированно все бины будут доступны - конструктор. При внедрении зависимостей через setter или поля вначале выполнится дефолтный конструктор, потом будут проиницилизированы поля.
6) для инициализации не используется рефлексия, как в случае autowired полей, т.е. инициализации работает немного быстрее
7) если конструктор единственный, то начиная со Spring 4.3 не нужно указывать аннотацию @Autowired. Для Kotlin в случае default конструктора есть особенности, для того, чтобы с ними не сталкиваться лучше использовать Spring 5+ ) Не сказать, что это большое преимущество, т.к. аннотацию для маркировки бина все равно придется использовать, поэтому класс без import Spring зависимостей так просто сделать не получится.
И второй важный момент - поддержка иммутабельности объектов, т.к. только в конструкторе можно инициализировать final поля в случае Java и val - в случае Kotlin.
#spring #java #kotlin #interview_question
Хочу разбавить тему микросервисов. Сегодня поговорим про Spring и Dependency Injection (DI).
Есть три способа внедрения бина:
1) через конструктор
2) через поле
3) через setter
Практически все слышали, что предпочтительнее внедрять через конструктор. Вопрос почему?
Первая и самая главная причина: внедрение зависимостей - это часть инициализации объекта. А конструктор - это простой и естественный способ инициализировать объект.
Что из этого следует:
1) список внедряемых бинов легко читается. Через setter-ы очень плохо читается, через поля получше, но конструктор вне конкуренции.
2) становится проще создать полностью инициализированный объект т.к. для этого есть готовый конструктор. Полезно в тестах, можно написать тест на чистом JUnit, без @SpringBootTest и других инструментов для инициализации контекста из Spring Test.
3) если классу нужно слишком много бинов для работы - SonarQube будет ругаться, что у конструктора слишком много полей. Считаю это плюсом, если ругается - это повод задуматься над рефакторингом
4) если у бинов циклические ссылки - инициализация контекста Spring упадет. Многие считают это минусом, но я не согласен: чем раньше найдешь циклическую зависимость - тем быстрее исправишь. См. https://t.me/javaKotlinDevOps/53
5) если с внедряемыми бинами надо что-то делать, то у нас есть место, где гарантированно все бины будут доступны - конструктор. При внедрении зависимостей через setter или поля вначале выполнится дефолтный конструктор, потом будут проиницилизированы поля.
6) для инициализации не используется рефлексия, как в случае autowired полей, т.е. инициализации работает немного быстрее
7) если конструктор единственный, то начиная со Spring 4.3 не нужно указывать аннотацию @Autowired. Для Kotlin в случае default конструктора есть особенности, для того, чтобы с ними не сталкиваться лучше использовать Spring 5+ ) Не сказать, что это большое преимущество, т.к. аннотацию для маркировки бина все равно придется использовать, поэтому класс без import Spring зависимостей так просто сделать не получится.
И второй важный момент - поддержка иммутабельности объектов, т.к. только в конструкторе можно инициализировать final поля в случае Java и val - в случае Kotlin.
#spring #java #kotlin #interview_question
Telegram
(java || kotlin) && devOps
Всем привет!
Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения.
1) классы. Технически Java компилятор умеет компилировать взаимозависимые классы за счет того, что является многопроходным - https://ru.wikip…
Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения.
1) классы. Технически Java компилятор умеет компилировать взаимозависимые классы за счет того, что является многопроходным - https://ru.wikip…
Всем привет!
Продолжается перерыв в теме микросервисов.
Сегодня будет немного холивара)
Можно ли использовать static методы?
Исторически static-и прошли 3 стадии в своем восприятии.
1 этап: о круто, можно не создавать объект, давайте все методы делать статическими.
Если что - я конечно утрирую насчет всех, но static-и активно использовались, это факт, который легко увидеть в legacy коде. Еще факт: static-и есть во многих языках, что как бы намекает. Даже в Kotlin они есть, просто выглядят странно - я про companion object.
2 этап: static-и трудно тестировать, это антипаттерн, не надо их использовать. Ну разве что для Util классов, и то ...
Вот пример, демонстрирующий почему возникают такие мысли.
Возьмем другой антипаттерн - синглетон.
class Singleton {
private static final instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
....
}
Сразу скажу - это не одна из простейших реализаций, не потокобезопасная, взятая для примера, так делать не надо.
Предположим, мы хотим сделать вместо Singleton заглушку для теста. Ну например, он ходит в БД или читает данные из файла, а мы хотим, чтобы тесты работали быстро.
Можно сделать так:
class TestSingleton extends Singleton {
private static final testInstance = new TestSingleton();
public static Singleton getTestInstance() {
return testInstance;
}
....
}
Но это работает только если Sigleton можно как-то передать в тестируемый класс, он же System under Test (SUT).
А если это зависимость зависимости SUT? Или в более общем случае его нельзя передать через public API?
Основная проблема в том, что static методы не наследуются, а следовательно переопределить их нельзя. Можно создать метод с тем же именем, но вызываться будет метод того класса, который указан в конкретном куске кода. Или декларируемого, а не фактического класса переменной, поля или параметра, если static вызывается на объекте. В этом плане Java позволяя вызывать static метод из объекта только путает людей( К слову, в Kotlin так нельзя.
Есть конечно грязные хаки с мокированием static методов, даже Mockito это умеет. Но тот факт, что для включения этой фичи нужно добавить настройку в classpath https://www.baeldung.com/mockito-mock-static-methods говорит о том, что авторы Mockito не рекомендуют так делать.
3 этап, наше время: все не так однозначно. Точнее однозначно вот что: если static метод используется в паре со static полями - это точно зло.
И самостоятельное создание синглетонов тоже зло) Это я на всякий случай уточняю)
Но если присмотрется к вот такому static методу:
1) он не имеет доступа к полям объекта по определению
2) в классе нет static полей как я писал выше
3) пусть метод не имеет побочных эффектов. Т.е. не лезет в БД, в файловую систему, в другие сервисы
Т.е метод получает что-то на вход, вычисляет что-то и возвращает результат.
Это типичный метод Util класса, да.
Но еще это определение функции из математики. А функция - это функциональное программирование. А Java начиная с 8 версии умеет передавать ссылки на функции. И хотя в ней нет функциональных типов, но есть функциональные интерфейсы, которые делают тоже самое, просто немного с большим количеством кода. Java же, все как мы любим)
Подводя итог - считать static злом не надо. Надо лишь правильно его использовать.
#interview_question #holy_war #java #kotlin
Продолжается перерыв в теме микросервисов.
Сегодня будет немного холивара)
Можно ли использовать static методы?
Исторически static-и прошли 3 стадии в своем восприятии.
1 этап: о круто, можно не создавать объект, давайте все методы делать статическими.
Если что - я конечно утрирую насчет всех, но static-и активно использовались, это факт, который легко увидеть в legacy коде. Еще факт: static-и есть во многих языках, что как бы намекает. Даже в Kotlin они есть, просто выглядят странно - я про companion object.
2 этап: static-и трудно тестировать, это антипаттерн, не надо их использовать. Ну разве что для Util классов, и то ...
Вот пример, демонстрирующий почему возникают такие мысли.
Возьмем другой антипаттерн - синглетон.
class Singleton {
private static final instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
....
}
Сразу скажу - это не одна из простейших реализаций, не потокобезопасная, взятая для примера, так делать не надо.
Предположим, мы хотим сделать вместо Singleton заглушку для теста. Ну например, он ходит в БД или читает данные из файла, а мы хотим, чтобы тесты работали быстро.
Можно сделать так:
class TestSingleton extends Singleton {
private static final testInstance = new TestSingleton();
public static Singleton getTestInstance() {
return testInstance;
}
....
}
Но это работает только если Sigleton можно как-то передать в тестируемый класс, он же System under Test (SUT).
А если это зависимость зависимости SUT? Или в более общем случае его нельзя передать через public API?
Основная проблема в том, что static методы не наследуются, а следовательно переопределить их нельзя. Можно создать метод с тем же именем, но вызываться будет метод того класса, который указан в конкретном куске кода. Или декларируемого, а не фактического класса переменной, поля или параметра, если static вызывается на объекте. В этом плане Java позволяя вызывать static метод из объекта только путает людей( К слову, в Kotlin так нельзя.
Есть конечно грязные хаки с мокированием static методов, даже Mockito это умеет. Но тот факт, что для включения этой фичи нужно добавить настройку в classpath https://www.baeldung.com/mockito-mock-static-methods говорит о том, что авторы Mockito не рекомендуют так делать.
3 этап, наше время: все не так однозначно. Точнее однозначно вот что: если static метод используется в паре со static полями - это точно зло.
И самостоятельное создание синглетонов тоже зло) Это я на всякий случай уточняю)
Но если присмотрется к вот такому static методу:
1) он не имеет доступа к полям объекта по определению
2) в классе нет static полей как я писал выше
3) пусть метод не имеет побочных эффектов. Т.е. не лезет в БД, в файловую систему, в другие сервисы
Т.е метод получает что-то на вход, вычисляет что-то и возвращает результат.
Это типичный метод Util класса, да.
Но еще это определение функции из математики. А функция - это функциональное программирование. А Java начиная с 8 версии умеет передавать ссылки на функции. И хотя в ней нет функциональных типов, но есть функциональные интерфейсы, которые делают тоже самое, просто немного с большим количеством кода. Java же, все как мы любим)
Подводя итог - считать static злом не надо. Надо лишь правильно его использовать.
#interview_question #holy_war #java #kotlin
Baeldung
Mocking Static Methods With Mockito | Baeldung
Explore a couple of examples of how we can use Mockito to mock static methods.
Всем привет!
Сегодня пост про очередной типичный вопрос на собеседовании.
Звучит он так: расскажите про жизненный цикл сборки Maven, он же build lifecycle.
Я вкратце уже писал про это https://t.me/javaKotlinDevOps/10. Но т.к. типичный ответ на собеседовании - перечисление циклов и фаз сборки, то хочется раскрыть тему. На самом деле вопрос про другое - в чем суть цикла сборки?
1) набор фаз сборки JVM проекта уже придуман и зафиксирован умными людьми) из команды Maven в виде Default Lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference Т.е. считается, что список покрывает все потребности сборки. Как пример продуманности могу привести фазы pre-integration-test и post-integration-test, которые нужны для того, чтобы поднять и опустить контейнер сервлетов, менеджер очередей или БД до и после фазы интеграционных тестов (integration-test). Для обычных тестов (test) таких вспомогательных фаз нет.
2) последовательность фаз также зафиксирована, порядок менять нельзя да и нет смысла
3) большинство фаз являются опциональными, запускаются только если к фазе подключен хоть один плагин и либо в проекте присутствуют определенные файлы, либо есть настройки плагина, указывающие что ему делать - например, generate-sources или компиляция Kotlin
4) на одной фазе могут работать несколько целей (goal) из разных плагинов - например, компиляция Java и Kotlin на фазе compile или несколько тестовых фреймворков на фазе test
5) запуская определенный шаг сборки - mvn install - вы запускаете все предыдущие шаги цикла. И в большинстве случаев это правильно. Если такое поведение не требуется - нужно запустить конкретную цель у конкретного плагина, для моего примера это mvn install:install (плагин:цель). Т.е. тут совпадают название фазы сборки, плагина и цели. Так бывает не всегда, например, для фазы test-compile цель выглядит так compiler:testCompile
Итого: цель жизненного цикла сборки - стандартизировать процесс сборки.
Да и в принципе стандартизация - конек Maven. Тут и типовой набор папок https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html, и цикл сборки, и сложность написания своего кода для сборки, и достаточно богатый набор стандартных плагинов, для подключения которых ничего не надо делать (если быть точным - неплохо бы зафиксировать версию в pom), и архетипы, и конвенция по наименованию артефактов, и наконец центральный репозиторий - https://mvnrepository.com/repos/central
#maven #interview_question
Сегодня пост про очередной типичный вопрос на собеседовании.
Звучит он так: расскажите про жизненный цикл сборки Maven, он же build lifecycle.
Я вкратце уже писал про это https://t.me/javaKotlinDevOps/10. Но т.к. типичный ответ на собеседовании - перечисление циклов и фаз сборки, то хочется раскрыть тему. На самом деле вопрос про другое - в чем суть цикла сборки?
1) набор фаз сборки JVM проекта уже придуман и зафиксирован умными людьми) из команды Maven в виде Default Lifecycle https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference Т.е. считается, что список покрывает все потребности сборки. Как пример продуманности могу привести фазы pre-integration-test и post-integration-test, которые нужны для того, чтобы поднять и опустить контейнер сервлетов, менеджер очередей или БД до и после фазы интеграционных тестов (integration-test). Для обычных тестов (test) таких вспомогательных фаз нет.
2) последовательность фаз также зафиксирована, порядок менять нельзя да и нет смысла
3) большинство фаз являются опциональными, запускаются только если к фазе подключен хоть один плагин и либо в проекте присутствуют определенные файлы, либо есть настройки плагина, указывающие что ему делать - например, generate-sources или компиляция Kotlin
4) на одной фазе могут работать несколько целей (goal) из разных плагинов - например, компиляция Java и Kotlin на фазе compile или несколько тестовых фреймворков на фазе test
5) запуская определенный шаг сборки - mvn install - вы запускаете все предыдущие шаги цикла. И в большинстве случаев это правильно. Если такое поведение не требуется - нужно запустить конкретную цель у конкретного плагина, для моего примера это mvn install:install (плагин:цель). Т.е. тут совпадают название фазы сборки, плагина и цели. Так бывает не всегда, например, для фазы test-compile цель выглядит так compiler:testCompile
Итого: цель жизненного цикла сборки - стандартизировать процесс сборки.
Да и в принципе стандартизация - конек Maven. Тут и типовой набор папок https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html, и цикл сборки, и сложность написания своего кода для сборки, и достаточно богатый набор стандартных плагинов, для подключения которых ничего не надо делать (если быть точным - неплохо бы зафиксировать версию в pom), и архетипы, и конвенция по наименованию артефактов, и наконец центральный репозиторий - https://mvnrepository.com/repos/central
#maven #interview_question
Telegram
(java || kotlin) && devOps
Всем привет!
Хочу рассказать собрать в одном посте несколько мало и среднеизвестных фичей Maven.
Поехали!
Для начала немного теории. Жизненный цикл сборки = фиксированная последовательность фаз сборки. К фазе сборки можно подключить плагин(ы), а точнее…
Хочу рассказать собрать в одном посте несколько мало и среднеизвестных фичей Maven.
Поехали!
Для начала немного теории. Жизненный цикл сборки = фиксированная последовательность фаз сборки. К фазе сборки можно подключить плагин(ы), а точнее…
Всем привет!
Про DI и DI.
Аббревиатура DI может расшифровываться на Dependency Inversion, а может как Dependency Injection.
Dependency Inversion - это буква D из SOLID - базовых принципов разработки.
Означает, что высокоуровневые классы не должны зависеть от конкретных реализации, и в Java API любых классов лучше использовать интерфейсы везде, где это возможно. Почему такая ремарка: интерфейс с единственной реализацией - очень странная штука) Но я отвлекся) Следование принципу облегчает тестирование и расширение функциональности системы, т.к. позволяет легко заменить любую реализацию.
Dependency Injection - это механизм внедрения зависимостей, важнейшая особенность которого - собственно внедрение зависимостей отдается на откуп внешнему модулю. Самые известный пример - Spring c его IoC контейнером, но есть и другие заточенные конкретно на эту задачу и поэтому более шустрые альтернативы.
Если подходить формально - это два разных понятия, кроме аббревиатуры никак не связанные. Но с другой стороны Dependency Injection по сути - это инструмент, сильно облегчающий реализацию принципа Dependency Inversion. А хороший инструмент помогает писать правильный код. Важное замечание - Spring IoC не обеспечит за вас реализацию инверсии зависимостей. Если метод API завязывается на конкретную реализацию или уровни приложения связаны циклически - Spring тут не поможет. Поможет предварительное проектирование на уровне кода и TDD.
#code_architecture #interview_question #arch #patterns #solid
Про DI и DI.
Аббревиатура DI может расшифровываться на Dependency Inversion, а может как Dependency Injection.
Dependency Inversion - это буква D из SOLID - базовых принципов разработки.
Означает, что высокоуровневые классы не должны зависеть от конкретных реализации, и в Java API любых классов лучше использовать интерфейсы везде, где это возможно. Почему такая ремарка: интерфейс с единственной реализацией - очень странная штука) Но я отвлекся) Следование принципу облегчает тестирование и расширение функциональности системы, т.к. позволяет легко заменить любую реализацию.
Dependency Injection - это механизм внедрения зависимостей, важнейшая особенность которого - собственно внедрение зависимостей отдается на откуп внешнему модулю. Самые известный пример - Spring c его IoC контейнером, но есть и другие заточенные конкретно на эту задачу и поэтому более шустрые альтернативы.
Если подходить формально - это два разных понятия, кроме аббревиатуры никак не связанные. Но с другой стороны Dependency Injection по сути - это инструмент, сильно облегчающий реализацию принципа Dependency Inversion. А хороший инструмент помогает писать правильный код. Важное замечание - Spring IoC не обеспечит за вас реализацию инверсии зависимостей. Если метод API завязывается на конкретную реализацию или уровни приложения связаны циклически - Spring тут не поможет. Поможет предварительное проектирование на уровне кода и TDD.
#code_architecture #interview_question #arch #patterns #solid
Всем привет!
Продолжая тему SOLID. Есть в этой аббревиатуре буква L - принцип Барбары Лисков, который гласит, что все потомки должны соблюдать контракт, объявленный в родителе.
Объясню на пальцах. Есть класс или даже скорее интерфейс А. Есть класс Б, как-то использующий А - например, принимающий его как параметр в методе или конструкторе. И принцип Лисков будет соблюдаться в том случае, если класс Б не "упадет" при получении любого (!) потомка класса А, т.е. класс Б не должен разбираться в том, какого именно потомка А он получил.
На первый взгляд все просто - в Java и Kotlin строгая типизация и инкапсуляция, поэтому если мы объявили интерфейс с определенными методами и типами параметров - все потомки обязаны их реализовать. Что тут может сломаться?
Если копнуть глубже, то в Java наследники все же могут изменять контракт:
1) заменить тип возвращаемого методом результата на тип-наследник
2) добавлять в сигнатуру метода исключения-наследники и увеличивать число объявленных исключений, тоже наследниками
3) повышать область видимости метода
Все это расширяет ограничения в исходном контракте. Поломать корректно написанный класс Б, который знает только про А - интерфейс предка - так не получится. Хотя расширяя таким образом контракт, нужно быть осторожным, т.к. мы подталкиваем потребителей нашего нового класса к использованию конкретного класса вместо интерфейса, а это уже может привести к нарушению другой буквы - D )))
Как же можно точно нарушить контракт? Для этого нужно ответить на вопрос - что есть контракт?
Контракт не равно Java API. Это может быть:
1) назначение метода. Например, называя метод getXXX или checkXXX мы как бы говорим потребителю, что он не меняет состояние объекта, т.е. нет побочных эффектов
2) порядок вызова методов класса. Опять же исходя из названий методов мы можем предположить некий порядок вызовов - init() вызываем в начале, build() - в случае паттерна Builder в конце.
3) назначение класса. Если опять же исходя из названия класса он предназначен только для работы с кэшом - нужно в потомках лазить в БД, это должны быть разные сервисные классы. Если предполагается, что в сервисном классе данного уровня нужно отбрасывать логи и метрики - все потомки должны это делать. И наоборот.
4) политика по исключениям. Если предполагается, что метод возвращает внутреннюю ошибку как результат вызова (Pair или свой класс), то именно так и должны поступать все потомки
5) время выполнения. Если в контракте явно предполагается, что метод что-то рассчитывает in-process и не ходит во внешние системы - контракт нужно соблюдать.
6) зависимости от других классов. Нужно стремится к тому, чтобы все зависимости были явно указаны в API и передавались в класс при создании. Т.е. класс Б не должен как-то настраивать среду для работы конкретного потомка класса А. Да, речь про Inversion of Control.
7) иммутабельность. Если предполагается иммутабельная реализация - все потомки должны быть такими.
Как описать такой контракт? В первую очередь - в именах методов и классов. Если этого не хватает - в JavaDoc. Часто так бывает, что JavaDoc не несет новой информации для изучающего код, но в данном случае будет полезен.
И последняя очевидная, но IMHO важная мысль: чтобы наследники соответствовали контракту - для начала контракт нужно описать)
#arch #solid #interview_question
Продолжая тему SOLID. Есть в этой аббревиатуре буква L - принцип Барбары Лисков, который гласит, что все потомки должны соблюдать контракт, объявленный в родителе.
Объясню на пальцах. Есть класс или даже скорее интерфейс А. Есть класс Б, как-то использующий А - например, принимающий его как параметр в методе или конструкторе. И принцип Лисков будет соблюдаться в том случае, если класс Б не "упадет" при получении любого (!) потомка класса А, т.е. класс Б не должен разбираться в том, какого именно потомка А он получил.
На первый взгляд все просто - в Java и Kotlin строгая типизация и инкапсуляция, поэтому если мы объявили интерфейс с определенными методами и типами параметров - все потомки обязаны их реализовать. Что тут может сломаться?
Если копнуть глубже, то в Java наследники все же могут изменять контракт:
1) заменить тип возвращаемого методом результата на тип-наследник
2) добавлять в сигнатуру метода исключения-наследники и увеличивать число объявленных исключений, тоже наследниками
3) повышать область видимости метода
Все это расширяет ограничения в исходном контракте. Поломать корректно написанный класс Б, который знает только про А - интерфейс предка - так не получится. Хотя расширяя таким образом контракт, нужно быть осторожным, т.к. мы подталкиваем потребителей нашего нового класса к использованию конкретного класса вместо интерфейса, а это уже может привести к нарушению другой буквы - D )))
Как же можно точно нарушить контракт? Для этого нужно ответить на вопрос - что есть контракт?
Контракт не равно Java API. Это может быть:
1) назначение метода. Например, называя метод getXXX или checkXXX мы как бы говорим потребителю, что он не меняет состояние объекта, т.е. нет побочных эффектов
2) порядок вызова методов класса. Опять же исходя из названий методов мы можем предположить некий порядок вызовов - init() вызываем в начале, build() - в случае паттерна Builder в конце.
3) назначение класса. Если опять же исходя из названия класса он предназначен только для работы с кэшом - нужно в потомках лазить в БД, это должны быть разные сервисные классы. Если предполагается, что в сервисном классе данного уровня нужно отбрасывать логи и метрики - все потомки должны это делать. И наоборот.
4) политика по исключениям. Если предполагается, что метод возвращает внутреннюю ошибку как результат вызова (Pair или свой класс), то именно так и должны поступать все потомки
5) время выполнения. Если в контракте явно предполагается, что метод что-то рассчитывает in-process и не ходит во внешние системы - контракт нужно соблюдать.
6) зависимости от других классов. Нужно стремится к тому, чтобы все зависимости были явно указаны в API и передавались в класс при создании. Т.е. класс Б не должен как-то настраивать среду для работы конкретного потомка класса А. Да, речь про Inversion of Control.
7) иммутабельность. Если предполагается иммутабельная реализация - все потомки должны быть такими.
Как описать такой контракт? В первую очередь - в именах методов и классов. Если этого не хватает - в JavaDoc. Часто так бывает, что JavaDoc не несет новой информации для изучающего код, но в данном случае будет полезен.
И последняя очевидная, но IMHO важная мысль: чтобы наследники соответствовали контракту - для начала контракт нужно описать)
#arch #solid #interview_question
Всем привет!
На собеседовании я иногда задаю вопрос: приведите пример нарушения принципа Single responsibility. Или альтернативный вариант - а вот если в методе, к примеру, activateCard мы заодно отбросим метрики или залогируем результат - это нарушение принципа или нет.
На первый взгляд ответ - нет. Метрики и логи - это технический код, не бизнес функционал. Он может понадобиться в любом месте кода. Часто такой функционал реализуют с помощью аспектов, т.к. во-первых - это можно реализовать с помощью аспектов, а во-вторых - это красиво))), т.е. некий синтаксический сахар, улучшающий читаемость кода.
Но можно рассмотреть немного другую ситуацию. Предположим, есть код с математическими вычислениям. Или любой алгоритм. Или логика обработки данных. То, что хорошо реализуется в функциональном стиле - входные данные метода, результат, никаких внешних зависимостей. В нём нет внешних взаимодействий, сохранения в хранилище. Чистая логика. В этом случае логирование и метрики - это уже некая обработка полученного результата. Мы же не просто так выводим что-то в лог - это либо данные для разбора ошибки, либо отслеживание пользовательского пути, сбор статистики, отслеживание времени выполнения кода... Т.е. есть отдельная логика по месту и составу того, что мы логируем. Опять же контекст логирования часто требует инициализации, что добавляет ненужные зависимости в нашу логику. Поэтому такой код лучше поместить на уровень выше.
Итого: бизнес функционал и логирование/метрики - да, "чистая" логика - нет.
#logging #metrics #interview_question #code_design #solid #dev_compromises
На собеседовании я иногда задаю вопрос: приведите пример нарушения принципа Single responsibility. Или альтернативный вариант - а вот если в методе, к примеру, activateCard мы заодно отбросим метрики или залогируем результат - это нарушение принципа или нет.
На первый взгляд ответ - нет. Метрики и логи - это технический код, не бизнес функционал. Он может понадобиться в любом месте кода. Часто такой функционал реализуют с помощью аспектов, т.к. во-первых - это можно реализовать с помощью аспектов, а во-вторых - это красиво))), т.е. некий синтаксический сахар, улучшающий читаемость кода.
Но можно рассмотреть немного другую ситуацию. Предположим, есть код с математическими вычислениям. Или любой алгоритм. Или логика обработки данных. То, что хорошо реализуется в функциональном стиле - входные данные метода, результат, никаких внешних зависимостей. В нём нет внешних взаимодействий, сохранения в хранилище. Чистая логика. В этом случае логирование и метрики - это уже некая обработка полученного результата. Мы же не просто так выводим что-то в лог - это либо данные для разбора ошибки, либо отслеживание пользовательского пути, сбор статистики, отслеживание времени выполнения кода... Т.е. есть отдельная логика по месту и составу того, что мы логируем. Опять же контекст логирования часто требует инициализации, что добавляет ненужные зависимости в нашу логику. Поэтому такой код лучше поместить на уровень выше.
Итого: бизнес функционал и логирование/метрики - да, "чистая" логика - нет.
#logging #metrics #interview_question #code_design #solid #dev_compromises
Всем привет!
Нашел хорошую статью о том, как совместить тестирование Spring контроллеров и один из самых известных фреймворков для тестирования REST - Rest Assured. https://www.baeldung.com/spring-mock-mvc-rest-assured
Кстати, в начале статьи есть ссылка на пример использования чистого Spring MVC Test, если кто его не использовал - можете сравнить синтаксис.
Еще статья хороша тем, что четко разделяет модульные и интеграционные тесты. И я бы разделил точно также) Я иногда задаю вопрос о видах тестов на интервью, ответ мне не всегда нравится. Для ленивых, вкратце - интеграционным тест можно считать, если появляется сеть - открывается порт, вызывается другой процесс, внешнее хранилище, пусть даже и в embedded варианте. Хотя справедливости ради - вопрос холиварный, из-за того, что много пограничных случаев.
#unittests #spring #rest #integration_tests #interview_question
Нашел хорошую статью о том, как совместить тестирование Spring контроллеров и один из самых известных фреймворков для тестирования REST - Rest Assured. https://www.baeldung.com/spring-mock-mvc-rest-assured
Кстати, в начале статьи есть ссылка на пример использования чистого Spring MVC Test, если кто его не использовал - можете сравнить синтаксис.
Еще статья хороша тем, что четко разделяет модульные и интеграционные тесты. И я бы разделил точно также) Я иногда задаю вопрос о видах тестов на интервью, ответ мне не всегда нравится. Для ленивых, вкратце - интеграционным тест можно считать, если появляется сеть - открывается порт, вызывается другой процесс, внешнее хранилище, пусть даже и в embedded варианте. Хотя справедливости ради - вопрос холиварный, из-за того, что много пограничных случаев.
#unittests #spring #rest #integration_tests #interview_question
Baeldung
REST-assured Support for Spring MockMvc | Baeldung
Learn how to test Spring REST controllers using the RestAssuredMockMvc API from REST-assured.