Полный пример бизнес-уровня на Spring

Итак, мы увидели возможности Spring на примере одной функциональности — Profession. Теперь мы сделаем следующее: для каждой из наших таблиц создадим подобную функциональность, которая будет включать в себя полный набор классов и интерфейсов:

  • Entity
  • DAO интерфейс
  • DAO реализация
  • Facade
  • View для UI

Это сделает наш код чуть-чуть сложнее и запутаннее. Но именно, что «чуть-чуть». В принципе можно было бы обойтись одним DAO, одним фасадом и даже убрать DAO-интерфейс. Вы можете реализовать такой упрощенный вариант сами. Исходный код для всех классов вы можете найти в проекте Spring_04. Здесь мы опишем только функциональность каждого фасада, чтобы вы имели представление кто что делает. А дальше по коду вы сможете разобраться сами. Я очень хочу, чтобы вы читали код самостоятельно — умение быстро читать код вом пригодится.
При чтении кода я вам рекомендую обратить внимание на то, что класса Main, который мы использовали для «тестирования» работоспособности нашего приложения, уже нет. Вместо него я написал специальный класс для тестирования SpringStudentFacadeTest. Этот класс использует еще одну функциональность Spring — тестирование. Пакет предоставляет несколько очень удобных инструментов, которые мы еще рассмотрим. А пока давайте посмотрим на код этого класса.

 

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

  1. Наш класс StudentFacadeTest, который занимается тестированием, наследуется от классаAbstractTransactionalJUnit4SpringContextTests. Это позволяет нам использовать транзакции и даже отменять сделанные изменения. Что достаточно удобно.
  2. Мы используем аннотацию @ContextConfiguration для указания, какие файлы используются для конфигурации Spring
  3. Аннотация @TransactionConfiguration позволяет нам выбрать тот менеджер транзакций, который нами будет использоваться в случае тестирования. В нашем случае он у нас один, но вполне может быть ситуация, что ваше приложение в реальности будет работать под управлением Application Server J2EE. Значит и менеджер транзакций будет использоваться от самого сервера. Когда же мы тестируем нашу программу, то вполне вероятно, что использование J2EE сервера не очень удачная мысль и тогда нам потребуется иной менеджер транзакций. Вот для этого и нужна данная аннотация.
  4. Еще одна аннотация — @Autowired. Она позволяет автоматически установить значение поля, что конечно же удобно.
  5. Остальные две аннотации: @Test служит для обозначения методов, которые надо вызывать в процессе тестирования. И @Rollback — значение false говорит, что изменения в базе данных, которые сделаны в этом методе должны быть оставлены. Я это сделал намеренно.

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

 

А теперь коротко рассмотрим функциональность фасадов. Только предварительно я хочу сделать небольшое отступление. При написании приложения возникает некоторая сложность при работе со списком и единичным экземпляром. Суть ее в том, что информация, которая требуется для списка, может быть более экономичной, чем для одного экземпляра. Когда же мы смотрим например одну специальность, то хорошо сразу иметь и список предметов для этой специальности. Для списка специальностей эта информация будет скорее всего излишней. Можно сделать отдельный класс View для списка и для одного экземпляра. Здесь я предлагаю иной вариант — класс один, но его заполнение может проиходить двумя способами — полное и частичное. Дополнительный логический аргумент в конструкторе View позволяет выбрать режим заполнения.

ProfessionFacade.java

  • addProfession — добавить новую специальность
  • updateProfession — изменить существующую специальность
  • updateSubjectList — изменить список прдеметов, которые соответствуют данной специальности.
  • updateSubjectList — иной вариант изменить список предметов
  • deleteProfession — удалить специальность
  • getProfession — получить одну специальность
  • findProfession — получить полный список специальностей

 

SubjectFacade.java

  • addSubject — добавить новый предмет
  • updateSubject — изменить существующий предмет
  • deleteSubject — удалить предмет
  • getSubject — получить один предмет
  • findSubject — получить полный список предметов
  • findSubjectById — получить список предметов по набору их ID
  • findSubjectByProfession — получить список предметов для выбранной специальности

 

ApplicantFacade.java

  • addApplicant — добавить абитуриента
  • updateApplicant — изменить данные абитуриента
  • updateApplicantResult — записать результаты абитуриента
  • deleteApplicant — удалить абитуриента
  • getApplicant — получить данные для одного абитуриента
  • findApplicant — получить список абитуриентов

 

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

  • Список оценок по предмету в каком-то году
  • Список профессий по предмету
  • Средняя оценка по предмету среди абитуриентов
  • и многое другое

Как говорил мой хороший знакомый: «Есть два способа научиться программированию. Первый — пытаться создавать что-то самому. Второй — пойти на курсы, прочитать рекомендуемую литературу и … пытаться создавать что-то самому.»

 

Остальной код вы можете рассмотреть самостоятельно. Для запуска теста откройте его в редакторе и нажмите Shift+F6 или выберите пункт менюRun->Run File.

Библиотеки

