Как стать автором
Обновить

Комментарии 106

«Голая Java» или разработка без всего

Рассказываю что можно сделать на одном только голом JDK

старое и ныне почти забытое искусство разработки без внешних библиотек и фреймворков

Но только:

без фреймворков и библиотек.

И все это сделано и работает на одном только JDK, без каких-либо внешних библиотек:

Без сервлетов, сервлет-контейнеров, серверов приложений и так далее.

Одна голая Java и все.

Напоминаю что все это реализовано с нуля в рамках проекта, без каких-либо внешних библиотек.

будем использовать исключительно средства JDK и ничего больше:

javac, jar и.. все.

Вот так выглядит «тру» компиляция без всего:

Да, все это также реализовано без каких-либо внешних библиотек и фреймворков — голыми руками

Я немного не понял технический стек проекта, можно подробнее? Какие библиотеки/фреймворки использовали?

Никаких вообще.

Вообще всё голое

Это сарказм?

Для автора com.sun.net.httpserver.*, java.net.* и java.io.* не внешние зависимости

Если вы покажете как можно работать с сокетами в java без импортов вообще - ну думаю сможете автора удивить.

Можно работать с сокетами без Java)

нахлынули воспоминания про boost и asio, кажется это было только вчера :)

Ну фактически вы написали свой собственный фреймворк: менеджер зависимостей, шаблонизатор, поддержка REST, самописная система сборки (шелл-скрипт) и так далее. И в качестве примера реализовали на нем форму логина.

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

а нем форму логина

вы невнимательно читали - в 700 строк уместился конечный проект гостевой, где авторизация лишь одна из фич.

Да ктстати про:

свой собственный фреймворк

«фреймворк» — от фразы «frame of work», что в дословном переводе означает «направление работы».

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

Поэтому выход за рамки направления работ у используемых фреймворков является самым проблемным местом в современной разработке — просто потому что Баба Яга сам фреймворк против таких действий.

То что я реализовал в рамках статьи является не фреймворком а конечным приложением, без каких либо внешних фреймворков и библиотек вообще — в этом и был смысл: показать чего стоит реализация бизнес‑логики «голыми руками».

Framework переводится как «каркас» или «остов». Структура, на которую полезная нагрузка навешивается

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

Общие замечание.

Многие вещи стоило унести в утилиты. Например, если даже мы используем jul, то можно было бы написать адаптер для удобного использования.

Форматирование кода просто нечитаемое. Но это скорее вкусовщина.

debugMessages = Boolean.parseBoolean(...);
тут можно и нужно унести в static final, раз сделали ее shared переменной

TinyDI notDI = new TinyDI();
notDI.setup(...)

если мы делаем свой IoC контейнер, может сделать более удобный API использования?
есть тысяча способов сделать это, у Жени Борисова есть Live-coding доклад, где он за пару часов созадет IoC контейнер.

int[] topoSort(ArrayList> adj)
не нужно привязываться к реализации коллекций

Map sessions = new HashMap<>();
public Session getSession
public synchronized String registerSessionFor

в многопоточном коде все операции должны быть под одним монитором.
но проще конечно же здесь и в других местах использовать конкурентные коллекции.

static void toJson(StringBuilder out, BookRecord r)
написать общий сериализатор в JSON дело часа-двух.
забыли про экранирование спецсимволов в toJson.

if (match(Boolean.TRUE.toString()))
вместо сравнения со строковой константой каждый раз дергаем метод.

OutputStream os = t.getResponseBody();
os.close();

try with resources был бы правильным решением.

new String(getResource("/static/html/template/main.html"))
а какая кодировка будет использоваться при разборе байт в строку?

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

не нужно привязываться к реализации коллекций

или:

 то можно было бы написать адаптер для удобного использования.

мягко говоря - неактуальны.

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

Вы смешали теплое с мягким. Идея написать приложение без внешних зависимостей никак не зависит от правил хорошего тона при разработке. Сама по себе себе интересная, реализация немного хромает.

К слову даже PoC можно за полчаса наговнякать так, что сам не разберешься через месяц, а можно постараться написать так, что код будет хорошо читаем. Это все приходит с опытом.

 Это все приходит с опытом

Вы серьезно считаете что у автора есть проблемы с опытом?

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

Ой, началось.

Тогда и тезис меняйте на:
"данная статья — лишь демонстрация, что минимальная возможная реализация функционала без библиотек ещё возможна."

А то я смотрю на основе минимальной реализации мы здесь развеселились, "если родились позже 2000х идите к психологу, ко-ко-ко"

К сожалению многие сениоры не поймут, что написано в этом проекте. Вы очень сильно переоцениваете тех, кто пришел в разработку за последние 5 лет.

