Pull to refresh
121.62
JUG Ru Group
Конференции для Senior-разработчиков

Чем старше Spring, тем больше контекстов

Reading time7 min
Views54K
Уже много лет работая со спрингом, я заметил забавную закономерность: в каждой новой версии добавляется новый способ конфигурирования контекста. Давайте вспомним, как это было:
  • В первом спринге конфигурацию можно было писать исключительно на 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
Tags:
Hubs:
Total votes 42: ↑40 and ↓2+38
Comments53

Articles

Information

Website
jugru.org
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
Алексей Федоров