Для текущего проекта нам понадобятся следующие библиотеки:
antlr-2.7.6.jar
asm.jar
cglib-2.1.jar
commons-collections-3.1.jar
commons-logging.jar
dom4j-1.6.1.jar
ejb3-persistence.jar
hibernate-annotations.jar
hibernate-commons-annotations.jar
hibernate3.jar
javaee.jar
javassist-3.4.GA.jar
jta-1.1.jar
log4j-1.2.15.jar
mysqlJDBC-3.1.13.jar
slf4j-api-1.5.3.jar
slf4j-log4j12-1.5.3.jar
spring.jar
spring-test.jar
spring-webmvc.jar
junit-4.4.jar
jstl.jar
standard.jar

Переходим на уровень Web

Как только мы начинаем заниматься Web-программированием, у нас возникает потребность удобно делать несколько вещей, главными из которых на мой взгляд являются:

  1. User Interface
  2. Вызов обработчика запроса от клиента на сервере
  3. Передача данных с клиента серверу и обратно

О первом пункте мы поговорим в другой раз. А вот пункты 2 и 3 мы рассмотрим, т.к. Spring включает в себя инструменты для них.

 

Для решения данной задачи был разработан шаблон MVC — Model-View-Controller (Модель, Представление, Контроллер). Их функции можно описать так:
Controller — это компонент, задача которого каким-либо образом решить, что конкретно надо делать. Можно сформулировать иначе и более конкретно: какой метод какого класса должен обработать данный запрос.
Model — компонент, который является хранителем данных, которые будут отображаться. Важно отметить, что модели совершенно неважно как данные будут отображаться на экране. Это очень важный момент, который позволяет иметь несколько вариантов отображения — для броузера, для телефона или даже для печатной формы.
View — компонент, который умеет отображать данные модели. Для отображения одной модели может быть использовано несколько View.

Таким образом, схема работы этих трех компонентов может быть описана следующим образом: Controller получает запрос и по определенным настройкам (правилам) с учетом полученных параметров определяет, что именно надо делать для получения данных (Model). После работы с данными Model может быть передана View и с помощью этого View данные будут отображаться на экране.

Очень часто контроллером может быть какой-то сервлет, который по определенной конфигурации делает что-либо. Model — здесь не могу однозначно что-то сказать. Это может быть самый обычный класс с нужными полями. А в качестве View выступает чаще всего JSP-страница.
Систем, реализующих данный шаблон, достаточно много. В данной части мы рассмотрми реализацию MVC на Spring.

Spring MVC

Как я уже упоминал, в качестве контроллера часто выступает сервлет. Spring следует этому правилу и для начала мы рассмотрим файл web.xml — вместилище сервлетов. Кстати сам web.xml можно также рассматривать в качестве несложного контроллера. Он ведь занимается вызовами разных сервлетов по определенным маскам URL. Итак, вот наш web.xml

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

  • ContextLoaderServlet — это сервлет из пакета Spring, который берет на себя обязанности по загрузке контента Spring. если web-сервер поддерживает спецификацию Servlet 2.4 и выше, то документация советует использовать ContextLoaderListener. Но мы оставим наш пример в таком виде. Как видите мы его загружаем сразу и первым (см. load-on-startup)
  • DispatcherServlet — в обязанности этого класса входит обработать все запросы. Это по сути и есть контроллер. Я бы назвал его предварительным. Как мы увидим чуть позже, он не единственный. Его мы тоже загружаем сразу. Отметьте, что он будет обрабатывать все запросы, которые оканчиваются на .std
  • contextConfigLocation — этот параметр содержит список всех файлов для конфигурации Spring

Также обратите внимание на определение ресурса класса DataSource (он в самом низу файла — ресурс studentDS). Мы уже пользовались таким определением в Часть 9 — Простое Web-приложение.
ВАЖНО: Не забудьте скопировать файл mysql-connector-java-3.1.13-bin.jar в каталог /lib корневого каталога Tomcat (я говорю о Tomcat 6). Для Tomcat 5 каталог /common/lib Если в двух словах: Tomcat предоставляет возможность воспользоваться реализацией интерфейсаjavax.sql.DataSource, которая является пулом коннектов к базе данных. параметры для коннекта находятся в файлеMETA_INF/context.xml

 

Надеюсь, что больших вопросов содержание данного файла у вас не вызовет. Если что — читайте 9-ю часть. Там все описано более подробно.
Гораздо более интересным будет файл StudentDatabase.xml, где мы увидим как использовать заново определенный DataSource

Как видите, теперь мы обращаемся к ресурсу по имени и этот ресурс предоставляет нам Web-контейнер Tomcat. Т.е. теперь параметры коннекта зарегистрированы у Tomcat и каждый, кто захочет, может им пользоваться. Опять разделение труда и облегчение работы. Что приятно.

Разобравшись с DataSource и вопросом загрузки контента Spring давайте рассмотрим что и как делает Spring после этого для реализации шаблона MVC. Но прежде чем рассматривать xml-файлы и конкретные классы, давайте более подробно остановимся на принципах организации всго механищма MVC в Spring. Я воспользовался картинкой из документации Spring.

