Comments 81
Можно без него, вот только придётся изобретать велосипеды. Много велосипедов.
Велосипеды?
Простой сервис "JSON api JAX-RS -> вызов процедур БД Oracle"
Зачем тут Spring (Spring Container, например)?
И какие велосипеды нужно изобретать?
Транзакционность, например.
JPA — это Java Persistence API. Вероятно, вы имели ввиду JTA?
Спринг/Спринг Бут очень сильно упрощают разроботку.
Типовой фреймворк под типовые задачи, да. Это относится к любым средствам разработки, к той же яве. Например, массив размером больше 2^32 средствами самого языка выделить нельзя, т.к. величина "размер массива" типа int
.
Казалось бы, серьёзный недостаток. Вот только если посмотреть на количество разработчиков, которым действительно нужно решать эту задачу, то их очень мало от общего числа пользователей языка.
Со Спрингом примерно так же.
Щас на любой вопрос по спрингу есть ответ в стек оверфлоу
Транзакционность, например.
Эта разница принципиально, что бы для нее тянуть SpringBoot библиотеки?
И бины только ради jdbcTemplate?
Классический Spring подход к вызову процедуры:
@Transactional
public void putSomething(String s) {
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("BBB")
.withFunctionName("putSSS")
.declareParameters(
new SqlParameter("p$SSS", Types.VARCHAR),
);
SqlParameterSource in = new MapSqlParameterSource().addValue("p$SSS", s);
jdbcCall.execute(in)
};
Cильно лучше (короче и понятнее) чем напрямую с jdbc (и пулом конеектов от Oracle)? С явным commit? там где это МНЕ нужно.
public void putSomething(String s) {
try (Connection conn = pds.getConnection()) {
try (CallableStatement stmt = conn.prepareCall("begin BBB.putSSS(?); end;")) {
stmt.setString(1, s);
stmt.executeUpdate();
conn.commit();
}
}
}
А кто из здесь читающий сможет сразу сказать как выполнить тонкую специфичную настройку пула коннектов и управлять им динамически в Spring? Все скрыто "магией" и приходится копаться в Интернете и в конечном итоге в Spring исходниках, что бы понять как...
SpringBoot удобен там где он удобен… Не обязательно его пихать везде. Мир им не ограничивается. guice для некоторых целей удобнее и проще чем Spring.
Не понимаю этого хайпа по спрингу. Один из инструментов. Не икона на стену.
А кто из здесь читающий сможет сразу сказать как выполнить тонкую специфичную настройку пула коннектов
1) не вижу в вашем примере тонкой или какой-то особой настройки пула коннектов. Всё очень рутинно, и как раз от этой повседневности и призван избавить Спринг.
2) ваш пример предполагает, что сложная логика вынесена в хранимку в БД (кстати, как отлаживать её будете?)
3) если вы очень переживаете о производительности, то в случае с БД 90% времени скорее всего придётся на сетевое взаимодействие с базой и исполнение запроса, так что отказ от фрейворка не даст какого-то сверхвыигрыша.
Я всё ещё не вижу внятной аргументации в пользу отказа от Спринга. Всё сводится к "спринг плохой потому что потому".
Не понимаю этого хайпа по спрингу.
Если вы внимательно прочитаете статью, то увидите, что она не претендует на какое-то откровение или на роль Евангелия от Холлера. Это некоторая сумма опыта, и описание граблей с целью избавить от боли ещё не наступивших на них, не более того.
1) не вижу в вашем примере тонкой или какой-то особой настройки пула коннектов. Всё очень рутинно, и как раз от этой повседневности и призван избавить Спринг.
Причем тут мой пример. Я им иллюстрировал конкретную простую задачу (озвучил в первом посте). Решение которой не отличается ни объемом ни сложность в обоих примерах. Мое замечание, к этому конкретному примеру прямого отношение не имеет.
А мой комментарий в конце… Я просто точно знаю, что любой шаг вправо-влево от типовой задачи — это долгий поиск решения. И зачастую копание в исходниках Spring.
2) ваш пример предполагает, что сложная логика вынесена в хранимку в БД (кстати, как отлаживать её будете?)
Там где я сейчас работаю, традиционно, слой бизнес логики целиком в БД (PL/SQL). Все работа через API процедур/функций и представления (view)
Есть плюсы… есть минусы такого подхода. Но это отдельная тема. Да и какая разница. Так есть.
так что отказ от фрейворка не даст какого-то сверхвыигрыша.
Не даст. Но я не это хотел сказать… А то, что задачи могут решаться разными способами. И утверждение, что Spring ОБЯЗАТЕЛЬНО дает какой тов выигрыш — это заблуждение. И то, что "велосипед изобретать" — это плохо. Это то же заблуждение. Иногда нужно. Как минимум, нужно уметь.
Я всё ещё не вижу внятной аргументации в пользу отказа от Спринга. Всё сводится к "спринг плохой потому что потому".
Где Вы увидели призыв отказаться от Спринга? Основной посыл — использовать то, что подходит к конкретной задаче.
Я видел, например, решения, когда ради 3-х (!) вызовов БД (однотипные select из view) в приложение пихался Hibernate! А ничего, что он +5 сек при старте отъедает на моем рабочем компе… И все что нужно, это "изобрести велосипед" из нескольких строчек кода (ResultSet в Map<Map<String, Object>>). Вместо нудного описания Entity классов..
Из за этого я же не буду призывать отказаться от JPA и конкретных реализаций JPA.
+5 сек
да не может быть
Если сущностей сотни-полторы, то легко. А если есть ещё и Spring Data, то добавьте время проверки запрсов (они проверяются при поднятии контекста). Я об этом ранее писал: https://habr.com/ru/post/439918/
Выпиливание лишней зависимости даёт -20 секунд ко времени запуска приложения.
да и разве не плевать, если даже пару минут стартует.
если вы для ручного теста постоянно перезапускаете приложение, то переходите на автотесты
сотни-полторы
переходите от монолита к микросервисам, или хотябы подробите монолит.
Ещё как может. Я видел проект, в котором основное приложение запускалось ок. 15 минут, и 12 минут как минимум из этого времени съедал Hibernate.
Вам же прямо сказали — чуть в сторону и сразу всё очень плохо. Это что, достоинство?
Нет проектов, где не нужно «в сторону». Просто нет. Детский сад, понятно, исключаем. Поэтому любой, кто погружается в спринг, всегда будет обязан долго и нудно изучать его недра, а потом через известное место подстраиваться под них, просто что бы выполнить поставленную задачу. И это — очень напрягает. Вам нравится? Ну ради бога. Но я вот — не мазохист.
чуть в сторону и сразу всё очень плохо
Я работаю со Спрингом уже 6 лет и приходилось решать довольно головоломные задачи. Иногда приходилось лазить в кишки, да. В 1 особом случае из 100. Во всех остальных хватало яндекса и СО.
Поэтому любой, кто погружается в спринг, всегда будет обязан долго и нудно изучать его недра
Без примеров (пока я не увидел не одного) всё это общие слова. Как правило (сужу по своему опыту) человек, лезущий в кишки в 9/10 невнимательно прочитал документацию. А она в Спринге удивительно хороша, покрывает почти все тонкости.
Опять же накоплен огромный опыт, который позволяет 99 задач из 100 решить одним поисковым запросом.
Хотите спорить — приводите примеры из жизни.
Да, за 6 лет можно многое изучить. Вот поэтому вам и кажется, что «В 1 особом случае из 100». Я же, когда пробовал это чудо, получил гораздо больше проблем. И именно в начале пути. То есть если бы я усидчиво 6 лет изучал, как мне решить порождённые спрингом проблемы при помощи спринга, то наверняка сегодня я бы их почти все решил. Но я сделал другой выбор — отказался от спринга. Хотя да, поэтому меня нельзя назвать экспертов по нему, ну и к моему мнению можно не прислушиваться. Только вы знаете, потом мне было приятно ощущать отсутствие того самого напряжения, которое я почувствовал при попытке решить простые проблемы при помощи спринга.
>> Без примеров (пока я не увидел не одного) всё это общие слова.
Вам другие участники дали несколько примеров. Там ясно видно — ненужные приседания можно устранить, просто отказавшись от спринга. Разве это не так?
>> Как правило (сужу по своему опыту) человек, лезущий в кишки в 9/10 невнимательно прочитал документацию. А она в Спринге удивительно хороша, покрывает почти все тонкости.
На счёт качества документации — ну не знаю. Вот их сайт, искал на странице с гайдами по ключевому слову «архитектура». Нашёл одну (одну!!!) ссылку, и та — про безопасность. Внимание, вопрос — а где архитектура? Как без понимания архитектуры решать сложные вопросы? И как можно говорить, что «А она в Спринге удивительно хороша», когда там нет информации о самых важных вещах?
>> Опять же накоплен огромный опыт, который позволяет 99 задач из 100 решить одним поисковым запросом.
Опять же — ну не знаю, что у вас там за опыт. Опыт гугления (если речь о нём) приобретается за пару дней. А вот опыт изучения, понимания, и применения на практике информации по спрингу — это нечто большее, для такой задачи 99% решений не нагуглить. Хотя я не знаю, что у вас за задачи. Теоретически, всё то, что на 99% решается гуглением — это тривиальнейшие задачи. Хотя с другой стороны, если вы о поиске способа обхода внезапно возникающих проблем со спрингом, то сама необходимость постоянно гуглить (в 99% случаев) говорит о невозможности как-то осмысленно разобраться с происходящим внутри. И количество ответов по спрингу на СО — тоже свидетельство в пользу сложности сабжа. Там реально миллионы вопросов. Хорошо, что кто-то на них успел ответить, но может было бы лучше, если бы используемая библиотека не предполагала миллионы (!!!) вопросов? То есть не была такой запутанной и непрозрачной. Ну а если она запутанная и непрозрачная — зачем вообще её использовать? Только потому, что миллионы программистов очень часто ходили по этим граблям и смогут ответить на некоторые (не все, даже в вашей практике) вопросы?
В целом повторю своё впечатление — непрозрачно, много возни в нетривиальных случаях, а в тривиальных случаях код получается проще вообще без спринга. Хотя да, есть множество ответов на СО, есть толпа поимевших опыт работы с изделием (неглубокий, чаще всего). Если плыть по течению и стараться быть в толпе — ну ладно, тогда скорее выбор в пользу спринга. Но если понятие «архитектура» всё же важно для вас и вы не готовы постоянно прогибаться под чужой шаблон, по сути отдавая архитектуру на откуп этому шаблону, то я бы вам советовал подумать перед выбором такого инструмента.
Ну а тем, кто «уже 6 лет» и более — да, им остаётся только лишь монетизировать потраченное время путём расширения толпы фанатов. К счастью, меня сия участь миновала.
Конвертер тоже компонент ;)
Да, в этом его ценность. Кстати, если у вас "восьмёрка", то можно написать что-то вроде
@Component
class LongToString implements Function<Long, String> {
}
@Component
class StringToLong implements Function<String, Long> {
}
И использовать так же:
@Component
@RequiredArgsConstructor
public class DI {
private final Function<Long, String> longToString;
private final Function<String, Long> stringToLong;
}
Думаю, будет работать.
org.springframework.core.convert.ConversionService
Примерно так:
User user = conversionService.convert(userObject, User.class)
Spring сам найдет нужный Converter (по типу объекта userObject и target class) и вызовет converter.
Спасибо за ваш комментарий! Во-первых, я не знал об этом, во-вторых, ваш коммент один из немногих по существу заметки.
Именно поэтому мы у себя используем свои мапперы, которые являются спринговыми бинами и обеспечивают типобезопасность на этапе компиляции. Нет маппера — ошибка компиляции.
Компилирйемость настройки — это скорее преимущество, т.к. ошибки времени исполнения превращаются в ошибки компиляции. Опять же, "Идея" умеет анализировать структуру проекта и прямо в классах указывать на возможные ошибки (неразрешенные зависимости, к примеру).
21 век, а спринг все еще не умеет в конфигурацию из разных источником сливать? То есть настройки в классе нельзя перебить переменными окружения?
Умеет, например тот же порт можно передать из коммандной строки.
1) Для этого есть профили: набор файлов application-dev.yml
, application-test.yml
, application-int.yml
, application-prod.yml
и т.д.
2) Если вам лень вносить изменения в файлы, то в build.gradle
прописываете что-то вроде
def psqlTestPort = project.hasProperty('psqlTestPort') ? project.getProperty('psqlTestPort') : '54321'
def psqlTestHost = project.hasProperty('psqlTestHost') ? project.getProperty('psqlTestHost') : 'localhost'
а в *.yml
spring:
datasource:
jdbc-url: jdbc:postgresql://${psqlTestHost}:${psqlTestPort}/postgres?currentSchema=AUTO
Теперь можно получить сборку с нужными значениями, подставив их в коммандную строку при сборке.
Список путей в ямле + @Value
в ява-конфигурации
Можно, только плюс ява-конфига в том, что ошибки/очепятки очень часто становятся ошибками компиляции. А у вас битый xml-конфиг упадёт только при запуске.
Подавляющее большинство приложений, написанных с нуля, используют именно ява-конфиг. ИМХО, распространённость технологии/подхода есть производная от её/его удобства/полезности.
Документацию пишут разработчики фрейворка, имеющие огромный опыт его разработки, внедрения, поддержки. Вы задумывались, почему вообще появился ява-конфиг? Ведь xml-конфиг уже существовал. Но разработчики почему-то заморочились и сделали другое решение.
Если взять ваш секьюрити xml, и выкусить из него описание какого-нибудь бина, то ошибку вы не увидите до запуска приложения.
А если написать ява-конфиг, при чём не передавать зависимости паарметрами в метод, а использовать вызов (как я показал в статье), то выкусывание бина превращается в ошибку компиляции и красный редактор в "Идее".
Это же касается других примеров из статьи. Пакеты и имена классов можно описать в xml-е, ямле и свойствах, вот только опечатка не проявится до запуска. В случае использования классов ошибка в его наименовании/импорте становится ошибкой сборки.
И да нормальная ide вам еще на процессе валидации покажет, что в xml файле написана ерунда.
Если собирать проект без среды разработки, то ошибка компиляции всё равно станет видна ещё до запуска. В случае с хмл ошибка станет видна только после запуска.
Потому что банально надо было обеспечить более быстрый и удобный запуск для программистов.
Потому что ява более гибкая, чем хмл. Как вы опишите типизацию бина в хмл-е? Никак. А в яве это раз плюнуть, и опять же ошибка станет ошибкой сборки, а не времени исполнения.
Потому что ява более гибкая, чем хмл. Как вы опишите типизацию бина в хмл-е?
Может покажете где вы такое в конфигурации используете?
Ну и да xml изначально декларативен, а вот в java для декларативности кода добавлены аннотации. В итоге для декларативности приходится их указывать и дописывать. В итоге конфигурация может сильно удлиняться в сложных случаях.
Простой пример мне надо было несколько view теплейт движков использовать. Один для страничек еще 4 было для различных документов и отчетов. В случае xml это было 8 объявленных bean прям в xml с указанием 5 разных viewClass и указанием приоритетов. Как подобное напилить в конфигурации и сколько там будет аннотаций?
Я без понятия, сколько в итоге получится аннотаций, так как с MVC почти не работаю.
Я констатирую факт: настройка проекта с помощью кода стала де-факто стандартом. Загрузка классов работает несравнимо быстрее чтения и разбора ХМЛ-а, а настройки, привязанные к профилям выносятся в application-*.yml
. Это сегодняшний день. ХМЛ — день вчерашний за редкими исключениями.
Загрузка классов работает несравнимо быстрее чтения и разбора ХМЛ-а, а настройки
У вас там xml в пару килобайт укладывается, вы о чем вообще? Я вон переключился на @Configuration аннотации и никакой разницы по скорости нет. Там дольше тот hibernate с spring jpa стартует.
Т.е. он пишет настройки контекста как код.
И при этом не надо учить новый язык.
При это можно ввести логику в создание контекста на понятном ЯП (Java).
А раз проще, то он и делается.
Дополнительно так-как это все же код, а не декларативные объявления что-то более сложное, требует больше писанины.
А XML это жесть.
Начинал как раз с 3-го Spring с конфигурированием xml.
Километровые простыни, в которых черт ногу сломит.
Простой пример мне надо было несколько view теплейт движков использовать. Один для страничек еще 4 было для различных документов и отчетов. В случае xml это было 8 объявленных bean прям в xml с указанием 5 разных viewClass и указанием приоритетов. Как подобное напилить в конфигурации и сколько там будет аннотаций?
Если надо дам вам xml файл и вы попробуете его завернуть в аннотации.
А XML это жесть.
Но стартует быстрее, чем аннотации ))
Существуют ли бенчмарки, подтверждающие это утверждение?
Честно говоря, не знаю. Я делаю такой вывод из того, что конфигурация с аннотациями скорее всего будет делать component scan, что не очень быстро.
Соглашусь, что через xml такая задача будет решаться проще. Не нужно дополнительных properties файлов и кода для считывания из них значений. Но опять же спринг сильно упрощает работу с properties файлами и сложностей там не должно быть.
Опыт работы есть и с xml и java конфигами. Мой выбор java конфиг.
И при этом люди уже не знают про xml и просто пишут кучу boilerplate кода.
Напишите статью на хабр с примерами ваших задач, которые проще решить с помощью ХМЛ. Серьёзно, я не подкалываю. Напишите, а сообщество посмотрит и оценит.
habr.com/ru/post/101546
Если их надо под пихнуть более одного то возникает ой в spring boot.
Тут, к сожалению, общая проблема с квалификацией кадров. На любой технологии.
Все настраиваемые параметры выносятся в стандартный yml конфиг.
Стандартные спринговые модули, типа БД и прочих, все имеют конфиги из коробки. Никаких проблем. Администраторы прекрасно меняют, что надо.
Почитайте документацию. 21 век же.
В книге лонга «джава в облаке» описано более 10 источников, как можно пропертя вносить
public interface Settings {
String property(String key);
default String property(@NonNull String key, String defaultValue) {
String value = property(key);
return StringUtils.isBlank(value) ? defaultValue : value;
}
void setProperty(String key, String value);
default void setProperty(@NonNull String key, @NonNull Object value) {
setProperty(key, value.toString());
}
// default implementations like getPropertyAsInt, getPropertyAsList, etc
}
@Service("settingsBasedOnApplicationProperties")
public class SettingsBasedOnApplicationProperties implements Settings {
private final Environment applicationProperties;
@Autowired
public SettingsBasedOnApplicationProperties(Environment applicationProperties) {
this.applicationProperties = applicationProperties;
}
@Override
public String property(@NonNull String key) {
String value = applicationProperties.getProperty(key);
return value == null? "" : value;
}
@Override
public void setProperty(String key, String value) {
throw new UnsupportedOperationException(
"It's readonly service."
);
}
}
@AllArgsConstructor
@Service("settingsBasedOnDatabaseProperties")
public final class SettingsBasedOnDatabaseProperties implements Settings {
private final SettingsRepository settingsRepository;
@Override
public String property(@NonNull String key) {
Setting value = settingsRepository.findOne(key);
return value == null ? "" : value.getValue();
}
public void setProperty(@NonNull String key, @NonNull String value) {
settingsRepository.save(new Setting(key, value));
}
}
@Service("settings")
public class SettingsImpl implements Settings {
private final Settings applicationProperties;
private final Settings databaseProperties;
@Autowired
public SettingsImpl(
@Qualifier("settingsBasedOnApplicationProperties") Settings applicationProperties,
@Qualifier("settingsBasedOnDatabaseProperties") Settings databaseProperties) {
this.applicationProperties = applicationProperties;
this.databaseProperties = databaseProperties;
}
@Override
public String property(String key) {
return StringUtils.isBlank(applicationProperties.property(key))
? databaseProperties.property(key)
: applicationProperties.property(key);
}
@Override
public void setProperty(String key, String value) {
databaseProperties.setProperty(key, value);
}
}
Таким образом, мы делаем Autowired для Settings и получаем значение проперти. Если она есть в конфиге (.properties файл или .yaml) или в environment variables — берем от туда. Если нет — берем с базы данных. Логику можно поменять и сделать значение в базе данных «главным». Похожим путем можно инкапсулировать получение значений с любых источников.
Конфигурирования бинов в классе, это очень хорошо.
Не нужно изучать два языка Java и XML.
С учетом текущей ориентированности Spring'а на микросервисы, это вообще замечательно.
Т.к. настройки по умолчанию, дают возможность просто добавить зависимость и получить уже готовый функционал, без написания портянки XML.
В случае если у вас микросервис и все необходимые конфигурации укладываются в то что предлагается разработчиками все хорошо и более менее прилично.
God object — это антипаттерн.
Если очень сильно хочется все настройки можно собрать в один пакет.
Если у вас монолитный монолит.
А так, я лично предпочитаю разделять монолит по отдельным модулям.
А потом в одном модуле, просто прописыванием зависимостей, собирать артефакт.
Что мы делаем не так со Спрингом