Приветствую всех любителей Spring!
Вчера выступил на встрече "Клуба анонимных разработчиков" по теме "Spring vs J2EE". Идея организовать эту встречу полностью оправдала себя. Впервые в истории клуба пришлось закрыть регистрацию преждевременно.
Я рассказывал о легковесной разработке на Spring с немного видоизмененным докладом. Было больше техники и меньше троллинга. Слайды доклада тут.
Моим оппонентом выступил Виктор Тесленко. Спасибо ему большое за рассказ. На мой взгляд получились очень интересные посиделки побольше бы таких. Видео скоро будет доступно тут.
Следите за анонсами.
2012-11-09
2012-11-05
Выступление в рамках "Клуба анонимных разработчиков"
Привет всем любителям Spring!
В этот четверг мы будем выступать на встрече "Клуба анонимных разработчиков". Тема встречи "Spring vs J2EE", а мы будем рассказывать про легковесную разработку на enterprise Java приложений на Spring.
В этот четверг мы будем выступать на встрече "Клуба анонимных разработчиков". Тема встречи "Spring vs J2EE", а мы будем рассказывать про легковесную разработку на enterprise Java приложений на Spring.
2012-10-24
Spring AOP или AspectJ: в чем же все таки разница?
Привет всем адептам Spring!
Продолжаем отвечать на популярные вопросы о фреймворке.
AOP используется Spring с начала существования. Но несмотря на это, мало кто может просто и правильно объяснить в чем разница между использованием Spring AOP и AspectJ, а также перечислить возможные варианты их использования. Попробуем разобраться вместе.
Начнем с определения понятий.
AOP - аспектно-ориентированное программирование, парадигма предназначенная для того, чтобы отделить, так называемую, cross-cutting функциональность от основного кода приложения, сгруппировав ее в одном месте. Основными составными частями AOP являются:
Далее, рассмотрим, что такое Spring AOP.
Это реализация AOP, специально разработанная для использования Spring. Она оперирует только только с method execution join points. В случае работы с интерфейсными типами, Spring AOP использует J2SE dynamic proxies, в противном случае с классами используется библиотека CGLIB. Документация тут.
Идем дальше - стили AOP.
@AspectJ это стиль объявления аспектов, как Java классов, с использованием аннотаций, разработанный проектом AspectJ. Spring поддерживает эти аннотации (объявление аспекта, декларация pointcut) и автоматически проксирует те бины, к которым будут применены аспекты. Прошу заметить что тут по прежнему используется ТОЛЬКО механизмы Spring AOP и не присутствуют зависимости от AspectJ compiler и weaver.
Spring имеет namespace для работы с AOP. Для начала работы (чтоб Spring сам искал в контексте компоненты, которые являются аспектами, то есть имеют @Aspect) требуется объявить:
Пример из нашего репозитория тут.
Документация тут.
Аналогичным образом можно использовать исключительно XML контекст для объявления и работы с аспектами. С использованием того же namespace, что и для @AspectJ style. Документауия ут.
Теперь немного больше про AspectJ.
AspectJ является имплементацией AOP для Java. Это самостоятельная технология не имеющая отношения к Spring кроме того, что фреймворк ее удачно использует. Как понятно из вышесказанного - можно использовать AOP на основе AspectJ без Spring. Она достаточно нетривиальна (как обычно Spring сделал ее для нас проще). В случае использования AspectJ вы можете либо использовать аннотации @AspectJ-style, либо язык AspectJ. Кто знаком со Spring Roo, тот обращал внимание, что там используются отдельные файлы ( .aj ) для хранения аспектов - это пример использования языка AspectJ.
Подробнее об AspectJ тут.
Теперь по выбору подхода в каждом конкретном случае.
Вы можете использовать Spring AOP как полностью XML конфигурацию, @AspectJ, с использованием языка AspectJ. Кроме того у вас есть возможность использовать непосредственно AspectJ (который тоже имеет варианты использования). В документации все очень неплохо описано. Рекомендую почитать тут. Если резюмировать для тех, кто не прошел по ссылке - чем проще - тем лучше. Если вам достаточно возможностей Spring AOP (ограничен poincut при вызове метода), используйте его. Причем dynamic proxies, конечно же, легковесней использования CGLIB. Аннотационный подход предпочтительней в любом из случаев.
Чего я не нашел в документации, так это подробного описание использования AspectJ ВМЕСТО Spring AOP. Документация ограничивается использованием @Configurable для расширения Spring контекста на бины ВНЕ контейнера. При этом используется Load Time Weaving (LTW). Кстати, пример использования этого подхода для интеграции Spring c Vaadin тут.
А вообще, тема интересная, так, что это только начало. Дальше - больше: использование @Cachable, Spring Data Graph, Spring for Andoid.
Всегда ваши me & eugene.
Продолжаем отвечать на популярные вопросы о фреймворке.
AOP используется Spring с начала существования. Но несмотря на это, мало кто может просто и правильно объяснить в чем разница между использованием Spring AOP и AspectJ, а также перечислить возможные варианты их использования. Попробуем разобраться вместе.
Начнем с определения понятий.
AOP - аспектно-ориентированное программирование, парадигма предназначенная для того, чтобы отделить, так называемую, cross-cutting функциональность от основного кода приложения, сгруппировав ее в одном месте. Основными составными частями AOP являются:
- Join point - место где мы вызываем наш специфический код.
- Pointcut - совокупность join point.
- Advice - собственно код нашей функциональности, который мы будем вызывать.
- Aspect - совокупность второго и третьего.
Далее, рассмотрим, что такое Spring AOP.
Это реализация AOP, специально разработанная для использования Spring. Она оперирует только только с method execution join points. В случае работы с интерфейсными типами, Spring AOP использует J2SE dynamic proxies, в противном случае с классами используется библиотека CGLIB. Документация тут.
Идем дальше - стили AOP.
@AspectJ это стиль объявления аспектов, как Java классов, с использованием аннотаций, разработанный проектом AspectJ. Spring поддерживает эти аннотации (объявление аспекта, декларация pointcut) и автоматически проксирует те бины, к которым будут применены аспекты. Прошу заметить что тут по прежнему используется ТОЛЬКО механизмы Spring AOP и не присутствуют зависимости от AspectJ compiler и weaver.
Spring имеет namespace для работы с AOP. Для начала работы (чтоб Spring сам искал в контексте компоненты, которые являются аспектами, то есть имеют @Aspect) требуется объявить:
Пример из нашего репозитория тут.
Документация тут.
Аналогичным образом можно использовать исключительно XML контекст для объявления и работы с аспектами. С использованием того же namespace, что и для @AspectJ style. Документауия ут.
Теперь немного больше про AspectJ.
AspectJ является имплементацией AOP для Java. Это самостоятельная технология не имеющая отношения к Spring кроме того, что фреймворк ее удачно использует. Как понятно из вышесказанного - можно использовать AOP на основе AspectJ без Spring. Она достаточно нетривиальна (как обычно Spring сделал ее для нас проще). В случае использования AspectJ вы можете либо использовать аннотации @AspectJ-style, либо язык AspectJ. Кто знаком со Spring Roo, тот обращал внимание, что там используются отдельные файлы ( .aj ) для хранения аспектов - это пример использования языка AspectJ.
Подробнее об AspectJ тут.
Теперь по выбору подхода в каждом конкретном случае.
Вы можете использовать Spring AOP как полностью XML конфигурацию, @AspectJ, с использованием языка AspectJ. Кроме того у вас есть возможность использовать непосредственно AspectJ (который тоже имеет варианты использования). В документации все очень неплохо описано. Рекомендую почитать тут. Если резюмировать для тех, кто не прошел по ссылке - чем проще - тем лучше. Если вам достаточно возможностей Spring AOP (ограничен poincut при вызове метода), используйте его. Причем dynamic proxies, конечно же, легковесней использования CGLIB. Аннотационный подход предпочтительней в любом из случаев.
Чего я не нашел в документации, так это подробного описание использования AspectJ ВМЕСТО Spring AOP. Документация ограничивается использованием @Configurable для расширения Spring контекста на бины ВНЕ контейнера. При этом используется Load Time Weaving (LTW). Кстати, пример использования этого подхода для интеграции Spring c Vaadin тут.
А вообще, тема интересная, так, что это только начало. Дальше - больше: использование @Cachable, Spring Data Graph, Spring for Andoid.
Всегда ваши me & eugene.
2012-10-13
Списки полезных ссылок
Мы установили голосовалку, чтобы понять интересен ли, был бы читателям сборник полезных ссылок и с какой периодичностью его лучше выпускать.
2012-10-06
Spring MVC REST + Spring Android
Приветствую читателей!
В дополнение к нашей презентации о новинках Spring MVC 3.1 мы решили добавить в наш репозиторий приложение на Spring Android в качестве клиента для REST сервиса, который мы показывали. Большое спасибо Толику Каверину за помощь в разработке клиента.
В дополнение к нашей презентации о новинках Spring MVC 3.1 мы решили добавить в наш репозиторий приложение на Spring Android в качестве клиента для REST сервиса, который мы показывали. Большое спасибо Толику Каверину за помощь в разработке клиента.
2012-10-05
Spring Security: Защити свое приложение
Приветствую всех любителей Spring :)
Сразу прошу простить за долгий перерыв. Причиной тому и наши гастроли во Львовe и подготовка выступления в Киеве, а также куча всякой мелочи в виде необходимости зарабатывать на жизнь :) Есть интересный проекты - напишите мне.
В данной статье я хотел бы показать пример настройки Spring Security для веб проекта. Делать то, чего 100500 раз понаписано в интернетах я не хотел, моя цель была сделать что-то полезное и информативное. Поэтому я попробовал сделать пример не таким тривиальным как в интернетах, кроме того, код как обычно в нашем проекте на GitHub.
Ну как обычно это работает примерно как магия раз-два-три:
Раз. Фильтры web.xml:
Для ограничение доступа к своим страницам SS использует servlet filter (рекомендую почитать как это работает, если кто запамятовал, чтоб понимать что происходит дальше. А вообще вот и реализация Chain of Responsibility, которую так часто любят спрашивать на собесах).
Два. Контекст. Я предлагаю создать отдельный контекст (и включить его потом в application-context). В этом же контексте лучше всего использовать security namespace:
На первой части останавливаться не буду. Так я подключаю нужные мне компоненты и отфильтровываю ненужные.
Начнем в тега http. Документация говорит что минимальная необходимая функциональность это использование атрибута auto-config, который является шорткатом (см. документацию). В данном случае я прошу SS сгенерировать мне форму логина и форму логаута, а также описываю поведение при попадании на определенный url pattern (как работает описано тут).
Для формы существует масса дополнительных атрибутов с которыми я рекомендую ознакомиться.
Наличие же тега authentication-manager говорит о том как я буду осуществлять аутентификацию. В моем случае я делегирую это userService, который я имплементировал как компонент. Также я использую хеширование пароля с использованием sha, а также salt в виде username (в моем случае как мы увидим из UserService это email). Пользователей с их правами доступа можно хранить прямо в контексте, как показано в большинстве тривиальных примеров.
Для упрощения я мог бы использовать стандартную схему БД и вместо моего сервиса передавать Data Source этой базы.
Детали: Кастомная форма логина/логаута
Для использование своей собственной формы, вместо сгенерированной, вам необходимо указать ее в теге login-form. Кроме того вам придется использовать именование полей формы по умолчанию (как тут). Аналогично с формой logout. По умолчанию она logout осуществляется по адресу /j_spring-security_logout
Детали: Роли пользователей
В теге intercept-url я могу указать роль пользователя. Таким образом я могу сделать иерархическую структуру безопасности.
Детали: Интеграция с LDAP
Этому посвящена целая глава мануала.
Три (как видно из прошлого пункта шаг не обязательный) Имплементация UserDetailsService:
Это тянет за собой создание entity, ее добавления к persistence entities, создание Dao (да простят мне читатели использование олдскульного нетипизированного criteria API, обещаю поправить на Hibernate 4 style), а также создание адаптера, чтоб возвращать объект имплементирующий UserDetails.
Детали: Создание пользователей
При хранении пароль в виде хеша не задудьте что вам необходимо после регистрации захешировать пароль с сохранить его в базе.
Про добавление зависимостей в maven писать не буду. Кому надо тот посмотрит в коде проекта, а кто не использует maven тому будет неинтересно.
Из того, что не добавил, но стоит сделать это Service Layer Security. Мощный механизм и простой как раз-два-три.
Рекомендованная литература: Spring Security documentation, Spring Security Tutorial
Дальше опять про AOP, Spring Shell, @Cachable, Spring Data для Neo4j и еще немного про Conversion & Formatting Service.
Всего всего - ваши me & eugene. Stay tuned.
Сразу прошу простить за долгий перерыв. Причиной тому и наши гастроли во Львовe и подготовка выступления в Киеве, а также куча всякой мелочи в виде необходимости зарабатывать на жизнь :) Есть интересный проекты - напишите мне.
В данной статье я хотел бы показать пример настройки Spring Security для веб проекта. Делать то, чего 100500 раз понаписано в интернетах я не хотел, моя цель была сделать что-то полезное и информативное. Поэтому я попробовал сделать пример не таким тривиальным как в интернетах, кроме того, код как обычно в нашем проекте на GitHub.
Ну как обычно это работает примерно как магия раз-два-три:
Раз. Фильтры web.xml:
Для ограничение доступа к своим страницам SS использует servlet filter (рекомендую почитать как это работает, если кто запамятовал, чтоб понимать что происходит дальше. А вообще вот и реализация Chain of Responsibility, которую так часто любят спрашивать на собесах).
Два. Контекст. Я предлагаю создать отдельный контекст (и включить его потом в application-context). В этом же контексте лучше всего использовать security namespace:
На первой части останавливаться не буду. Так я подключаю нужные мне компоненты и отфильтровываю ненужные.
Начнем в тега http. Документация говорит что минимальная необходимая функциональность это использование атрибута auto-config, который является шорткатом (см. документацию). В данном случае я прошу SS сгенерировать мне форму логина и форму логаута, а также описываю поведение при попадании на определенный url pattern (как работает описано тут).
Для формы существует масса дополнительных атрибутов с которыми я рекомендую ознакомиться.
Наличие же тега authentication-manager говорит о том как я буду осуществлять аутентификацию. В моем случае я делегирую это userService, который я имплементировал как компонент. Также я использую хеширование пароля с использованием sha, а также salt в виде username (в моем случае как мы увидим из UserService это email). Пользователей с их правами доступа можно хранить прямо в контексте, как показано в большинстве тривиальных примеров.
Для упрощения я мог бы использовать стандартную схему БД и вместо моего сервиса передавать Data Source этой базы.
Детали: Кастомная форма логина/логаута
Для использование своей собственной формы, вместо сгенерированной, вам необходимо указать ее в теге login-form. Кроме того вам придется использовать именование полей формы по умолчанию (как тут). Аналогично с формой logout. По умолчанию она logout осуществляется по адресу /j_spring-security_logout
Детали: Роли пользователей
В теге intercept-url я могу указать роль пользователя. Таким образом я могу сделать иерархическую структуру безопасности.
Детали: Интеграция с LDAP
Этому посвящена целая глава мануала.
Три (как видно из прошлого пункта шаг не обязательный) Имплементация UserDetailsService:
Это тянет за собой создание entity, ее добавления к persistence entities, создание Dao (да простят мне читатели использование олдскульного нетипизированного criteria API, обещаю поправить на Hibernate 4 style), а также создание адаптера, чтоб возвращать объект имплементирующий UserDetails.
Детали: Создание пользователей
При хранении пароль в виде хеша не задудьте что вам необходимо после регистрации захешировать пароль с сохранить его в базе.
Про добавление зависимостей в maven писать не буду. Кому надо тот посмотрит в коде проекта, а кто не использует maven тому будет неинтересно.
Из того, что не добавил, но стоит сделать это Service Layer Security. Мощный механизм и простой как раз-два-три.
Рекомендованная литература: Spring Security documentation, Spring Security Tutorial
Дальше опять про AOP, Spring Shell, @Cachable, Spring Data для Neo4j и еще немного про Conversion & Formatting Service.
Всего всего - ваши me & eugene. Stay tuned.
2012-10-04
Выступление в Киеве
Приветствую читателей!
В конце месяца мы с Женей хотели бы повторить наш опыт и провести воркшоп Spring By Example в Киеве. Сейчас идет точное уточнение дат и принимающей компании. Если у Вас есть какие-либо пожелания/предложения - напишите мне.
В конце месяца мы с Женей хотели бы повторить наш опыт и провести воркшоп Spring By Example в Киеве. Сейчас идет точное уточнение дат и принимающей компании. Если у Вас есть какие-либо пожелания/предложения - напишите мне.
2012-09-11
Воркшоп во Львове (08.09.2012)