Ну нет, сеньоры с 5-7 лет опыта как были "рабочими проектными лошадками" 10 лет назад так и остались. И врядли это изменится в будущем.

Не все разумеется, но именно из таких формируются команды разработки.

Вот там где 10+ стажа уже начинаются сложности: необучаемость, бесконечные закидоны, требования и придирки.

Ни разу не видел успешных команд (кроме своей) собранных только из ветеранов, чтобы там все не передрались и пересрались до взаимной ненависти за первые полгода работы, вот честно.

Не совсем по теме статьи - судя по прическе и удивленным галазм на первой картинке Эльза в кожанке, можно ссылку на wallpaper? )))

Раз и туда вкинуть два и получайте сколько хотите, хоть в кожанке, хоть без кожанки. Работает даже без видеокарты, наверное по полчаса на картинку, но работает.

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

можно ссылку на wallpaper? )))

Ого, а как вы поняли что это спросил малолетний специалист и что указало и на ультимативное требование? Скобки?

Потому что взрослые специалисты интересуются живыми женщинами а не нарисованными на картинке и тем более не требуют «дать линк» под технической статьей.

"Можно ссылку" - это требуют??

Восхищен тем, как вы по-взрослому, твёрдо и четко, записали в малолетки всех мужчин старше 18, интересующихся аниме, мультипликацией и артом.

import com.sun.net.httpserver.*;

Но ведь com.sun.net.httpserver это собственно и есть http сервер (который в свою очередь просто обёртка над либой, для работы с сокетами java.net.InetSocketAddress). Ну да, оно встроено в джаву по умолчанию, т.к. джаву делали с явным прицелом на веб разработку. И насколько я понимаю, сейчас это уже негласный стандарт, встраивать в любой новый ЯП веб сервер, превращая тем самым язык в том числе и в веб фреймворк.

Ну а имея на руках http сервер, уже не сложно отправлять респонсы на реквесты. Ведь всю основную работу выполняет InetSocketAddress - в нём сила, брат!

, уже не сложно отправлять респонсы на реквесты.

Получается все было зря? О боже мой.

Ладно, в следующей раз будет статья про реализацию HTTP-сервера на чистом ассемблере.

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

А так, конечно не зря. В принципе чуть доработать и можете в виде книги оформить и продавать. Так в принципе и делают - есть ряд книг, которые учат делать веб приложения на произвольном ЯП с парой-тройкой страниц и формочкой силами либо встроенного в язык http сервера либо сторонней либой. И ничего - людям нравится, читают, потом пишут хорошие отзывы. У вас как раз формат приложения подходящий - 700 строк кода целиком влезут в книгу с подробными комментариями каждого действия.

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

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

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

Вы имеете в виду вообще без использования явского рантайма? Хотел бы я посмотреть на этот цирк с конями... Хотя, нет, не хотел бы - бессмысленность такой затеи поразительна сама по себе. ;)

Если вам принципиально, то достаточно давно существует утилита jpackage, поставляемая в составе JDK, с помощью которой можно генерировать готовые запускабельные бинарники с упакованным внутрь Java-приложением.

Проект из статьи запросто превращается в запускаемый бинарник размером ~ 3Мб без зависимости от JDK/JRE в системе.

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

Если вам принципиально

Мне - нет. Это мой собеседник чего-то такого ожидал. ;)

давно существует утилита jpackage,

Она всего лишь упаковывает рантайм в бинарник, выкидывая лишнее.

К сожалению эта интересная технология немного опоздала и в нынешних реалиях не особо кому нужна

Да, она и раньше не особо была нужна. Такие бинари давно можно было делать сторонними утилитами.

Такие бинари давно можно было делать сторонними утилитами.

И получались либо обертки, судорожно ищущие установленный на машине JRE либо распаковщики этого самого JRE при запуске. Либо JRE рядом .exe клали.

Нормального варианта создать полноценное нативное приложение на Java до jpackage не было.

Либо JRE рядом .exe клали.

Смешно. Вы сами-то хоть раз использовали этот jpackage?

Нормального варианта создать полноценное нативное приложение на Java до jpackage не было.

А что, как вы полагаете, делает jpackage? :)
Думаете, он прямо компилирует байткод в нативный? :)

