Уже много лет работая со спрингом, я заметил забавную закономерность: в каждой новой версии добавляется новый способ конфигурирования контекста. Давайте вспомним, как это было:
Консультируя и проводя тренинги в различных компаниях, я видел самое разное отношение к этим способам конфигурирования. Крупные компании, зачастую живущие по принципу «работает – не трогай», до сих пор лелеют старые xml -конфигурации, продолжая их множить и кормить новыми бинами. «Зато у нас все централизовано!» — кричат их архитекторы, добавляя 100500-тысячную строчку в xml-Бога.
Компании поменьше, пытающееся угнаться за новшеством технологий, беспощадно сжигают старые XML-ы, переписывая всё что могут, на аннотации, а что не могут на Java-конфиг. И уже потирают руки, пытаясь придумать, а куда бы им теперь приткнуть конфигурацию на грувях.
Видел я и совсем забавные ситуации, когда не очень разбирающийся во всей этой каше конфигураций джуниор, дублировал декларацию бинов, прописывая их и в xml-e и через аннотации (ну так чтобы наверняка).
А где же находится правда? Неужели как всегда посередине?
Давайте попробуем разобраться…
Для начала давайте сравним стратегии декларации бинов.
Если сравнивать затраченные усилия, то для данной конфигурации лидируют аннотации (их всего 6 штук, минус сеттер).
Но поскольку конфигурация полноценного приложения (а не отдельного модуля), включающее также сторонние библиотеки, не может держаться только на аннотациях, было бы более честно сравнивать между конфигурациями Groovy, Java и XML. Даже без очков видно, что конфигурация на груви лидирует по лаконичности и элегантности, однако мне хотелось бы оставить её на закуску. Тем более, что она пока ещё не допилена до конца.
А пока давайте обсудим недостатки и преимущества того, что было до выхода Spring 4.
Прочитав последнюю строчку, суровые критики уже наверняка сделали себе пометку, мол ага, автор статьи видать не в курсе про Factory Beans, SpEL и всякие хитрые профайлы… Но давайте будем честны, XML в данной таблице заработал и так немало плюсов, и на их фоне этот плюс будет смотреться притянутым за уши. SpEL довольно ограничен, а Factory Beans не являются частью XML-a. И положа руку на сердце, даже самые ярые поклонники XML-a вряд ли выбрали бы его как язык программирования.
Еще один вопрос, который мне часто задают на тренингах: «А почему не перекомпилировать при изменении конфигурации — это плюс? Кто рискнёт на продакшне менять имплементацию бинов не прогнав после этого тесты? А всякие мелочи типа имён, паролей и портов, можно просто держать в проперти файлах, содержимое которых так же можно менять ничего не перекомпилируя».
Тут будет уместно рассказать один случай из моей практики, которой можно озаглавить как-то так:
Несколько лет назад я работал в одной IT компании, в которой, из соображений безопасности, программистам нельзя было подключаться к рабочей среде через удалённый доступ. И вот после очередной трудовой недели, мы пошли отдыхать. Сидим в пабе, время — начало третьего утра, настроение отличное, и вдруг, звонит наш саппорт. Приезжай, говорит, срочно. Продакшн падает.
Знакомая ситуация? Есть много причин не хотеть ехать на работу в пятницу ночью… На мои попытки уговорить его попробовать решить всё по телефону, он твердит, что он только сапорт и единственное, что он умеет делать, это набирать номер телефона того, кого надо вызвать. В итоге он всё-таки согласился прочитать мне эксепшн. Проблема оказалась тривиальная: один из веб-сервисов начал глючить, и бин, который к нему обращался, выкидывал эксэпшн из-за которого падало всё приложение. Видимо, автор бина не ожидал такой засады, и эксепшн не отлавливался. А поскольку раньше такого не случалось, то QA пропустили этот код в продакшн.
Причем я прекрасно знал, что до понедельника сделать ничего нельзя, так как веб сервис не наш, и разговаривать не с кем. Но и оставить все как есть было нельзя, на сервере было еще много других job'ов, которые могли бы прекрасно работать, если бы выкинуть проблематичный бин из контекста. Я не буду углубляться в детали, но уверен, что вы сами можете продумать похожий сценарий. Вопрос в том, как можно поменять конфигурацию по телефону, разговаривая с человеком, который ничего не понимает в коде.
На моё счастье этот бин был прописан в xml-e. Дальше состоялся следующий диалог:
«Зайди на комп продакшна, открой папку JBoss/server/default/deploy найди там файл idi.ear и открой его. Что значит, как? Ладно переименуй его в zip и открывай. Отлично, видишь там папка META-INF? В ней есть applicationContext.xml? Зайди в него, найди там SchedulerFactoryBean и чуть ниже есть datacarTrigger. Закомментируй эту строчку. Что значит, как? Неважно, сотри её. Отлично, теперь запиши, закрой, переименуй обратно в ear и включай сервер по-новому.»
Ну и всё, дальше счастливый конец. А теперь представьте, что этот бин был бы прописан в java config… Пришлось бы видимо на работу ехать.
Таких историй можно рассказать много, и возможность поменять конфигурацию, ничего не компилируя и не строя заново, часто является большим плюсом. Начиная от аспектов, которые надо то включать, то выключать, то перенастраивать, и, заканчивая delivery-oriented компаниями, которые кастомизируют один и тот же продукт для разных клиентов.
Такая архитектора изначально подразумевает, что xml будет лежать вне архива (war, ear), причём там, где до него легко добраться. И скорее всего, в компаниях, где используется такой подход существует целая экосистема, заточенная под такой динамизм, со специальными тестами и ролбэками.
С другой стороны, когда речь идёт о сложных конфигурациях, которые не могут быть сугубо декларативными, работать с XML становится крайне неудобно. Все костыли, придуманные для этого еще в первых версиях Спринга, делают конфигурацию очень запутанной, и её трудно поддерживать
.
Конфигурация на Java имеет в таких случаях большое преимущество. Все бины, настройки которых требуют определённой бизнес логики, намного правильнее прописывать в ней. Не говоря о том, что для джава разработчика намного приятнее писать Джаву, а не XML.
Еще одно небольшое, но все же преимущество конфигурации на Java — это производительность. Все мы знаем, еще со школы, что «рефлекшн тормозит». А бины, прописанные в XML-e или при помощи аннотаций, будут создаваться именно с его помощью. Если все они синглтоны, то это не так важно: просто немного увеличится время бутсрапа. Однако, если ваша программа постоянно создаёт прототайпы, Reflection не желателен. Ну а с конфигурацией на Java, рефлекшн просто не используется. Бины создаются обычным Java-кодом.
Преимущество аннотаций заключается в том, что они позволяют уменьшить конфигурацию. Кроме того, если каждый новый бин, вынуждает разработчика бегать в конфиг, и что-то там править или дописывать, то помимо неудобства и постоянных мёрджей, возникает вероятность, того, что кто-то случайно испортит чужое. Кроме того, как мы прекрасно знаем, если в первом акте на сцене висит ружьё…
Именно поэтому, далеко не в каждой компании архитекторы позволяют всем подряд ковыряться в конфиге и прописывать там свои бины, опасаясь криворуких программистов, которые не ведают, что творят.
Кто-то скажет, мол, а вот мы не пользуемся аннотациями вообще: да это не всегда удобно, зато все централизовано. Ну тут одно из двух, либо ваш проект очень маленький, и спринга в нём кот наплакал, либо вся эта «централизованность», не вносит ясности. Огромные XML-ы или Java-конфиги, разобраться в которых может только гроссмейстер по шахматам, в итоге приводят к ещё большей путанице.
Все наши классы, которые должны являться бинами Спринга, объявляются и настраиваются аннотациями, красиво раскладываются по пэкэджам и сканируются, по указанию центральной конфигурации.
Все бины из сторонних библиотек, и то, что нуждается в сложной конфигурации определяется в Java config.
А та часть конфигурации, которая нуждается в динамизме выносится в XML, который, вдобавок можно еще и хранить, как внешний ресурс.
Зачем же нужно было придумывать конфигурацию на Groovy, когда и без нее уже хватало бардака? Груви, конечно сегодня в моде, но разве это причина добавлять ещё одну конфигурацию, увеличивая путаницу? Давайте разберемся в чем его суть?
Идея Groovy-конфигурации заключается в том, чтобы заменить всё, кроме аннотаций. Она обладает всеми плюсами XML – ведь это скрипт, и его так же можно держать как внешний ресурс. Более того динамизму может быть еще больше, в потенциале, изменения конфигурации не будут требовать рестарта.
С другой стороны, она обладает и всеми плюсами Java-конфига, и более того, код на груви, лаконичней, элегантней и мощнее.
Правда, на данном этапе, его еще нельзя в полной мере использовать вместо Java-конфигурации, потому что отсутствует поддержка namespac'ов а ля @EnableAspectJAutoProxy, @EnableAsync и многое другое. Это, конечно, можно решить, настроив все нужные бины в конфигурации, но чтобы не заморачиваться, проще пока оставить Java-конфиг. А вот XML уже можно выкидывать :)
Чувствуя, как от последней фразы сжались кулаки у сторонников XML-а, я предлагаю устроить батл.
В синий угол ринга приглашаются все оптимисты, которые не боятся новых технологий, а наоборот пытаются их внедрять, чтобы двигаться вперед самим и двигать свою компанию. Они будут сражаться за максимальный динамизм, за возможность девелоперов делать больше и проще, за Agile-разработку и за технические инновации.
В красный угол ринга приглашаются наученные горьким опытом реалисты, которые знают, что произойдёт, когда у всех появится сила. Эти люди сегодня, ценят то, что конфигурация на джаве — Type Safe, а XML не даёт никому возможность наломать дров.
Ну а пока батл продолжается, я рискну высказать своё предположение, чем закончится война против XML-ов. Давайте ещё раз посмотрим на картинку и вспомним, старый фильм:
Шварценеггер, конечно, в итоге победил, но какой ценой…
Ну а если кто захочет продолжить баттл в живую, приходите на мой доклад: Spring the Ripper, кто хочет серьёзно поднять свои навыки — приходите на тренинг Spring for Seniors
- В первом спринге конфигурацию можно было писать исключительно на xml-e. (ClassPathXmlApplicationContext(“context.xml”))
- Во втором (точнее с 2.5) появилось возможность создавать контекст через аннотации. (AnnotationConfigApplicationContext(“package.name”))
- Третий спринг добавил конфигурацию на джаве. (AnnotationConfigApplicationContext(JavaConfig.class))
- Четвёртый тоже сохранил традицию и уже с декабря 2013 года можно конфигурировать при помощи груви скриптов (GenericGroovyApplicationContext(“context.groovy”))
Консультируя и проводя тренинги в различных компаниях, я видел самое разное отношение к этим способам конфигурирования. Крупные компании, зачастую живущие по принципу «работает – не трогай», до сих пор лелеют старые xml -конфигурации, продолжая их множить и кормить новыми бинами. «Зато у нас все централизовано!» — кричат их архитекторы, добавляя 100500-тысячную строчку в xml-Бога.
Компании поменьше, пытающееся угнаться за новшеством технологий, беспощадно сжигают старые XML-ы, переписывая всё что могут, на аннотации, а что не могут на Java-конфиг. И уже потирают руки, пытаясь придумать, а куда бы им теперь приткнуть конфигурацию на грувях.
Видел я и совсем забавные ситуации, когда не очень разбирающийся во всей этой каше конфигураций джуниор, дублировал декларацию бинов, прописывая их и в xml-e и через аннотации (ну так чтобы наверняка).
А где же находится правда? Неужели как всегда посередине?
Давайте попробуем разобраться…
Для начала давайте сравним стратегии декларации бинов.
Начнём с классического XML-a:
<beans....>
<bean class="com.inwhite.spring.compare.CoolDaoImpl" id="coolDao"/>
<bean id ="coolService" class="com.inwhite.spring.compare.CoolServiceImpl"
init-method="init"
destroy-method="closeResources"
scope="prototype">
<property name="dao" ref="coolDao"/>
</bean>
</beans>
Теперь тоже самое, но при помощи аннотаций
@Repository
public class CoolDaoImpl implements CoolDao {
@Override
public void doCRUD() {
//some logic here
}
}
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class CoolServiceImpl implements CoolService {
@Autowired
private CoolDao dao;
@PostConstruct
public void init() {
//init logic here
}
@PreDestroy
public void closeResources() {
//close resources here
}
@Override
public void doWork() {
dao.doCRUD();
}
}
Ещё разок, но с конфигурацией на джаве:
@Configuration
public class JavaConfig {
@Bean
public CoolDao dao(){
return new CoolDaoImpl();
}
@Bean(initMethod = "init", destroyMethod = "closeResources")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public CoolService coolService(){
CoolServiceImpl service = new CoolServiceImpl();
service.setDao(dao());
return service;
}
}
И наконец тоже самое на груви:
beans {
coolDao(CoolDaoImpl)
coolService(CoolService){bean->
bean.scope = 'prototype'
bean.initMethod = 'init'
bean.destroyMethod = 'closeResources'
}
}
Если сравнивать затраченные усилия, то для данной конфигурации лидируют аннотации (их всего 6 штук, минус сеттер).
Но поскольку конфигурация полноценного приложения (а не отдельного модуля), включающее также сторонние библиотеки, не может держаться только на аннотациях, было бы более честно сравнивать между конфигурациями Groovy, Java и XML. Даже без очков видно, что конфигурация на груви лидирует по лаконичности и элегантности, однако мне хотелось бы оставить её на закуску. Тем более, что она пока ещё не допилена до конца.
А пока давайте обсудим недостатки и преимущества того, что было до выхода Spring 4.
Прочитав последнюю строчку, суровые критики уже наверняка сделали себе пометку, мол ага, автор статьи видать не в курсе про Factory Beans, SpEL и всякие хитрые профайлы… Но давайте будем честны, XML в данной таблице заработал и так немало плюсов, и на их фоне этот плюс будет смотреться притянутым за уши. SpEL довольно ограничен, а Factory Beans не являются частью XML-a. И положа руку на сердце, даже самые ярые поклонники XML-a вряд ли выбрали бы его как язык программирования.
Еще один вопрос, который мне часто задают на тренингах: «А почему не перекомпилировать при изменении конфигурации — это плюс? Кто рискнёт на продакшне менять имплементацию бинов не прогнав после этого тесты? А всякие мелочи типа имён, паролей и портов, можно просто держать в проперти файлах, содержимое которых так же можно менять ничего не перекомпилируя».
Тут будет уместно рассказать один случай из моей практики, которой можно озаглавить как-то так:
Как XML с JBoss-ом вечеринку спасли
Несколько лет назад я работал в одной IT компании, в которой, из соображений безопасности, программистам нельзя было подключаться к рабочей среде через удалённый доступ. И вот после очередной трудовой недели, мы пошли отдыхать. Сидим в пабе, время — начало третьего утра, настроение отличное, и вдруг, звонит наш саппорт. Приезжай, говорит, срочно. Продакшн падает.
Знакомая ситуация? Есть много причин не хотеть ехать на работу в пятницу ночью… На мои попытки уговорить его попробовать решить всё по телефону, он твердит, что он только сапорт и единственное, что он умеет делать, это набирать номер телефона того, кого надо вызвать. В итоге он всё-таки согласился прочитать мне эксепшн. Проблема оказалась тривиальная: один из веб-сервисов начал глючить, и бин, который к нему обращался, выкидывал эксэпшн из-за которого падало всё приложение. Видимо, автор бина не ожидал такой засады, и эксепшн не отлавливался. А поскольку раньше такого не случалось, то QA пропустили этот код в продакшн.
Причем я прекрасно знал, что до понедельника сделать ничего нельзя, так как веб сервис не наш, и разговаривать не с кем. Но и оставить все как есть было нельзя, на сервере было еще много других job'ов, которые могли бы прекрасно работать, если бы выкинуть проблематичный бин из контекста. Я не буду углубляться в детали, но уверен, что вы сами можете продумать похожий сценарий. Вопрос в том, как можно поменять конфигурацию по телефону, разговаривая с человеком, который ничего не понимает в коде.
На моё счастье этот бин был прописан в xml-e. Дальше состоялся следующий диалог:
«Зайди на комп продакшна, открой папку JBoss/server/default/deploy найди там файл idi.ear и открой его. Что значит, как? Ладно переименуй его в zip и открывай. Отлично, видишь там папка META-INF? В ней есть applicationContext.xml? Зайди в него, найди там SchedulerFactoryBean и чуть ниже есть datacarTrigger. Закомментируй эту строчку. Что значит, как? Неважно, сотри её. Отлично, теперь запиши, закрой, переименуй обратно в ear и включай сервер по-новому.»
Ну и всё, дальше счастливый конец. А теперь представьте, что этот бин был бы прописан в java config… Пришлось бы видимо на работу ехать.
Таких историй можно рассказать много, и возможность поменять конфигурацию, ничего не компилируя и не строя заново, часто является большим плюсом. Начиная от аспектов, которые надо то включать, то выключать, то перенастраивать, и, заканчивая delivery-oriented компаниями, которые кастомизируют один и тот же продукт для разных клиентов.
Такая архитектора изначально подразумевает, что xml будет лежать вне архива (war, ear), причём там, где до него легко добраться. И скорее всего, в компаниях, где используется такой подход существует целая экосистема, заточенная под такой динамизм, со специальными тестами и ролбэками.
С другой стороны, когда речь идёт о сложных конфигурациях, которые не могут быть сугубо декларативными, работать с XML становится крайне неудобно. Все костыли, придуманные для этого еще в первых версиях Спринга, делают конфигурацию очень запутанной, и её трудно поддерживать
.
Конфигурация на Java имеет в таких случаях большое преимущество. Все бины, настройки которых требуют определённой бизнес логики, намного правильнее прописывать в ней. Не говоря о том, что для джава разработчика намного приятнее писать Джаву, а не XML.
Еще одно небольшое, но все же преимущество конфигурации на Java — это производительность. Все мы знаем, еще со школы, что «рефлекшн тормозит». А бины, прописанные в XML-e или при помощи аннотаций, будут создаваться именно с его помощью. Если все они синглтоны, то это не так важно: просто немного увеличится время бутсрапа. Однако, если ваша программа постоянно создаёт прототайпы, Reflection не желателен. Ну а с конфигурацией на Java, рефлекшн просто не используется. Бины создаются обычным Java-кодом.
Преимущество аннотаций заключается в том, что они позволяют уменьшить конфигурацию. Кроме того, если каждый новый бин, вынуждает разработчика бегать в конфиг, и что-то там править или дописывать, то помимо неудобства и постоянных мёрджей, возникает вероятность, того, что кто-то случайно испортит чужое. Кроме того, как мы прекрасно знаем, если в первом акте на сцене висит ружьё…
Именно поэтому, далеко не в каждой компании архитекторы позволяют всем подряд ковыряться в конфиге и прописывать там свои бины, опасаясь криворуких программистов, которые не ведают, что творят.
Кто-то скажет, мол, а вот мы не пользуемся аннотациями вообще: да это не всегда удобно, зато все централизовано. Ну тут одно из двух, либо ваш проект очень маленький, и спринга в нём кот наплакал, либо вся эта «централизованность», не вносит ясности. Огромные XML-ы или Java-конфиги, разобраться в которых может только гроссмейстер по шахматам, в итоге приводят к ещё большей путанице.
Итоги
Все наши классы, которые должны являться бинами Спринга, объявляются и настраиваются аннотациями, красиво раскладываются по пэкэджам и сканируются, по указанию центральной конфигурации.
Все бины из сторонних библиотек, и то, что нуждается в сложной конфигурации определяется в Java config.
А та часть конфигурации, которая нуждается в динамизме выносится в XML, который, вдобавок можно еще и хранить, как внешний ресурс.
Зачем же нужно было придумывать конфигурацию на Groovy, когда и без нее уже хватало бардака? Груви, конечно сегодня в моде, но разве это причина добавлять ещё одну конфигурацию, увеличивая путаницу? Давайте разберемся в чем его суть?
Конфиг на Groovy
Идея Groovy-конфигурации заключается в том, чтобы заменить всё, кроме аннотаций. Она обладает всеми плюсами XML – ведь это скрипт, и его так же можно держать как внешний ресурс. Более того динамизму может быть еще больше, в потенциале, изменения конфигурации не будут требовать рестарта.
С другой стороны, она обладает и всеми плюсами Java-конфига, и более того, код на груви, лаконичней, элегантней и мощнее.
Правда, на данном этапе, его еще нельзя в полной мере использовать вместо Java-конфигурации, потому что отсутствует поддержка namespac'ов а ля @EnableAspectJAutoProxy, @EnableAsync и многое другое. Это, конечно, можно решить, настроив все нужные бины в конфигурации, но чтобы не заморачиваться, проще пока оставить Java-конфиг. А вот XML уже можно выкидывать :)
Чувствуя, как от последней фразы сжались кулаки у сторонников XML-а, я предлагаю устроить батл.
В синий угол ринга приглашаются все оптимисты, которые не боятся новых технологий, а наоборот пытаются их внедрять, чтобы двигаться вперед самим и двигать свою компанию. Они будут сражаться за максимальный динамизм, за возможность девелоперов делать больше и проще, за Agile-разработку и за технические инновации.
В красный угол ринга приглашаются наученные горьким опытом реалисты, которые знают, что произойдёт, когда у всех появится сила. Эти люди сегодня, ценят то, что конфигурация на джаве — Type Safe, а XML не даёт никому возможность наломать дров.
Ну а пока батл продолжается, я рискну высказать своё предположение, чем закончится война против XML-ов. Давайте ещё раз посмотрим на картинку и вспомним, старый фильм:
Шварценеггер, конечно, в итоге победил, но какой ценой…
Ну а если кто захочет продолжить баттл в живую, приходите на мой доклад: Spring the Ripper, кто хочет серьёзно поднять свои навыки — приходите на тренинг Spring for Seniors