Если Вы по какой-то причине не смогли попасть к нам на выступление - напишите нам и мы постараемся что-нибудь придумать. Если Вы бы хотели увидеть нас в своем городе/компании - опять же напишите нам.
Ваши me & eugene.
2012-09-10
Выступление на JUG Lviv (07.09.2012)
Но в целом отзывы позитивные. Так что приглашайте еще :)
1. BDD on Java using Concordion and Selenium slides (slideshare)
BDD on Java code (github)
2. Lightweight J2EE development with Spring slides
@Configuration examples
MVC new features
Vaadin integration
2012-08-31
Едем во Львов
SpringByExample.com.ua едет во Львов. 7-го сентября я выступлю на встрече JUG Lviv, а 8-го me & eugene проведем воркшоп. Регистрация на воркшоп тут. Спасибо Lohika за сотрудничество.
2012-08-29
Использование Spring task scheduler
Не так давно в одном из проектов стояла задача регулярного опроса базы данных и обработки записей полученных оттуда по мере их возникновения. Эту задачу мы решали с помощью Spring Task Scheduling. Начнем с конфигурации контекста:
Spring предоставляет удобный namespace task, который содержит все необходимое для создания Executor/Scheduler бинов. В моей конфигурации максимальное кол-во потоков, проводящих опрос базы данных - пять. <task:annotation-driven> элемент позволяет использовать аннотировнные компоненты для запуска задач, а также указывает executor/scheduler которые будут использоваться по умолчанию для запуска аннотированных методов.
Таким образом, чтобы навести порядок в логах и очистить совесть, решили исправить ситуацию. Для того, чтобы корректно разрушить нашу компоненту с задачами, нам необходимо вклиниться в процесс разрушения контекста и закрыть выполняемые задачи до того, как разрушатся остальные бины. Для решения этой проблемы Spring предоставляет специальный интерфейс ApplicationListener, позволяющий реагировать на различные сообщения, связанные с контекстом.
Мы включили в нашу компоненту две новых зависимости (databasePollExecutor, databasePollScheduler), которые дали нам доступ к executor/scheduler, обьявленных в XML контексте. Также мы добавили в нее реализацию интерфейса, которая уничтожает executor/scheduler и связанные с ними задачи. Метод onApplicationEvent будет вызван Spring-ом до начала разрушения остальных бинов и контекста в целом, а это именно то что нам нужно.
Spring предоставляет удобный namespace task, который содержит все необходимое для создания Executor/Scheduler бинов. В моей конфигурации максимальное кол-во потоков, проводящих опрос базы данных - пять. <task:annotation-driven> элемент позволяет использовать аннотировнные компоненты для запуска задач, а также указывает executor/scheduler которые будут использоваться по умолчанию для запуска аннотированных методов.
Как видно из примера выше, для создания задачи достаточно аннотировать необходимый метод аннотацией @Scheduled, которой необходимо указать один из трех параметров:
1. fixedDelay - период между завершением предыдущего запуска и началом следущего запуска задачи в миллисекундах
2. fixedRate - период между каждым запуском задачи в миллисекундах (считается от старта предыдущей задачи)
3. cron - указывает cron подобное выражение, в котором можно более точно указать периоды запуска задачи
Хочу обратить внимание, что fixedDelay следует использовать в тех случаях, когда время выполнения задачи может превышать период между выполнениями задач. Для остальных случаев вполне подойдет fixedRate.
После старта контекста Spring автоматически настроит необходимые Executor бины и будет выполнять метод pollDatabase раз в секунду (ну или чуть реже, учитывая время на выполнение самого метода).н
Правда, во время эксплуатации этого кода, возникла одна неприятная проблема, которая в общем-то не влияла на работоспособность системы, но оставляла мусор в логах. Дело в том, что выключая контейнер (Tomcat в нашем случае), Spring закрывает контекст и разрушает все бины, находящиеся в нем. При этом выполняемые запланированные задачи иногда разрушались после того как уже были разрушены сервисные бины; задачи пытались использовать разрушенные сервисные бины, вызывая различные странные исключения.
Правда, во время эксплуатации этого кода, возникла одна неприятная проблема, которая в общем-то не влияла на работоспособность системы, но оставляла мусор в логах. Дело в том, что выключая контейнер (Tomcat в нашем случае), Spring закрывает контекст и разрушает все бины, находящиеся в нем. При этом выполняемые запланированные задачи иногда разрушались после того как уже были разрушены сервисные бины; задачи пытались использовать разрушенные сервисные бины, вызывая различные странные исключения.
Таким образом, чтобы навести порядок в логах и очистить совесть, решили исправить ситуацию. Для того, чтобы корректно разрушить нашу компоненту с задачами, нам необходимо вклиниться в процесс разрушения контекста и закрыть выполняемые задачи до того, как разрушатся остальные бины. Для решения этой проблемы Spring предоставляет специальный интерфейс ApplicationListener, позволяющий реагировать на различные сообщения, связанные с контекстом.
Мы включили в нашу компоненту две новых зависимости (databasePollExecutor, databasePollScheduler), которые дали нам доступ к executor/scheduler, обьявленных в XML контексте. Также мы добавили в нее реализацию интерфейса, которая уничтожает executor/scheduler и связанные с ними задачи. Метод onApplicationEvent будет вызван Spring-ом до начала разрушения остальных бинов и контекста в целом, а это именно то что нам нужно.
2012-08-07
FAQ: Использование @Transactional аннотации
В этой статье я бы хотел рассказать о проблемах, которые часто возникают при использовании транзакций в Spring, а в частности, вопросы связанные с использованием аннотации @Transactional.
Начнем с общих вопросов.
Стоит ли использовать эту аннотацию на интерфейсах или на конкретных классах?
Простой ответ - используйте их на конкретных классах. С точки зрения построения сервисов, управление транзакциями - это детали реализации. В очень редких случаях имеет смысл делать транзакции частью контракта, если есть какие-то явные требования к наследованию транзакционных методов (transaction propagation), обьявленых в интерфейсе.
Кроме того, работоспособность аннотаций на интерфейсе не всегда будет корректной. Если стратегия проксирования классов будет выбрана не верно (например, proxy-target-class='true'), то @Transactional на интерфейсе вообще будут игнорироваться. Детальнее об этом можно прочитать в документации Spring.
Будет ли работать @Transactional на приватных методах класса?
Для начала, следует понять каким образом аннотации на методах порождают транзакции. Spring использует AOP для пре/постобработки аннотированных методов. Таким образом все зависит от имплементации AOP и способа вызова метода.
Если используется конфигурация по умолчанию, то класс будет обернут в прокси, и в этом случае только внешние вызовы методов будут транзакционными. А поскольку вызов приватных методов является внутренним для каждого конкретного бина, то @Transactional на нем будет игнорироваться. Аналогичная ситуация будет происходить даже в том случае если метод публичный - вызов такого метода из этого же класса никогда не начнет транзакцию.
Если же бин использует AspectJ AOP, ситуация чуть лучше. В этом случае класс будет "обработан" байткодом, который обеспечит корректный вызов транзакционных вставок на любых методах, не зависимо от области видимости и места вызова.
Почему использовать @Transactional лучше на сервисных классах?
В первую очередь DAO бины, предоставляют примитивные операции над определенным доменом, тем не мнее являются плохими кандидатами для аннотации @Transactional. Конечно, такие транзакции будут очень короткими, это несомненный плюс. Но при этом теряется возможность объеденять несколько мелких операций в один блок, и в случае если, одна из мелких операций даст сбой (rollback), то остальные операции останутся закоммичеными, так как, проходили в отдельных транзакциях. Именно для этого и нужны сервисные бины - обьеденять логически совместимые операции в монолитные блоки. А для того чтобы явно подчеркнуть, что методы DAO бина должны выполнятся в транзакциях обьявленных во внешних сервисах, можно на DAO бин поставить аннотацию @Transactional(propagation = MANDATORY). Такие вспомогательные аннотации помогут быстрее находить ошибки, связанные с пропущенными аннотациями на сервисном уровне.
Стоит ли делать контроллеры транзакционными?
Если ваше приложение маленькое и сервисный слой вырождается в однострочные перевызовы DAO, то возможно, в этом случае и стоит ставить @Transactional на контроллерах. Ну а вобщем случае и в продолжение предыдущего вопроса хотел бы сказать, что мешать MVC и сервисный слой не очень красиво. Кроме того транзакционные контроллеры потребуют дополнительной конфигурации DispatcherServlet контекста и включения в него менеджера транзакций.
Как @Transactional работает в JUnit тестах?
Если Вы используете интеграционные тесты, то @Transactional аннотация обязательно Вам пригодится.
@Transactional аннотацию можно использовать как на уровне класса, так и на отдельных тест методах. В первом случае для каждого тест метода будет создаваться отдельная транзакция, если необходимо отключить использование транзакций для определенных тест методов, можно их пометить аннотацией @NonTransactional.
По умолчанию любой транзакионный тест всегда откатывается после завершения. Это поведение можно поменять либо с помощью вспомогательной аннотации @Rollback(false) применяемой на тесте, либо с помощью аннотации @TransactionConfiguration(defaultRollback=false), которая применяется на JUnit классе.
Следует отметить, что методы аннотированные JUnit аннотациями @Before/@After будут также запускаться в транзакции. Потому, если есть необходимость сделать какие-то подготовительные работы вне транзакции, их следует вынести в отдельные методы и пометить специальными аннотациями @BeforeTransaction/@AfterTransaction. Правда, не забудьте, что такие методы не будут запускаться вообще для нетранзакционных тест методов.
Почему некоторые обьекты остаются в базе после выполнения @Transactional Unit теста?
Самая частая причина - это использование @Transactional(propagation = Propagation.REQUIRES_NEW) в тестируемых классах. Эта аннотация указывает менеджеру транзакций открыть новую транзакцию при входе в метод, вне зависимости от того, существует уже открытая внешняя транзакция или нет. Поэтому все действия выполненные в таком методе не будут откатаны после завершеня тест метода.
Начнем с общих вопросов.
Стоит ли использовать эту аннотацию на интерфейсах или на конкретных классах?
Простой ответ - используйте их на конкретных классах. С точки зрения построения сервисов, управление транзакциями - это детали реализации. В очень редких случаях имеет смысл делать транзакции частью контракта, если есть какие-то явные требования к наследованию транзакционных методов (transaction propagation), обьявленых в интерфейсе.
Кроме того, работоспособность аннотаций на интерфейсе не всегда будет корректной. Если стратегия проксирования классов будет выбрана не верно (например, proxy-target-class='true'), то @Transactional на интерфейсе вообще будут игнорироваться. Детальнее об этом можно прочитать в документации Spring.
Будет ли работать @Transactional на приватных методах класса?
Для начала, следует понять каким образом аннотации на методах порождают транзакции. Spring использует AOP для пре/постобработки аннотированных методов. Таким образом все зависит от имплементации AOP и способа вызова метода.
Если используется конфигурация по умолчанию, то класс будет обернут в прокси, и в этом случае только внешние вызовы методов будут транзакционными. А поскольку вызов приватных методов является внутренним для каждого конкретного бина, то @Transactional на нем будет игнорироваться. Аналогичная ситуация будет происходить даже в том случае если метод публичный - вызов такого метода из этого же класса никогда не начнет транзакцию.
Если же бин использует AspectJ AOP, ситуация чуть лучше. В этом случае класс будет "обработан" байткодом, который обеспечит корректный вызов транзакционных вставок на любых методах, не зависимо от области видимости и места вызова.
Почему использовать @Transactional лучше на сервисных классах?
В первую очередь DAO бины, предоставляют примитивные операции над определенным доменом, тем не мнее являются плохими кандидатами для аннотации @Transactional. Конечно, такие транзакции будут очень короткими, это несомненный плюс. Но при этом теряется возможность объеденять несколько мелких операций в один блок, и в случае если, одна из мелких операций даст сбой (rollback), то остальные операции останутся закоммичеными, так как, проходили в отдельных транзакциях. Именно для этого и нужны сервисные бины - обьеденять логически совместимые операции в монолитные блоки. А для того чтобы явно подчеркнуть, что методы DAO бина должны выполнятся в транзакциях обьявленных во внешних сервисах, можно на DAO бин поставить аннотацию @Transactional(propagation = MANDATORY). Такие вспомогательные аннотации помогут быстрее находить ошибки, связанные с пропущенными аннотациями на сервисном уровне.
Стоит ли делать контроллеры транзакционными?
Если ваше приложение маленькое и сервисный слой вырождается в однострочные перевызовы DAO, то возможно, в этом случае и стоит ставить @Transactional на контроллерах. Ну а вобщем случае и в продолжение предыдущего вопроса хотел бы сказать, что мешать MVC и сервисный слой не очень красиво. Кроме того транзакционные контроллеры потребуют дополнительной конфигурации DispatcherServlet контекста и включения в него менеджера транзакций.
Как @Transactional работает в JUnit тестах?
Если Вы используете интеграционные тесты, то @Transactional аннотация обязательно Вам пригодится.
@Transactional аннотацию можно использовать как на уровне класса, так и на отдельных тест методах. В первом случае для каждого тест метода будет создаваться отдельная транзакция, если необходимо отключить использование транзакций для определенных тест методов, можно их пометить аннотацией @NonTransactional.
По умолчанию любой транзакионный тест всегда откатывается после завершения. Это поведение можно поменять либо с помощью вспомогательной аннотации @Rollback(false) применяемой на тесте, либо с помощью аннотации @TransactionConfiguration(defaultRollback=false), которая применяется на JUnit классе.
Следует отметить, что методы аннотированные JUnit аннотациями @Before/@After будут также запускаться в транзакции. Потому, если есть необходимость сделать какие-то подготовительные работы вне транзакции, их следует вынести в отдельные методы и пометить специальными аннотациями @BeforeTransaction/@AfterTransaction. Правда, не забудьте, что такие методы не будут запускаться вообще для нетранзакционных тест методов.
Почему некоторые обьекты остаются в базе после выполнения @Transactional Unit теста?
Самая частая причина - это использование @Transactional(propagation = Propagation.REQUIRES_NEW) в тестируемых классах. Эта аннотация указывает менеджеру транзакций открыть новую транзакцию при входе в метод, вне зависимости от того, существует уже открытая внешняя транзакция или нет. Поэтому все действия выполненные в таком методе не будут откатаны после завершеня тест метода.
2012-08-06
Использование ConversionService в Spring MVC
Привет, всем кто нас читает! Мы продолжаем печатать гостевые статьи. Сегодня у нас опять статья Олега Артемова - нашего давнего коллеги и эксперта по Spring.
Добрый день всем ! Данная статья посвящена концепции ConversionService, которая была успешно применена в моем текущем проекте. Итак, преамбула:
Как мы видим, это обычная команда, которых в любом Spring – проекте миллионы. С помощью Spring ConversionService мы можем избавиться от примитивных типов в команде и использовать доменную модель. Итак, как это делается:
В документации описан способ немного проще. Моей целью являлось, чтобы конвертеры сами регистрировались в контексте. Итак, рассмотрим подробно:
- ConvertersHolder – содержит в себе все конвертеры(GenericConverter, Converter, etc…). ExtendedConversionServiceImpl – более расширенная версия ConversionService, которая умеет конвертировать коллекции из одной в другую.
- HibernateDaoGenericConverter – generic конвертер. Одним этим конвертером мы можем конвертировать строки в объекты доменной модели.
- StringToEntityConverter – интерфейс, с помощью которого строка конвертируется в доменный объект
- AutoDetectFormattingServiceFactoryBean – бин, который регистрирует конвертеры.
Я привел имплементацию метода convert, которая сделана в базовом классе DAO. На самом деле она может быть абсолютно любая. Теперь наша команда выглядит иначе(поля все равно должны совпадать с названиями параметров из реквеста).
Как и прежде, её можно валидировать (см. предыдущие статьи) и решать, что же будет в случае ошибок. Из минусов:
- Данная концепция может быть применена и для конвертации параметров метода контроллера, но стандартные аннотации валидации на них не действуют, поэтому приходится использовать везде команды.
2012-07-30
И опять про тестирование SpringMVC: Новая надежда
В одной из прошлых статей мы реализовали тест для тестирования @RequestMapping аннотаций контроллера. Тест получился легковесный и всем нас устраивал. В процессе тестирования нашли небольшой баг. Он был починен буквально сразу, что меня порадовало. Кроме того я получил очень интересный совет от Rossen Stoyanchev - уделить внимание проекту spring-test-mvc.
Далее предлагаю мой модифицированный тест:
Контекст для теста:
Интересный инструмент в копилку разработчика.
Переписка с автором о pro & cons тут.
Дальше еще про SpringMVC (PropertyEditors vs ConversionServices), наконец таки будет SpringSecurity. Кроме того пишите о чем было бы интересно почитать вам!
Всего всего - ваши me & eugene.
Далее предлагаю мой модифицированный тест:
Контекст для теста:
Интересный инструмент в копилку разработчика.
Переписка с автором о pro & cons тут.
Дальше еще про SpringMVC (PropertyEditors vs ConversionServices), наконец таки будет SpringSecurity. Кроме того пишите о чем было бы интересно почитать вам!
Всего всего - ваши me & eugene.
2012-07-26
Использование SpringData JPA или прощай GenericDao!
Доброго утра всем! Это для кого оно доброе конечно, а всем остальным просто привет.
На нашем воркшопе в Одессе было много общения и обмена опытом. Дмитрий задал интересный вопрос: "А почему вы до сих пор используете GenericDao в примере, это же Spring". Звучало примерно как: "Вы все еще кипятите? Тогда мы идем в вам!".
Ну а теперь за дело. Все мы привыкли начинать написание Persistence слоя с Dao, а более конкретно с объектов GenericDao. Например таких:
Набор методов может быть различным все зависит от нужд проекта и стиля/вкуса разработчиков. Затем следует имплементация для Hibernate/JPA/JDBC/и.т.п.:
Далее интерфейс для сущности и его имплементация:
Фух. Много букаф. Я думаю что все копировали эти артефакты из проекта в проект с небольшими изменениями. Но это день вчерашний, теперь же давайте поговорим о дне сегодняшнем. Для того, что использовать SpringData для построение JPA репозиториев мы берем для начала документацию, а затем читаем Oliver Gierke.
Ну что ж к делу. Все просто как раз (pom файл):
Два (интерфейс):
Три (контекст):
Кстати XML использовать совсем не обязательно, для любителей @Configuration в SpringData есть полная поддержка plain-Java подхода к конфигурации.
Дело за малым, а именно написать собственно сервис:
Далее пишем тест (код уже приводить не буду) и видим что мы полетели. Какие на мой взгляд за и против:
Код, как обычно вы можете найти на нашем GitHub
Дальше Spring Security, SpringData + NoSQL, @Cachable, Gradle.
Всего всего - ваши me & eugene
На нашем воркшопе в Одессе было много общения и обмена опытом. Дмитрий задал интересный вопрос: "А почему вы до сих пор используете GenericDao в примере, это же Spring". Звучало примерно как: "Вы все еще кипятите? Тогда мы идем в вам!".
Ну а теперь за дело. Все мы привыкли начинать написание Persistence слоя с Dao, а более конкретно с объектов GenericDao. Например таких:
Набор методов может быть различным все зависит от нужд проекта и стиля/вкуса разработчиков. Затем следует имплементация для Hibernate/JPA/JDBC/и.т.п.:
Далее интерфейс для сущности и его имплементация:
Фух. Много букаф. Я думаю что все копировали эти артефакты из проекта в проект с небольшими изменениями. Но это день вчерашний, теперь же давайте поговорим о дне сегодняшнем. Для того, что использовать SpringData для построение JPA репозиториев мы берем для начала документацию, а затем читаем Oliver Gierke.
Ну что ж к делу. Все просто как раз (pom файл):
Два (интерфейс):
Три (контекст):
Кстати XML использовать совсем не обязательно, для любителей @Configuration в SpringData есть полная поддержка plain-Java подхода к конфигурации.
Дело за малым, а именно написать собственно сервис:
Далее пишем тест (код уже приводить не буду) и видим что мы полетели. Какие на мой взгляд за и против:
- меньше писанины это хорошо
- возможность генерации методов по названию класса, а именно не надо упражнений с HQL или Criteria - это тоже хорошо
- дополнительные зависимости это не очень хорошо
- не очень привычный по сравнению с EMF/SF интерфейс это тоже не очень
- ну и магия, она всегда опасна
Код, как обычно вы можете найти на нашем GitHub
Дальше Spring Security, SpringData + NoSQL, @Cachable, Gradle.
Всего всего - ваши me & eugene
2012-07-25
Тестирование мапингов в SpringMVC
После нашего воркшопа в Одессе мы получили ряд интересных вопросов. Об одном из которых, а точнее о его решение я бы хотел рассказать.
Задача: протестировать @RequestMapping для контроллера
Документация говорит нам (а она как я люблю говорить у Спринга отличная):
"Prior to Spring 3.1, type and method-level request mappings were examined in two separate stages -- a controller was selected first by the DefaultAnnotationHandlerMapping and the actual method to invoke was narrowed down second by the AnnotationMethodHandlerAdapter.
With the new support classes in Spring 3.1, the RequestMappingHandlerMapping is the only place where a decision is made about which method should process the request. Think of controller methods as a collection of unique endpoints with mappings for each method derived from type and method-level @RequestMapping information. "
Правда нет конкретной рекомендации. Зато Oliver Girke на StackOverflow рекомендовал.
Итого имеем:
Поднятие контекста в тесте не требуется. Mockito в свою очередь порадовал вкусняшками типа @Mock. Я думаю что все заметили хак "request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, true);". Промучившись минут 15, а также немного подебажив (как говорит Женя дебажить Спринг одно удовольствие) я засабмитил багу и с чистой совестью поставил костыль.
В следующей серии о Spring Data и GenericDao.
Всего всего - ваши me & eugene
Задача: протестировать @RequestMapping для контроллера
Документация говорит нам (а она как я люблю говорить у Спринга отличная):
"Prior to Spring 3.1, type and method-level request mappings were examined in two separate stages -- a controller was selected first by the DefaultAnnotationHandlerMapping and the actual method to invoke was narrowed down second by the AnnotationMethodHandlerAdapter.
With the new support classes in Spring 3.1, the RequestMappingHandlerMapping is the only place where a decision is made about which method should process the request. Think of controller methods as a collection of unique endpoints with mappings for each method derived from type and method-level @RequestMapping information. "
Правда нет конкретной рекомендации. Зато Oliver Girke на StackOverflow рекомендовал.
Итого имеем:
Поднятие контекста в тесте не требуется. Mockito в свою очередь порадовал вкусняшками типа @Mock. Я думаю что все заметили хак "request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, true);". Промучившись минут 15, а также немного подебажив (как говорит Женя дебажить Спринг одно удовольствие) я засабмитил багу и с чистой совестью поставил костыль.
В следующей серии о Spring Data и GenericDao.
Всего всего - ваши me & eugene
2012-07-23
Еще немного о валидации
В предыдущей статье мы показали, каким образом можно организовать валидацию данных, поступающих на наши контроллеры. Сегодня я бы хотел рассказать про дополнительные возможности, которые предоставляет Spring. В часности, в этой стетье пойдет речь о JSR-303 (Валидация Бинов) и Hibernate Validation. Также я расскажу о новшествах, которые были введены в Spring MVC 3.1 для более удобной валидации данных.
Итак, построим наши примеры на предыдущей бизнес задаче: создание скидочных купонов. На вход наш контроллер получает данные о купонах, которые нужно сгенерить (DiscountCouponGenerationCommand), в качестве результата возвращает список сгенерированных купонов (CouponJson) либо список ошибок валидации.
Поля нашей команды помечены специальными аннотациями, которые называются Constraint. В примере я использовал несколько стандартных аннотаций @Min, @Digits. Также я использовал свои собвстенные Constraint аннотации: @CouponCode для валидации поля couponCode, @DiscountCoupon для валидации всего бина DiscountCouponGenerationCommand.
Как же работают Constraint аннотации? Давайте заглянем внутрь одной их них:
Прежде всего, обратите внимание на аннотацию @Constraint, которая указывает на то, что @CouponCode является Constraint аннотацией, также она содержит в себе имя валидатора, который будет использоваться для валидации поля, помеченного как @CouponCode. В нашем случае валидирующий класс - это CouponCodeValidator. Из других частей аннотации также следует обратить внимание на параметр message, который содержит сообщение-ошибку, которое будет передано на клиент, в случае если валидация поля прошла не успешно. Также аннотация @Target содержит список элементов Java класса, перед которыми мы сможем использовать @CouponCode.
Следует сказать, что текст ошибок валидации можно указывать явно в классе, либо использовать property файл, что особенно удобно, если необходима локализация сообщений. В этом случае вместо сообщения в параметре message нужно указать ключ окруженный фигурными скобками, а само сообщение поместить в файл ValidationMessages.properties, который обязательно должен находиться в корне classpath:
Рассмотрим теперь сам валидатор CouponCodeValidator.
Как видно из примера, валидатор имплементирует generic интерфейс. Первый generic параметр - это constraint который обрабатывает валидатор, второй - тип поля, на котором предположительно будет использоваться constraint. Итак, наш валидатор проверяет, что поле анотированное @CouponCode не пустое, имеет длину 10 символов и начинается с символов 'CP'.
Теперь перейдем собственно к конроллеру, который будет заниматься обработкой команды, валидацией и генерацией купонов.
Мы помечаем параметр DiscountCouponGenerationCommand аннотацией @Valid, которая и включит механизм валидации перед тем, как будет запущен метод generateDiscountCoupons. Всю работу за нас сделает Spring MVC:
В последних версиях Spring MVC ввели еще одну возможность обработки ошибок валидации - с помощью обработчиков исключений.
Метод аннотированный @ExceptionHandler аннотацией будет автоматически использоваться для обработки ошибок валидации, если таковые возникнут. Параметр MethodArgumentNotValidException содержит всю необходимую нам информацию об ошибках и месте где они возникли.
Конфигурация Spring контекста для JSR-303 не требует ничего особенного, она содержит 2 строки, которые инициализируют поиск компонент и Spring MVC:
В качестве имплементации JSR-303 я использовал Hibernate Validator, его необходимо поместить в classpath. Так как для сборки я использую Maven, я включил в pom.xml следующие зависимости:
Также хочу сказать, что весь код, использованный в примерах, находится на GitHub: https://github.com/hoaz/spring-validation-example. Вы можете склонировать себе этот репозиторий, собрать и поэксперементировать с примерами.
Итак, построим наши примеры на предыдущей бизнес задаче: создание скидочных купонов. На вход наш контроллер получает данные о купонах, которые нужно сгенерить (DiscountCouponGenerationCommand), в качестве результата возвращает список сгенерированных купонов (CouponJson) либо список ошибок валидации.
Поля нашей команды помечены специальными аннотациями, которые называются Constraint. В примере я использовал несколько стандартных аннотаций @Min, @Digits. Также я использовал свои собвстенные Constraint аннотации: @CouponCode для валидации поля couponCode, @DiscountCoupon для валидации всего бина DiscountCouponGenerationCommand.
Как же работают Constraint аннотации? Давайте заглянем внутрь одной их них:
Прежде всего, обратите внимание на аннотацию @Constraint, которая указывает на то, что @CouponCode является Constraint аннотацией, также она содержит в себе имя валидатора, который будет использоваться для валидации поля, помеченного как @CouponCode. В нашем случае валидирующий класс - это CouponCodeValidator. Из других частей аннотации также следует обратить внимание на параметр message, который содержит сообщение-ошибку, которое будет передано на клиент, в случае если валидация поля прошла не успешно. Также аннотация @Target содержит список элементов Java класса, перед которыми мы сможем использовать @CouponCode.
Следует сказать, что текст ошибок валидации можно указывать явно в классе, либо использовать property файл, что особенно удобно, если необходима локализация сообщений. В этом случае вместо сообщения в параметре message нужно указать ключ окруженный фигурными скобками, а само сообщение поместить в файл ValidationMessages.properties, который обязательно должен находиться в корне classpath:
Рассмотрим теперь сам валидатор CouponCodeValidator.
Как видно из примера, валидатор имплементирует generic интерфейс. Первый generic параметр - это constraint который обрабатывает валидатор, второй - тип поля, на котором предположительно будет использоваться constraint. Итак, наш валидатор проверяет, что поле анотированное @CouponCode не пустое, имеет длину 10 символов и начинается с символов 'CP'.
Теперь перейдем собственно к конроллеру, который будет заниматься обработкой команды, валидацией и генерацией купонов.
Мы помечаем параметр DiscountCouponGenerationCommand аннотацией @Valid, которая и включит механизм валидации перед тем, как будет запущен метод generateDiscountCoupons. Всю работу за нас сделает Spring MVC:
- вначале будет создана команда DiscountCouponGenerationCommand, которая заполнится значениями из POST запроса;
- далее команду просканируют на наличие constraint аннотаций, и для каждой из них запустится соответствующий валидатор;
- если ошибок не будет, то вызовется метод контроллера generateDiscountCoupons.
В последних версиях Spring MVC ввели еще одну возможность обработки ошибок валидации - с помощью обработчиков исключений.
Метод аннотированный @ExceptionHandler аннотацией будет автоматически использоваться для обработки ошибок валидации, если таковые возникнут. Параметр MethodArgumentNotValidException содержит всю необходимую нам информацию об ошибках и месте где они возникли.
Конфигурация Spring контекста для JSR-303 не требует ничего особенного, она содержит 2 строки, которые инициализируют поиск компонент и Spring MVC:
В качестве имплементации JSR-303 я использовал Hibernate Validator, его необходимо поместить в classpath. Так как для сборки я использую Maven, я включил в pom.xml следующие зависимости:
Также хочу сказать, что весь код, использованный в примерах, находится на GitHub: https://github.com/hoaz/spring-validation-example. Вы можете склонировать себе этот репозиторий, собрать и поэксперементировать с примерами.
2012-07-11
Валидация данных в Spring MVC
Сегодня мы публикуем статью Олега Артемова. Нашего старого боевого товарища и знатока Spring.
Добрый день всем !!
Данная статья является лишь первой моей статьей об использовании Spring, поэтому не стоит судить строго. :). На её написание меня натолкнуло постоянное количество одного и того же кода в нашем WEB - приложении, поэтому в один прекрасный момент я решил сделать что-нить более элегантное( благо у Spring возможностей хоть отбавляй).
Итак, начнем с преамбулы.
Как мы видим, это код в обычном Spring контроллере, который возвращает JSON. Обычно наш код выглядит именно так. Т.е мы видим, что приходит команда (набор параметров с UI), которые ввел пользователь и объект BindingResult (Spring сам его формирует и передает как параметр в метод) . Дальше проводится валидация команды, если она не прошла – возвращаем перечень ошибок, в противном случае выполняем какую-то логику и возвращаем статус ОК. Таких методов в разных контроллерах у нас довольно много и как любому программисту мне не хочется дублировать код. Поэтому я поставил перед собой 2 задачи:
- Как сделать, чтобы когда я ставлю @Valid на команде (а это стандартные возможности Spring), то вызывался нужный мне валидатор (который я зарегистрирую в контексте, а не стандартный JSR валидатор – у меня в валидаторах часто кастомная логика).
- Как сделать, чтобы в контроллер приходила команда только в том случае, если валидация полностью прошла. Этим я избавлюсь от ненужных инжектов валидаторов в контроллер и их постоянного вызова.
- Есть стандартная Spring – аннотация для метода InitBinder, которая позволяет регистрировать валидаторы для разных команд. Но тогда бы мне в каждом контроллере пришлось бы писать метод с этой аннотацией для каждой команды, которую обрабатывает контроллер (не очень хочется).
- Зарегистрировать глобальный валидатор, который централизованно будет управлять валидацией.
Как мы видим из кода, композитный валидатор ищет нужный валидатор для данной команды. Если не нашел, то используется стандартный JSR валидатор (для этого я использую hibernate-validators). Таким образом, теперь мой код сокращается и порядок действий выглядит так:
1. Ставлю аннотацию @Valid на команду.
2. Спринг вызовет композитный валидатор, а тот в свою очередь найдет нужный валидатор для данной команды и провалидирует её.
Итак, с первой задачей разобрались. Вторая поинтереснее. Для её решения был выбран Spring AOP. Напомню, что с помощью аспекта можно изменить поведение метода (более детально читайте главу по АОП в Спринг референс). Итак, решение такое:
Постараюсь объяснить. Аспект перехватывает все методы контроллеров (а есть разные виды аспектов – этот вид, лишь один из множества), которые помечены аннотацией ValidOnlyForJSON. Если команда была провалидирована успешно, то вызовется оригинальный метод контроллера, в противном случае – вернется перечень ошибок для клиента (то что мы делали в каждом методе).
Для имплементации был подключен AspectJ. Итак посмотрим, как теперь выглядит наш код:
По-моему, выглядит очень прилично. Таким образом, мы получили элегантное и готовое для переиспользования (а это самое главное) решение.
Оригинал статьи тут.
Обновляем материалы
2012-07-07
Гостевые статьи
Если Вы хотите:
- что-то рассказать о Spring,
- например поделиться тем, как Вы решали проблемы проекта с помощью Spring,
- либо рассказать о некоторых неочевидных,
- или просто удобных веща.
Мы с удовольствием примем гостевый статьи и напишем их у себя с указанием авторства.
Кроме того если у Вас есть интересные ссылки по тематике Spring - отправляйте их на наш твиттер @ua_spring.
Заранее спасибо, ваши me & eugene
- что-то рассказать о Spring,
- например поделиться тем, как Вы решали проблемы проекта с помощью Spring,
- либо рассказать о некоторых неочевидных,
- или просто удобных веща.
Мы с удовольствием примем гостевый статьи и напишем их у себя с указанием авторства.
Кроме того если у Вас есть интересные ссылки по тематике Spring - отправляйте их на наш твиттер @ua_spring.
Заранее спасибо, ваши me & eugene
2012-07-06
Будущие выступления
Мы хотели бы расширить географию проведения наших воркшопов. С удовльствием выступим и в Киеве и везде где интересуются Спрингом :) Пишите, будем рады помочь.
Отчет о JavaTechTalks №2, 29-30 июня Одесса
Первым нашим выступлением в рамках проекта, для нас киевлян, Одесса стала не случайно. Куда же ещё поехать летом как не к морю? Что собственно мы и сделали воспользовавшись возможностью, которую нам дала Логика в которой мы с Женей длительное время работали. Мы планировали выступить на встрече TechTalks в пятницу вечером с докладом о Spring MVC (слайды тут, виде ожидаем), а на следующий день провести воркшоп. Доклад на наш взгляд получился неплохой (судя по количеству вопросов).
На воркшоп в праздничную субботу дошли не все зарегистрировавшиеся, но как говорится остались только те, кто действительно хотел узнать больше про Spring. Рассказ первого модуля IoC занял у нас примерно столько сколько мы планировали, а вот Persistence и Spring MVC пошёл намного быстрее. В целом, на наш взгляд получилось очень интересно и продуктивно. Ждём отзывов от участников :)
Спасибо всем кто пришёл! Приходите ещё!
Спасибо организаторам - компании Логика, а также лично Юле Черняк.
2012-06-26
JavaTechTalks 29-30 июня, Одесса
В рамках проводимого компанией Логика в Одессе мепроприятия под названием JavaTechTalks состоится однодневный воркшоп Spring By Example. Детали мероприятия тут. Места на воркшоп были заняты за пару дней, что очень радует. На воркшопе мы рассмотрим на примерах работу Spring IoC контейнера, построения persistence слоя с помощью Spring. А также программирование веб-приложений на основе Spring MVC. Все примеры воркшопа составлены с использованием новинок Spring 3.1 и работают на артефактах версии Spring 3.1.1 RELEASE.
2012-06-21
Subscribe to:
Posts (Atom)