А я вам скажу что он делает НА САМОМ ДЕЛЕ ))
Он создаёт ИНСТАЛЛЯТОР приложения. Да, инсталлятор действительно нативный. И этот инсталлятор устанавливает в вашу систему нативную jre, jar-ник вашего приложения, jar-ники используемых либ и небольшой нативный бинарник-запускалку всего этого добра-бобра :). Вот, только что собрал стандартный шаблонный хелловордовый fx проектик который идея предлагает шаблоном. Инсталлятор получился размером в 55 мегабайт (ни о каких 3 мегабайтах не может быть и речи). Я его установил, посмотрел что он там установил - почти нормальная такая jre - благодаря модульности он её, конечно, почикал. Тыкнул в программулину и... она не заработала :). Ну, это fx, там надо было либы fx-овские подкладывать - лень разбираться. Но работает это вот так, а не так, как вы вообразили. Я же говорю - такого добра и раньше было в достатке и оно никогда не было особо востребованным.

Вообще-то помимо инсталлятора создается еще и сам запускаемый рантайм (сюрприз) путем нарезания JRE, вот пример под мак (обратите внимание на каталог runtime).

За создание этого рантайма отвечает другая утилита jlink, которую jpackage вызывает при работе.

~3мб достигается если выкинуть практически все модули, особенно модуль java.desktop (он самый большой).

Но скажу сразу, что для более-менее крупных приложений такое урезание невозможно, поскольку в модуль java.desktop запихана часть javax.el (Expression Language) и потому никакие Tomcat/Jetty на таком обрезанном рантайме не заработают.

Поэтому если вам действительно надо генерировать бинарник из настоящего Java-приложения, то стоит использовать GraalVM, который действительно его создает и честно падает с "page fault" при ошибках.

Вообще-то помимо инсталлятора создается еще и сам запускаемый рантайм (сюрприз) путем нарезания JRE

А я что написал? (сюрприз!) - И этот инсталлятор устанавливает в вашу систему нативную jre, jar-ник вашего приложения, jar-ники используемых либ и небольшой нативный бинарник-запускалку всего этого добра-бобра

Таким образом, ни о каком "полноценном нативном приложении" c "готовые запускабельные бинарники с упакованным внутрь Java-приложением" не может быть и речи. Делается ровно то, что делалось раньше сторонними приложухами - "Либо JRE рядом .exe клали."

то стоит использовать GraalVM

угу. Только это даже рядом не jpackage

>На чистом ассемблере
А вот это я бы с удовольствием почитал

Читал вас ещё на другом ресурсе посвященным линуксу. Рад, что вы продолжаете свое дело. Всегда читаю с интересом ваши статьи, спасибо.

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

Нормально.

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

Писать собственный шаблонизатор — чуть более спорная затея. Для этого на свете есть достаточно удобные и при этом тонкие либы. А для HTTP сервиса всё-таки стоит брать jetty, встроенный совсем уж убогий.

Но в целом вполне зачёт. Полезное упражнение.

Писать собственный шаблонизатор — чуть более спорная затея.

Ага а собственная реализация недопарсера JSON — прям божья благодать ) Весь этот проект целиком — не более чем упражение, применять такие подходы в реальном проекте стоит только если вы очень хорошо разбираетесь в теме.

Написание потокового парсера для какого-нибудь простого регулярного формата вполне валидная бизнесовая задача. Мне в своё время пришлось писать парсер для .po, например. Потому что нормальной легковесной либы не нашлось :) Или вот быстрый glob pattern парсер в текущем проекте оказался нужен, а сторонние все или слишком перегружены, или не умеют в полный синтаксис. Взял и написал.

Потренироваться на json, чтобы не пугали потом такие задачи, весьма неплохо.

Разумеется я не ставлю под сомнение, что в некоторых случаях даже «самостийная и краснознаменная реализация» JSON — оправдана (если ваша компания называется «Яндекс» например).

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

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

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

В яндексе процветает NIH

Яндекс хотя-бы идет на такой шаг осознанно и может себе такое позволить.

и в более мелких конторах иногда приходится велосипедить с такими вещами

Видите ли путь библиотеки (например парсера JSON) на реализации только начинается. Самое важное — что будет потом: огромное количество тестов, кейсы использования, примеры реализации, замеры производительности и выпуск релизов.

Как только библиотекой начинают пользоваться — 90% работы уходит на тесты и сопровождение. Как бы вы не старались, такой огромный масштаб работы спрятать не получится, поэтому ситуация когда:

Не все кто «сверху» к этому бывают морально готовы...

имеет все шансы закончиться для вас крайне печальным образом.

Вы сейчас рассуждаете так, как будто каждой внутренней либой будет пользоваться мильён человек за пределами конторы. Нет, не будут. На то либа и внутренняя, часто специфичная для проекта, её почти никто не увидит, и вряд ли когда-либо переиспользует. Это нормально.

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

Более того. Частенько бывает, что для разбора какого-нибудь микроформата используется код, выдернутый из подходящего опенсорсного проекта, и обрезанный до нужного feature scope. Чтобы не тянуть лишнего, просто берём и копируем нужные классы к себе, без обвеса, который за пределами необходимого.

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

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