Как видим, все начинается с прихода запроса в DispatcherServlet (Front Controller). В нем определяется какой именно класс (который реализует интерфейсorg.springframework.web.servlet.mvc.Controller) будет использоваться для обработки конкретного запроса. Именно в этом классе мы будем организовывать логику получения данных для отображения. Если быть более точным, то DispatcherServlet использует объект/класс, который реализует интерфейс HandlerMapping. Вобщем-то никто не мешает использовать уже готовые классы от Spring -SimpleUrlHandlerMapping или BeanNameUrlHandlerMapping. После того, как сделаны нужные изменения и данные готовы, контроллер решает, какое именно представление (View) будет использовано для отображения. Имя этого View передается так называемому ViewResolver’у (точный перевод сделать сложно, но наверно лучшим будет что-то вроде «определитель/выбиратель» View). Если быть более точным — ViewResolver’ов может быть несколько. Они организуются в последовательность (причем порядком вы можете управлять) и каждый пытается определить, какой View скрывается под указанным именем. Когда View определен (в большинстве случаев это какая-то JSP-страница) ему передаются данные и уже сформированная HTML-страница (а может WML) отправляется в броузер.
Еще раз кратко опишем всю цепочку: HTTP-запрос получает DispatcherServlet, который передает управление нужному контроллеру (в соответствии со своей конфигурацией). Контроллер получает данные и передает их набору ViewResolver’ов, которые по очереди пытаются найти нужный View. После того, как View найден, он получает данные, подготавливает HTML-страницу и отправляет ее в броузер.

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

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

Далее идут три наших контроллера — для показа наших трех тсраниц. И самое интересное — это «выбиратель» страниц — в данном случае мы воспользовались классом InternalResourceViewResolver. Принцип его работы следующий: Сначала подставляется часть из prefix, потом к ней подставляется имя View, которое нам передаст контроллер (мы чуть ниже это увидим) и в конце подставляется часть из suffix. Т.е. если в качестве имени контроллер передаст строку subject/subject, то итогом будет страница JSP /subject/subject.jsp, которой и будет передано управление.

А теперь самое время посмотреть на код одного из контроллеров — они у нас достаточно похожи и поэтому мы рассмотрим только один -ProfessionController.

Как видите, в нем нет ничего сложного. Мы унаследовали наш контроллер от класса AbstractController и переопределили методhandleRequestInternal, который в качестве параметров имеет то же, что и обычный сервлет. Обратите внимание на два момента:

  1. Мы создали объект Map, который содержит имя объекта с данными и сам объект (именно по этому имени мы будем обращаться к данным из страницы JSP)
  2. Нащ метод возвращает объект класса ModelAndView, в конструкторе которого мы указали имя View (которое позволит нам сконструировать имя для JSP) и объект с данными.

Теперь нам осталось собрать проект, положить готовый файл Spring_05.war в директорию <TOMCAT_HOME>\webapps и запустить Tomcat.

 

Еще один момент — это страница JSP которая будет отображать данные.

Обратите внимание на часть

Если вы посмотрите снова на код нашего контроллера, то увидите, что наши данные мы поместили под именем professionList. И именно по этому имени обращаемся к данным.

Теперь вы можете проверить наше приложение подставляя разные URL:
http://localhost:8080/Spring_05/profession.std
http://localhost:8080/Spring_05/subject.std
http://localhost:8080/Spring_05/applicant.std

Исходный код для всех классов вы можете найти в проекте Spring_05.

Тестирование без Tomcat

В конце мне бы хотелось обратить ваше внимание на еще два класса, которые у нас появились в разделе Test Packages — а именноStudentControllerTest и StudentSuit

Spring предоставляет немало интересных вохможностей по тестированию. Одна из них — возможность создания объектов, к котороым можно обратиться через JNDI — мы ведь используем данный способ. Чтобы не переделывать конфигурацию можно использовать нужные классы. Мы создаем эмулятор JNDI и помещаем туда DriverManagerDataSource, который связан с тем же именем, что и при использовании Tomcat. Также следует обратить внимание, каким образом создается целый набор классов, котоый мы запускаем для тестирование — я имею в виду аннотацию@Suite.SuiteClasses.

И давайте посмотрим на класс StudentControllerTest. В нем самое главное — это использование так называемых mock-объектов (я бы перевел это как подставных/тренировочных). Как вы уже видели в метод контроллера мы должны передать объекты, которые реализуют интерфейсHttpServletRequest и HttpServletResponse. Но это интерфейсы, а нам нухны реальные объекты. И Spring предоставляет нам такой набор — их возможности достаточно большие — я настоятельно советую вам посмотреть документацию на них.

ВНИМАНИЕ !!! Перед запуском тестов база данных должна быть пустой. Вы можете это сделать запустить скрипт создания базы — Часть 15 — Новая структура данных. Такой вариант не является удачным, но в данном случае мне хотелось сразу наполнить базу данными. Ну и заодно увидеть, что не так именно в таком тесте. Вы можете сделать тесты более удобными и правильными.

Архив с исходными кодами: Исходный код

Leave a reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.