В очередной раз приведу пример с поддержкой юникода в JSON на уровне стандарта (а не только UTF-8, как все почему-то думают).

Если вы реализовываете свой парсер JSON и не делаете поддержку юникода (или делаете неправильно), то рискуете словить ошибки обработки данных уже в клиентском коде, причем в работающей системе — на проде вам в API прилетит такой JSON и привет.

используется код, выдернутый из подходящего опенсорсного проекта, и обрезанный до нужного feature scope. 

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

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

«Что дозволено Юпитеру — не дозволено Быку», не надо слепо повторять за Яндексом и Гуглом, не имея их бюджетов.

Если бы вы знали, как на заводе делается колбаса, вы бы не стали её есть (с)

Ваша наивность и, прямо скажем, некоторый максимализм несколько удивляют. Зачем вообще вы написали эту статью тогда? Из принципа «Дети, никогда так не делайте?», что ли? Ну так дети и не будут.

Если бы вы знали, как на заводе делается колбаса, вы бы не стали её есть (с)

Именно по этой причине я не пишу софт для медицинских устройств )

Ваша наивность и, прямо скажем, некоторый максимализм несколько удивляют. 

Вас смутило описанное про ответственность? Ну так это реальность, а не максимализм.

Если берете в свой проект чужой код — берете и ответственность за его дальнейшую поддержку своими силами.

Или вы как-то по другому на это смотрите?

Я знаю, что делаю, так также почему именно так, и уж тем более зачем. И могу это всё доступно объяснить бизнесу. Но у меня и опыта 25 лет, и роль на проекте «техлид»... Собсно, моя задача и состоит в определении набора либ, стека технологий, и проч. Если я решу, что в каком-то месте лучше воткнуть самописный велосипед, там будет воткнут такой велосипед. Если увижу, что взять готовое решение оптимальнее (что и происходит в 90% случаев), то так оно и будет.

Я сразу, как увидел JSON, захотел поинтересоваться: а чего не CSV? Парсер и генератор сильно проще, типы вам не нужны (если все значения—строки).

Потому что до такого уровня безумия я еще не дошел, в современных браузерах есть встроенный парсер JSON, так что как минимум половина работы с форматом (клиентская часть) исчезает.

Тоже пришёл к тому, что тащить современные фреймворки в небольшой новый проект, или легаси с самописом, с которого надо слезть, избыточно.

Да, я из другого стека, но сути это не особо меняет. Свой di через рефлексию покрывающий 90% кейсов реализовал в 2017 для php, в 2019 для python. ORM свою писать в общем случае для проектов такого масштаба не стоит, и для остальных задач в любом мэйнстримовом языке есть куча пакетов реализующих всё необходимое (роутинг, шаблонизация, i18n), и фреймворк только ради di тащить будто лишнее ограничение архитектуры. Тем более если пакеты брать реализующие jsr / psr, чтобы их можно было легко заменить позже.

Тоже пришёл к тому, что тащить современные фреймворки в небольшой новый проект, или легаси с самописом, с которого надо слезть, избыточно.

Прежде чем приходить к такому интересному выводу, рекомендую ознакомиться с проектом OWASP и посмотреть сколько новых уязвимостей сейчас выкладывают в паблик за один день.

Дальше думаю стоит оценить риски, собственные таланты и объем свободного времени — действительно ли вы готовы вкладываться в поиск уязвимостей и защиту.

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

Проект в статье если что тестовый, в тексте специально написано несколько раз:

«не стоит так делать в реальном проекте».

Написано как раз потому что простота обманчива — существенный объем кода уходит на решения проблем с безопасностью и разумеется в 700 строк я бы просто не уложился.

сколько новых уязвимостей сейчас выкладывают в паблик за один день

Чем больше зависимостей и компонентов в системе - тем шире вектор атаки.

Простота может быть обманчива, а сложность убивает и требует ресурсов на поддержку, снижает возможность контроля. Плюс фреймворки создают вендор-лок и порой заставляют писать бизнес-логику специфичным для них образом, мешая тестированию (не уверен как в java с этим, но в моих тех.стеках частая картина).

Я не говорю что фреймворки и библиотеки зло и их нельзя использовать. Как по мне статья хорошо иллюстрирует что npm left-pad incident от лени и невежества. Простое локальное решение банальной задачи, может быть эффективнее импортированного и оправдано. Для разработчика владеющего языком и мозгом, а не только фреймворком.

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

Как по мне, если у вас такое приложение что тянуть спринг не имеет смысла, то можно и ручками все разрешить, и не городить ад с рефлексией и прочим

Спринг вообще в 80% случаев оверкилл, который берут чисто по привычке.

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

Спринг вообще в 80% случаев оверкилл, который берут чисто по привычке.

Понимаю что речь про современные вебсервисы, отдающие JSON из пары десятков однотипных методов, но если смотреть шире — Spring Framework еще очень небольшой, по сравнению с тем что живет в Java-мире.

А живет там например OSGi, который сложнее Spring наверное раза в два или RCP вроде Eclipse, первая же разработка под который будет незабываемой .

Не стоит забывать про Jakarta EE, в девичестве JEE, где каждый сервер приложений фактически является огромным фреймворком, доходящим до своего апофеоза в виде Websphere или Weblogic.

И это я еще не трогал более глобальные вещи вроде ESB или BPM, которые вообщем‑то тоже — фреймворки.

А есть еще Portlet API и Java Portals с монстрами вроде Liferay (не к ночи будет помянут), который тоже является огромным и сложным фреймворком, под который ведется разработка конечного приложения.

Вообщем переусложнение на ровном месте и бесконечное количество архитектурных слоев — конек Java.

Что-то вы батенька sending совсем уж mixed messages in your comments.

Не надо путать тёплое с мягким, то есть enterprise application containers и AOP/DI, это решения совершенно разной мощности и для разных нужд. Сравнивать их могут только люди, не разбирающиеся в теме. EE — это вообще-то набор стандартов.

Чем больше отвечаете в разных ветках комментов, тем больше складывается впечатление, что в голове у вас какая-то каша.

Да понятно давно что я ничего не знаю и понимаю, не утруждайте себя моей повторной оценкой, а то еще надорветесь.

Но думаю вам ваших лет опыта все же хватит, чтобы посмотреть на вещи чуть дальше терминов.

Так почти любой бэкенд фреймворк это зачастую оверкилл. Что тот же нест, что спринг. Но как по мне это не плохо

А так если нужна какая то такая тема, как вы описали, думаю можно накидать какую нить простенькую библиотечку с нужным функционалом

Да здравствует объектно дезориентированное программирование!

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

Реляционная СУБД уж слишком большая и сложная в плане реализации, не влезет в ~ 1000 строк никак. Вот пример такого проекта, написанный лектором для курса по устройству реляционных баз данных.

ну, у меня веб-сервер без библиотек, на чистом jdk. и не только json парсер-сериализатор, но и http-сервер тоже. причём с вебсокетами. и много чего другого. свой парсер-сериализатор der контейнеров для сертификатов и т.д.

В нынешние интересные времена, когда один только boilerplate (шаблон проекта) может занимать на диске гигабайт, а количество библиотек в самом обычном проекте приближается к паре сотен — данная статья может нанести психическую травму неподготовленному читателю и заставить задуматься о правильности выбора профессии.

Мне сложно представить себе, что это за такие "обычные проекты", где сотни библиотек. Я сталкивался с достаточно большим количеством проектов - и больших, с кодовой базой на миллионы строк кода. И поменьше. На разных языках. Но не помню ни одного, где хотя бы сотня библиотек набралась. Можно конечно с натяжкой сказать это про javascript, если считать непрямые завимимости. Но это звучит не слишком честно.

Эта объемная работа предназначена в первую очередь для профессионалов разработки на Java, которые уже имеют практический опыт с большими фреймворками, так популярными в этом болоте среде и смогут в полной мере оценить всю сложность работы «без всего».

Мне кажется целевая аудитория выбрана неверно. Данная статья действительно может быть полезна для новичков. Для профессионалов тут просто нет ничего интересного.

Мне сложно представить себе, что это за такие "обычные проекты", где сотни библиотек.

А вы не очень умный: это надо было умудриться процитировать текст со ссылкой на JHipster и не попробовать ее даже открыть )

Мне кажется целевая аудитория выбрана неверно. 

Если «кажется» — креститься надо, говорят помогает.

А вы не очень умный: это надо было умудриться процитировать текст со ссылкой на JHipster и не попробовать ее даже открыть )

  1. Исходя из текста, ссылка относится к "шаблону проекта". Не спорю с этим утверждением. Шаблон проекта может занимать и 10 Кб, и 10 Гб - личное дело каждого.

  2. Если же Вы считаете, что результат работы какого-то конкретного генератора проектов = "самый обычный проект", то хотелось бы увидеть какую-то статистику на этот счет.

  3. И даже при таком подходе, sample app по Вашей ссылке не имеет упомянутых "сотен библиотек".

Вывод: Автор пытается словить хайпа на громких утверждениях, которые не может подтвердить.

  1. И даже при таком подходе, sample app по Вашей ссылке не имеет упомянутых "сотен библиотек".

А вы собрать-то попробуйте ) Скачается из интернета примерно ~1.5Гб библиотек Java и где-то 55-60 тысяч библиотек для Node.js.

Вывод:

"Сказочный специалист" (ц)

Считать непрямые зависимости не имеет никакого смысла. С таким подходом можно и JDK представить как сотни библиотек (ну а чего, так и есть, просто запакованы в одну большую)

Считать непрямые зависимости не имеет никакого смысла.

И память наверное у вас тоже бесконечная?

Или вы думаете что все эти библиотеки скачиваются просто так, для коллекции?

С таким подходом можно и JDK представить как сотни библиотек (ну а чего, так и есть, просто запакованы в одну большую)

Не поверите, но именно так и произошло: в какой-то момент умные люди представили объем библиотек, поставляемых вместе с JDK и ужаснулись.

После чего появился проект Jigsaw (пила), созданный специально для задачи выпиливания всех лишних библиотек из JDK.

А вы говорите.

И память наверное у вас тоже бесконечная?

Или вы думаете что все эти библиотеки скачиваются просто так, для коллекции?

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

Во-вторых, выбор библиотек основывается на требованиях проекта. Есть много случаев, когда память не является ограничивающим фактором.

После чего появился проект Jigsaw (пила), созданный специально для задачи выпиливания всех лишних библиотек из JDK.

А для javascript, Вы не поверите, появились бандлеры с tree shake, которые выпилывают весь неиспользуемый код из финального бандла. Это не мешает Вам считать "60 тысяч библиотек".

Я думал тут статья про когнитивную сложность, к чему эти пассажи про память?

 Есть много случаев, когда память не является ограничивающим фактором.

Это практически девиз современной разработки. К сожалению.

Слушайте я не знаю что вам ответить, вот честно.

Есть на свете такая вещь как «инженерная культура», остатки которой еще позволяют ИТ‑индустрии хоть как‑то развиваться и не разваливаться под зловонными массами «современных решений».

Передать вам эту культуру одной перепиской не получится, если вы не понимаете в чем проблема использовать «50к библиотек для CRUD‑проекта» — ну у вас этой культуры просто нет, что печально.

Передать вам эту культуру одной перепиской не получится, если вы не понимаете в чем проблема использовать «50к библиотек для CRUD‑проекта» — ну у вас этой культуры просто нет, что печально.

Я прекрасно понимаю. И именно поэтому я не пользуюсь всякими "генераторми проектов", где какие-то случайные люди решили, что они лучше меня знают, что именно нужно для моего проекта.

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

Но Вы переводите разговор в русло производительности, заявляя про ограничения памяти и т.д. В данном контексте видимо идея статьи в том, чтобы переписать все нужные библиотеки самому, выкинув лично Вам ненужый функционал? Очередное устранение "фатального недостатка".

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

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

Называется это «коммерческая разработка», которой на сегодняшний день большинство.

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

Но это повторюсь — большая редкость и чаще всего дело приходится иметь с уже существущей и работающей системой, которую надо «просто доработать» или что‑то в ней исправить.

В этом случае думаю вполне очевидно — ничего глобального менять вам просто не дадут.

я еще меньше стал понимать смысл данной статьи

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

Отдых для уставшей души — временами надо увидеть, что хоть где‑то может быть по другому.

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

Подкованные блохи это из разряда диковин, все же.

Заголовок "Content-Encoding" добавьте.

Интересный эксперимент :)

Что делать если оказался на необитаемом информационном острове, нужно написать свой сайт с блек-джеком и всем прилагающимся, а из инструментария у тебя один только голый JDK.

Ох уж этот сам себя нахваливающий автор-графоман, со своими статьями "для профессионалов разработки на Java", объясняющий, что такое DI-контейнер и "откуда сервлет достаёт параметры HTTP-запроса".

У вас в первом же куске кода (пример использования сервера) лажа: вы задаёте заголовок "Content-Length" как длину строки в символах, а потом в тело ответа эту строку с кириллическими буквами кодируете в байты в UTF-8, у вас получится не 8, а 15 байт.

Контейнер у вас забавный: вам надо бины и пометить интерфейсом, и передать в setup(). Теоретически можно было бы считать, что в setup() передавать надо только те бины, которые потом через getInstance() получают, но нифига не сработает, ваш массив классов cl ограничен тем размером, который передаётся в setup(). Вообще не очень понятно, зачем marker-интерфейс Dependency, всё равно у вас все параметры конструктора должны быть внедряемыми зависимостями, иначе вы экземпляр не создадите. Метод addClassNum(), который проходится по массиву, включая незаполненную часть, и проверяющий классы через equals() - не очень производительно.

В хранилище сессий у вас утечка памяти: если пользователь не делает явно "logout", а просто закрывает страницу и уходит, то сессия останется в хранилище навечно. Поэтому не непонятно, нафига вы делали протухание сессий? Этот механизм не от хорошей жизни придуман, он может доставлять неудобство пользователю, но в настоящих http-серверах сессия вычистилась по таймеру и потом её уже взять неоткуда, а вы её удаляете и тут же создаёте новую. С вашим ограничением на количество сессий - беда, новые пользователи зайти не смогут.

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

вы задаёте заголовок "Content-Length" как длину строки в символах, а потом в тело ответа эту строку с кириллическими буквами кодируете в байты в UTF-8, у вас получится не 8, а 15 байт.

Как безусловный профессионал, вы ведь знаете что все строки в Java в юникоде? Ведь так, о великий гуру?

Как профессионал, вы должня знать о UTF-8, UCS-2, UTF-16, и о том, на каком из них основан API строк в Java.

Да я сельский неуч, вчера только компьютер увидавший, что вы. Но идею по поводу длины строки уловили? Или картинку нарисовать?

А нарисуйте. Мне теперь даже интересно понять, в каком именно месте там будет ошибка.

На память - UCS-16.

Пойду почитаю, как оно на самом деле.

UPD: почитал, выяснил, что я все спутал.

Знаю конечно.

Выражение "это тест".length() вернёт 8. Выражение "это тест".getBytes("UTF-8").length вернёт 15. Вы в заголовок "Content-Length" передали первое, причём сразу через sendHeaders(), чтобы улетело, а в response body передали второе.

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

все строки в Java в юникоде

Вы довольно смутно понимаете причём тут юникод, надо сказать... Туда же и прочие по коду response.getBytes() без кодировок. Джуновские такие косячки. Ну и ладно вроде как, проект джаст фор фан, как говорится. И на жаве всё равно так не пишут (почти в каждом методе). Просто поразительно сколько при этом гонора, скромнее надо быть)

Контейнер у вас забавный: вам надо бины и пометить интерфейсом, и передать в setup().

Это остатки более сложной логики инстанциации, которая учитывала наличие примитивных типов, чтобы можно было передавать числа и строки и сделать некий аналог @Value.

Вот тут была ветка с учетом примитивов:

 for (Class<?> p : c.getParameterTypes())
                if (Dependency.class.isAssignableFrom(p) && services.containsKey(p)) params.add(services.get(p));
           

К сожалению компактно сделать не удалось и пришлось порезать.

 если пользователь не делает явно "logout", а просто закрывает страницу и уходит, то сессия останется в хранилище навечно.

Как видите есть две проверки:

           // disallow creation if max sessions limit is reached
            if (registeredUsers.size() > MAX_SESSIONS) return null;
            // disallow creation if there is existing session
            if (registeredUsers.containsKey(user.username)) return null;
 

Первая как раз не позволяет бесконечный рост хранилища сессий - те той самой утечки никогда не произойдет.

Вторая не дает повторную регистрацию, если сессия для логина уже есть, поэтому получается связка 1 к 1 всегда: один пользователь можен открыть только одну сессию.

Наконец вот эта логика удаляет существующую сессию при устаревании:

  if (s.created.plusHours(SESSION_EXPIRE_HOURS).isBefore(java.time.LocalDateTime.now())) {
                LOG.log(Level.INFO, "removing expired session: %s for user: %s".formatted(s.sessionId, s.user.username));
                sessions.remove(sessionId); return false;
            }

Код вызывается при любом обращении к странице где есть сессионность.

Поэтому описанный вами кейс:

если пользователь не делает явно "logout", а просто закрывает страницу и уходит, то сессия останется в хранилище навечно.

возможен только если он не просто уйдет а еще и удалит cookie с id сессии.

Во всех остальных случаях произойдет обращение с id из cookie и сессия мирно удалится при устаревании.

Для демо проекта этого достаточно, поскольку логика с фоновой обработкой хранилища сессий и автоудалением - больше и сложнее.

Простой код у джавистов вызывает раздражение и несварение.

Но ведь писать просто можно, да, не на уровне написания своего json serializer, но свою то логику можно и попроще. Просто сколько залезал в джава код — всегда видел сильную размазанность и обилие папок с 2-3 файликами, где названия вроде вот они — мои, зашел... а там интерфейсы голые, ат ы уже по самые помидоры на глубине папок. И бегаешь ищешь чего и где :) другое дело в го библиотеках: в среднем примере тупо все файлы в одной папке :)

на самом деле это просто низкая инженерная культура. На java можно писать нормально, если не делать всё со всем связанным и всё подряд со скоупом public.

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

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

К сожалению это редко когда возможно в современной разработке.

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

Есть требования к JPA Entity классам, требования к POJO сериализуемым в JSON через какой-нибудь Jackson, требования к CDI бинам, требования к Repository в Spring и так далее и тому подобное.

В результате вся прикладная разработка так или иначе выстраивается вокруг запросов используемых фреймворков а не ваших архитектурных идей.

требования к JPA Entity классам

Но ведь дата-маппер придуман, чтобы требований к ентити не было, "пишите свою сущность", а маппер заммапит. Никто не просил обвязывать их ломбуком.

требования к POJO

Требования к Plain Object ... звучит как оксюморон

В результате вся прикладная разработка так или иначе выстраивается вокруг запросов используемых фреймворков

Но ведь никто не требует написать простой сервис без абстрактных фабрик и стратегий, который просто сделает работу (например как в го пишут и не испытывают проблем с этим), да, немного копипасты, но вся копипаст — проблема, особенно пока "вдруг нам понадобится" еще не наступило

Но ведь дата-маппер придуман, чтобы требований к ентити не было, "пишите свою сущность", а маппер заммапит. Никто не просил обвязывать их ломбуком.

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

А что если "надо такой же но с перламутровыми пуговицами": в Entity например старый java.util.Date а в DTO уже LocalDateTime (что сплошь и рядом)? Конвертер будете писать? Для маппера?

Требования к Plain Object .

Я вас удивлю, сказав что существуют требования оформления к POJO?

(например как в го пишут и не испытывают проблем с этим

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

Я думал вы в этой статье как раз подсвечиваете, что все можно делать просто :) Не так понял посыл статьи и комментарии

// создание DI контейнераfinal TinyDI notDI = new TinyDI();// инициализация - указываем все классы являющиеся зависимостямиnotDI.setup(List.of(Users.class,Sessions.class,LocaleStorage.class, BookRecordStorage.class,RestAPI.class,Expression.class, Json.class,PageHandler.class,ResourceHandler.class));

По-моему вы просто реализовали свой контейнер IoC, а не DI. Не проще было использовать синглтоны с фабричными методами и не городить огород с контейнерами ?

проще было использовать синглтоны с фабричными методами

Проще конечно, но не так показательно.

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

Ещё бы DI убрать. Только лучше бы стало)

Все реализовано в виде одного класса с некоторой вложенностью

я не совсем понял, для чего один класс? Почему 12 вложенных классов (и один интерфейс) не разнести по отдельным файлам? Вроде бы это должно способствовать читаемости кода, поддерживаемости, банально удобству и пр.

И что в "голом" проекте делает pom.xml ?)

  не совсем понял, для чего один класс?

Замечательный вопрос для человека с таким-то ником )

Постараюсь ответить максимально четко: чем меньше файлов с исходным кодом в проекте — тем меньше проблем. Для всех и вне зависимости от языка разработки.

Меньше рисков что-то потерять при коммите, меньше нерешаемых конфликтов при сведении изменений от нескольких разработчиков.

Сильно проще провести ревью коммита с парой файлов но сотней правок, нежели с сотней файлов но по паре правок в каждом.

Чем меньше исходных файлов — тем проще и быстрее идет сборка, поскольку на каждый исходный файл генерируется минимум один.class файл (для Java).

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

Если один файл будут менять много разработчиков - вы будете постоянно страдать от конфликтов. Современные ide отлично справляются с большим количеством файлов и разработчики за счет этого работают в разных частях проекта и не толкаются. Кроме того, системы вроде гитлаба давно отображают изменения как простыню, как будто это один большой файл, в которой всё как на ладони. Рабочее место разработчика должно очевидно соответствовать моменту с точки зрения ресурсов.

Мне статья понравилась. Сразу вспомнилось, как я сам в 2011 году велосипедил di для андроида, а в 2007 писал свою СУБД на файликах и sax парсер xmlя по-честному на грамматиках, без всякого antlra. Ностальгия, так что как забавное чтение перед сном вполне. В остальном вопрос только к уровню - почему он сложный?..

Везде, где только возможно, стараюсь не использовать фреймворки. И хотя стандартный RT джавы уже содержит немало всего, вместе с тем ч не против библиотек, которые содержат максимально необходимый функционал без прочего мусора. Например, для API неплохо подходит Javalin. Template engines вне фреймворка огромное количество (очень прикольный j2html). Для металлизации JSON есть Gson. Для доступа к базе JDBI.

Насчёт DI: в большинстве небольших проектов он вообще лишний, ибо связать 5 бинов можно и вручную. Просто на каждый бин в ApplicationContext заводим соответствующий порождающий метод и все. Там же где он нужен, я использую Guice.

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории