Глава 1 Spring in Action 2th edition |
"Пружина" в действии
Эта глава охватывает
- Исследование основных модулей Spring
- Разъединение объектов приложений
- Управление сквозными задачами с AOP
Все началось с компонента (bean) В 1996 язык программирования Java был еще молодой, захватывающей, перспективной платформой. Многие разработчики пришли в этот язык после того, как они увидели, как создавать богатые и динамические web-приложения используя апплеты.
Однако вскоре они увидели, что в этом новом странном языке было гораздо больше, нежели просто управление анимацией. Как ни один другой язык до этого, Java сделал возможным написание сложных приложений, состоящих из отдельных частей. Они пришли ради апплетов, а остались ради компонентов.
Это было в декабре того года, когда Sun Microsystems обнародовал 1.00-A спецификацию JavaBeans. JavaBeans определили компонентную модель Java. Эта спецификация определила набор приемов программирования, который предоставлял возможность повторного использования и простого соединения Java-объектов в более сложные приложения. Несмотря на то, что JavaBeans были предназначены в качестве универсального средства определения повторно используемых компонентов, они преимущественно использовались в качестве шаблона для создания фрагментов пользовательского интерфейса.
Они оказались слишком простыми, чтобы быть способными к реальной работе. Enterprise-разработчики хотели большего. Сложные приложения часто требовали такие сервисы как поддержка транзакций, безопасность и распределенные вычисления, которые абсолютно не поддерживались спецификацией JavaBeans. Поэтому в марте 1998 Sun выпустил версию 1.0 спецификации Enterprise JavaBeans (EJB). Эта спецификация расширяла понятие компонентов Java на стороне сервера, предоставляя весьма недостающие enterprise сервисы, но была не в состоянии поддерживать простоту исходной спецификации JavaBeans.
На самом деле, за исключением названия, EJB имел весьма небольшое сходство с исходной спецификацией JavaBeans. Несмотря на тот факт, что многие удачные приложения созданы на базе EJB, к сожалению EJB никогда не достигала своей цели: упростить разработку корпоративных приложений. Это правда, что декларативное программирование в модели EJB упрощает многие инфраструктурные аспекты разработки, такие как транзакционность и безопасность. Однако, с другой стороны, EJB усложняют разработку, предписывая дескрипторы развертывания и отягощая код. Спустя время, многие разработчики разочаровались в EJB. В результате, его популярность в последние годы начала снижаться, заставляя многих разработчиков искать более простой путь.
Сегодня, разработка Java компонентов вернулась к своим истокам. Новые технологии программирования, включая аспектно-ориентированное программирование (aspect-oriented programming (AOP)) и внедрение зависимостей (dependency injection (DI)), дают JavaBeans большую часть возможностей, ранее заложенных в EJB. Эти технологии оснащают обычные Java объекты (plain-old Java objects (POJO)) моделью декларативного программирования, напоминающим EJB, но без всей сложности спецификации EJB. Больше не надо создавать громоздкий EJB компонент, когда будет достаточно простого компонента JavaBean.
По правде, даже EJB развились в соответствии с моделью программирования основанной на POJO. Используя идеи DI и AOP, последняя спецификация EJB гораздо проще своих предшественников. Для многих разработчиков, тем не менее, этот шаг очень мал и слишком запоздал. К тому времени, когда спецификация EJB 3 вышла на сцену, другие POJO-ориентированные фреймворки уже утвердились в Java сообществе в качестве стандартов де-факто.
Передовым средством простой разработки, основанной на POJO, является Spring Framework, который мы будем изучать на протяжении этой книги. В этой главе мы собираемся рассмотреть Spring Framework на верхнем уровне, давая вам представление о том, что такое Spring. Эта глава дает хорошее понимание круга задач, которые решает Spring и готовит почву для остальной части книги. Перво-наперво – давайте выясним что такое Spring.
Source(s): Spring in Action 2th edition
Что такое Spring?
Spring – это фреймворк с открытым исходным кодом, созданный Родом Джонсоном и описанный в его книге Expert One-on-One: J2EE Design and Development. Она была посвящена проблематике разработки корпоративных приложений. Spring делает возможным использование простых компонентов JavaBeans для достижения вещей, которые ранее были возможны только с EJB. Хотя полезность Spring не ограничивалась исключительно разработкой серверной стороны. Любое Java приложение может извлечь выгоду из Spring с точки зрения простоты, тестируемости и слабой связанности.
ЗАМЕЧАНИЕ
- Чтобы избежать неоднозначности, я буду использовать слово "бин", обращаясь к обычному компоненту, соответствующему спецификации JavaBeans, и "EJB", обращаясь к компоненту, соответствующему спецификации EnterpriseJavaBeans. Я также время от времени буду использовать термин "POJO" (обычный объект Java - plain-old Java object) в качестве синонима слову "бин".
Spring делает многие вещи, но, если обнажить его до уровня базовых компонентов, Spring – это легковесный dependency injection (DI) и аспектно-ориентированный (AOP) контейнер и фреймворк. Это довольно сложно произносимо, но хорошо раскрывает основную цель Spring. Чтобы определение Spring имело больше смысла, давайте разберем это описание:
- Легковесный – Spring является легковесной с точки зрения размера и накладных расходов. Весь Spring Framework можно разделить на простые JAR файлы, которые весят всего порядка 2.5 МВ. А издержки обработки при использовании Spring незначительны. К тому же Spring ненавязчив:
в приложенииобъекты приложения, использующего Spring, зачастую не имеют зависимостей от специфических классов Spring.
- Dependency Injection (инъекция зависимости) – Spring поддерживает слабое связывание посредством технологии, известной как dependency injection (DI). При использовании DI объекты пассивно отдают свои зависимости вместо того, чтобы создавать или искать зависимые объекты самостоятельно. Вы можете думать о DI как о перевернутом JNDI – вместо поиска объектом зависимостей в контейнере, контейнер предоставляет зависимости объекту на этапе создания не дожидаясь запроса.
- Аспектно-ориентированный (AOP) – Spring поставляется с богатой поддержкой аспектно-ориентированного программирования (AOP), что делает возможным взаимосвязанную разработку, отделяя бизнес-логику приложения от системных сервисов (таких как аудит и управление транзакциями). Объекты приложения делают то, что должны делать – выполняют прикладную логику – и ничего больше. Они не отвечают за другие системные вопросы (или даже не осведомлены о них), такие как регистрация событий (logging) или поддержка транзикций.
- Контейнер – Spring является контейнером в том смысле, что он содержит в себе и управляет жизненным циклом и конфигурацией программных объектов. В Spring можно определить порядок создания программных объектов, их конфигурацию, и то, как они взаимодействуют друг с другом. Framework – Spring позволяет конфигурировать и составлять сложные приложения из простых компонентов.
В Spring программные объекты соединены декларативно, обычно в XML файле. Кроме того Spring обеспечивает инфраструктурную функциональность (управление транзакций, взаимодействие с фреймворками хранения данных и пр.), оставляя разработку прикладной логики за вами. Итак: если обнажить Spring до уровня базовых компонентов, вы получите фреймворк, который помогает вам разрабатывать слабо связанный программный код. Даже если это все, что может Spring, преимущества слабого связывания (масштабируемость и простота тестирования) делают Spring стоящим фреймворком для создания приложений.
Но Spring - больше. Spring Framework поставляется с несколькими модулями, основанными на DI и AOP, что делает его многофункциональной платформой разработки приложений.
Модули Spring-а
Spring фреймворк состоит из нескольких структурированных модулей (рисунок 1.1). В совокупности данные модули предоставляют вам все необходимое для разработки приложений уровня предприятий. Но вы не обязаны основывать ваше приложение только на фреймворке Spring. Вы свободны в выборе модулей , которые необходимы в вашем приложении, и поиске альтернатив в том случае, если Spring не отвечает всем требованиям. Фактически Spring предлагает точки интеграции с некоторыми другими фреймворками и библиотеками, и вам не придется писать их самим.
Как можно видеть, все модули Spring построены на базе ядра контейнера. Контейнер, являясь существенной частью Spring, определяет, как компоненты создаются, конфигурируются и управляются. Вы будете неявно использовать эти классы, когда будете конфигурировать ваше приложение. Но как разработчика, вас, вероятно, более будут интересовать другие модули, которые используют сервисы, предоставляемые контейнером. Эти модули обеспечат фреймворки, с помощью которых вы будете строить ваше приложение, такими сервисами как AOP и persistence.
Давайте рассмотрим модули Spring на рисунке 1.1, каждый в отдельности, чтобы увидеть, как они вписываются в общую картину Spring.
Ядро контейнера
В самом основании рисунка 1.1, вы найдете ядро контейнера Spring. Ядро контейнера предоставляет основную функциональность фреймворка Spring. Этот модуль состоит из BeanFactory, который является основным контейнером Spring и базисом, на котором зиждиться Spring DI. Модуль ядра (центральная часть любого приложения Spring) будет обсуждаться на протяжении всей книги, начиная с главы 2, где мы исследуем связывание компонентов с использованием DI.
Модуль контекста приложения
Контекст приложения Spring строится на ядре контейнера. BeanFactory в модуле ядра делает Spring контейнером, в то время как модуль контекста делает его фреймворком. Данный модуль расширяет концепцию BeanFactory, добавляя поддержу интернационализации (I18N) сообщений, событий жизненного цикла приложения, и валидации. Кроме того, этот модуль предлагает многие enterprise сервисы, такие как email, доступ с помощью JNDI, EJB интеграцию, удаленный вызов и запланированный запуск. Также включена поддержка интеграции с фреймворками шаблонов, такими как Velocity и FreeMarker.
AOP модуль
Spring обеспечивает богатую поддержку аспектно-ориентированного программирования в своем AOP модуле. Данный модуль работает как базис при разработке ваших собственных аспектов в приложении, построенном с использованием Spring. Как и DI, AOP содействует слабой связанности объектов приложения. Однако с помощью AOP аспекты приложения (такие как транзакции и безопасность) отделяются от объектов, к которым они применяются. AOP module в Spring предоставляет несколько подходов в построении аспектов, включая создание аспектов, основанных на интерфейсах AOP Alliance (https://aopalliance.sf.net) и поддержку AspectJ. Поддержка AOP в Spring будет рассмотрена более подробно в главе 4.
Модуль абстракции JDBC и DAO
Работа с JDBC зачастую сводится к обширному использованию шаблонного кода, который получает соединение, создает SQL выражение, обрабатывает результирующее множество, а затем закрывает соединение. JDBC и модуль Объектов Доступа к Данным (DAO) в Spring абстрагируются от шаблонного кода, что позволит вам поддерживать код, касающийся доступа к базам данных, прозрачным и простым, а также предотвратит проблемы, появляющиеся в результате ошибки освобождения ресурсов базы данных. Этот модуль также формирует важный слой исключений, основаных на сообщениях об ошибках, выдаваемых некоторыми серверами баз данных. Так что больше никаких попыток расшифровать непонятные и проприетарные сообщения об ошибках SQL! К тому же данный модуль использует AOP модуль, чтобы предоставить сервисы управления транзакциями для объектов в приложении на Spring. Мы увидим, как основанная на шаблонах JDBC абстракция может сильно упростить JDBC код, когда будем рассматривать доступ к данным в Spring в главе 5.
Модуль интеграции объектно-реляционного отображения (ORM)
Для тех, кто предпочитает использовать инструментарий объектно-реляционного связывания (ORM) поверх JDBC, Spring предоставляет ORM модуль. Поддержка ORM в Spring строится на поддержке DAO, обеспечивающей удобный способ создания DAO объектов для некоторых ORM решений. Spring не пытается реализовать свое собственное ORM решение, но просто предоставляет рычаги управления некоторыми популярными ORM фреймворками, включая Hibernate, Java Persistence API, Java Data Objects, и iBATIS SQL Maps. Управление транзакциями в Spring поддерживает все данные ORM фреймворки так же, как и JDBC. В добавок к основанной на шаблонах JDBC абстракции, мы рассмотрим как Spring предоставляет похожие абстракции для ORM и persistence фреймворках в главе 5.
Расширения Управления Java (JMX)
Раскрытие внутренних деталей Java приложения для управления ими является важной частью завершения приложения для выпуска. Модуль JMX упрощает экспозицию ваших компонентов приложения в качестве JMX MBeans. Это делает возможным мониторинг и переконфигурацию исполняемого приложения. Поддержка JMX в Spring будет рассмотрена в главе 12.
Java EE Connector API (JCA)
Эскиз enterprise приложения изобилует смесью приложенией, исполняемых в наборе разнородных серверов и платформ. Интеграция всех этих приложений может быть непростым делом. Java EE Connection API (лучше известный как JCA) предоставляет стандартный способ интеграции Java приложений с разнообразными информационными системами уровня предприятий, включая мейнфреймы и базы данных. Во многих отношениях JCA очень похож на JDBC за исключением того, что JDBC сфокусирован на доступе к базам данных, а JCA является более универсальным API, используемым для соединения к унаследованным системам. Поддержка JCA в Spring похожа на поддержку JDBC в данном фреймворке, в том смысле, что шаблонный код JCA абстрагируется в шаблоны.
MVC фреймворк в Spring
Парадигма Модель/Вид/Контроллер (MVC) является общепринятым подходом к построению веб-приложений таким образом, чтобы пользовательский интерфейс был отделён от логики приложения. Java не испытывает недостатка в MVC фреймворках, таких как Apache Struts, JSF, WebWork, и Tapestry, которые являются наиболее популярным выбором среди MVC. Хотя Spring интегрируется с несколькими популярными MVC фреймворками, он также поставляется со своим собственным очень мощным MVC фреймворком, который использует слабо связные техники Spring в веб-слое приложения. Spring MVC будет рассмотрен в главах 13 и 14.
Portlet MVC в Spring
Многие веб-приложения построены по принципу страниц, который заключается в том, что каждый запрос к приложению дает на выходе отображение совершенно новой страницы. Каждая страница обычно представляет специфический набор информации или запрос новых данных от пользователя в определенной форме. В отличие от этого, приложения, основанные на портлетах, сосредотачивают несколько частей функциональности в одну веб-страницу. Это обеспечивает представление в нескольких приложениях одновременно. Если вы разрабатываете приложения с использованием портлетов, то вы определенно захотите взглянуть на Portlet MVC фреймворк в Spring. Portlet MVC основывается на Spring MVC и предоставляет набор контроллеров, которые поддерживают Java portlet API.
Веб модуль в Spring
Spring MVC и Spring Portlet MVC требуют особого подхода при загрузке контекста приложения Spring. Поэтому веб-модуль Spring предоставляет специальные классы для поддержки Spring MVC и Spring Portlet MVC. К тому же веб-модуль предлагает поддержку некоторых веб-ориентированых задач, таких как загрузку файлов несколькими частями и автоматическое связывание параметров запроса с вашими бизнес-объектами. Также он содержит поддержку интеграции с Apache Struts и JavaServer Faces (JSF).
Удаленный вызов
Не все приложения работают самостоятельно. Зачастую им необходимо задействовать функциональность других приложений, чтобы выполнить свою работу. При доступе по сети к другой программе для коммуникации используется некоторая форма удаленного вызова. Поддержка удаленного вызова в Spring делает доступной функциональность Java объектов в качестве удаленных объектов. Или, в случае, когда необходим удаленный доступ к объектам, модуль для удаленного вызова упрощает компоновку удаленных объектов в ваше приложение, как если бы они были обычными локальными объектами Java. Доступно несколько вариантов удаленного вызова, включая Remote Method Invocation (RMI), Hessian, Burlap, JAX-RPC и собственный HTTP Invoker. В главе 8 будут рассмотрены различные варианты реализации удаленного доступа поддерживаемые Spring.
Java Message Service (JMS)
Недостаток удаленного доступа в том, что он зависит от надежности сети и требует, чтобы оба конца связи были доступны. Связь, ориентированная на сообщения, с другой стороны, более надежна и гарантирует доставку сообщений, даже когда сеть и конечные узлы не надежны. Модуль Spring Java Message Service (JMS) помогает отправлять сообщения в JMS очередь сообщений и темы. В то же время, этот модуль также помогает создавать POJO объекты, управляемые сообщениями, способные принимать асинхронные сообщения. Мы увидим, как использовать Spring для отправки сообщений, в главе 10.
Хотя Spring охватывает многие вопросы, важно понимать, что Spring избегает повторное изобретение колеса, где это возможно. Spring опирается в большей степени на существующие API фреймворки. Например, как мы увидим дальше в главе 5, Spring не реализует собственный пакет доступа к данным, - вместо этого он взаимодействует с некоторыми платформами, способными на это, включая простой JDBC, iBATIS, Hibernate и JPA.
Теперь, когда вы увидели полную картину, давайте посмотрим, как работают DI и AOP функции Spring. Мы намочим ноги, внедряя наш первый компонент в контейнер Spring.
Быстрый старт
Dependency Injection - самая основная вещь Spring-а. Но на что похожа DI?
По великой традиции программирования, я начну показывать Вам как Spring работает на легком примере "Hello World". В отличие от оригинала программы Hello World, тем не менее, этот пример будет немного изменен, чтобы продемонстрировать основы Spring.
Первый класс “Springified” в примере Hello World нуждается в классе, который бы распечатал знакомое приветствие. Листинг 1.1 показывает интерфейс GreetingService, который определяет действие класса.
Листинг 1.1 Интерфейс для сервиса приветствия
package com.springinaction.chapter01.hello;
public interface GreetingService {
void sayGreeting()
}
GreetingServiceImpl (Листинг 1.2) имплементирует интерфейс GreetingService. Хотя и не обязательно скрывать информацию позади интерфейса, это
очень рекомендовано в качестве способа отделить реализацию от ее контракта.
Листинг 1.2 GreetingServiceImpl, который печатает приветствие
package com.springinaction.chapter01.hello;
public class GreetingServiceImpl implements GreetingService {
private String greeting;
public GreetingServiceImpl() {}
public GreetingServiceImpl(String greeting) {
this.greeting = greeting;
}
public void sayGreeting() {
System.out.println(greeting);
}
public void setGreeting(String greeting) {
this.greeting = greeting;
}
}
Класс GreetingServiceImpl имеет единственное свойство: приветствие. Это свойство является простой строкой, которая печатается, когда происходит вызов метода sayGreeting(). Вы, возможно, обратили внимание, что приветствие может быть установлено двумя разными способами:
При помощи конструктора, и при помощи сеттера.
Вот только не совсем очевидно, кто будет вызывать конструктор или метод setGreeting(), чтобы установить приветствие.
На самом деле, мы собираемся позволить установить свойство приветствия контейнеру Spring-а. Конфигурационный файл Spring-а
(hello.xml) в листинге 1.3 говорит контейнеру, как конфигурировать сервис приветствия.
Листинг 1.3 Конфигурирование Hello World в Spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="greetingService" class="com.springinaction.chapter01.hello.
<property name="greeting" value="Buenos Dias!" />
</bean>
</beans>
XML файл в листинге 1.3 объявляет экземпляр класса GreetingServiceImpl в контейнере Spring конфигурирует свойство приветствия значением “Buenos Dias!”. Позволим себе немного углубиться в подробности XML файла, чтобы понять, как это работает.
В корне этого простого XML файла находится элемент <beans>, который является корневым элементом любого конфигурационного файла Spring-а. Элемент <bean> используется для того, чтобы говорить контейнеру Spring-а о классе и как он должен быть сконфигурирован. Здесь аттрибут id задает имя бина greetingService, и аттрибут class используется для того, чтобы указать нужное имя класса.
В пределах элемента <bean>, элемент <property> используется для того, чтобы задать свойство, В нашем случае - приветствия. Как показано здесь, элемент <property> говорит контейнеру Spring вызывать setGreeting(), и отправить “Buenos Dias!” (немного таланта в испанском) описав бин.
Следующий фрагмент кода иллюстрирует примерно, что контейнер делаетпри создании экземпляра службы поздравлений, основанной на определении XML в листинге 1.3:
GreetingServiceImpl greetingService = new GreetingServiceImpl();
greetingService.setGreeting("Buenos Dias!");
По-другму, вы можете установить свойство приветствия через конструктор GreetingServiceImpl. Например:
<bean id="greetingService"
class="com.springinaction.chapter01.hello.
<constructor-arg value="Buenos Dias!" />
</bean>
Следующий отрезок кода иллюстрирует, как контейнер инициализирует сервис приветствия,когда используется элемент <constructor-arg>:
GreetingServiceImpl greetingService = new GreetingServiceImpl("Buenos Dias");
Последний кусочек паззла, это класс, коорый загружает контейнер Spring, и использует его для извлечения приветствия. Листинг 1.4 Показывает этот класс.
Листинг 1.4 main класс программы
package com.springinaction.chapter01.hello;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class HelloApp {
public static void main(String[] args) throws Exception {
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("hello.xml"));
GreetingService greetingService = (GreetingService) factory.getBean("greetingService");
greetingService.sayGreeting();
}
}
Класс BeanFactory, использованный здесь, является контейнером Spring. После загрузки файла hello.xml в контейнер, метод main() обращается к методу getBean() в BeanFactory, чтобы установить ссылку на сервис приветствия.
При помощи этой ссылки, в конце вызывается метод sayGreeting().
Когда вы запустите приложение, оно напечатает (не удивительно)
Buenos Dias!
Это самое простое приложение, которое я мог придумать. Однако, не смотря на его простоту, оно иллюстрирует основы конфигурирования и
использования классов в Spring. К сожалению, это возможно слишком просто, потому что он только иллюстрирует как конфигурировать бин путем ввода строки в свойство. Реальная мощь Spring-а кроется в том, что бины могут быть встроены в другие бины, использую DI.
Понимание dependency injection (DI)
Хотя Spring делает много вещей, DI является сердцем Spring Framework.
Это может казаться немного запугивающим, вызывая в воображении сложной техники программирования
или дизайна паттернов. Но, как оказывается, DI не очень сложна, как кажется.
Фактически, применяя DI в вашем проекте, вы увидите, что ваш код
становится более легче, проще, и более легким в тестировании.
Но что означает "dependency injection" (DI)?
Внедрение зависимостей
На самом деле, dependency injection обычно применяется под другим именем: инверсия управления (inversion of control). Но в статье, написанной в 2004 году, Мартин Флаувер спросил какой аспект управления инвертируется. Он пришел к выводу, что это получение зависимостей, которые инвертируются. Основываясь на этом, он придумал фразу “dependency injection”, термин, лучше описывающий происходящее.
Другие нетривиальные приложения (в значительной степени более сложные, чем HelloWorld.java), составленные из двух или более классов, сотрудничающих друг с другом, реализуя бизнес логику. Традиционно, каждый объект ответственнен за получение его собственной ссылки на объекты, взаимодействующие с ним (их зависимости). Это может привести к чрезвычайно сложному и тяжело тестируемому коду.
Когда работает DI, объекты дают свои зависимости во время создания некоторому внешнему объекту, который координирует каждый объект в системе.
Другими словами, зависимости вводятся в объекты Итак, DI подразумевает инверсию ответственности относительно того, как объект получает
ссылки на зависимые объекты (см. рис. 1.2).
Основная выгода от DI заключается в слабой связанности. Если объект знает о своих используемых объектах только через их интерфейсы (а не через их реализации или то, как экземпляры были созданы), то зависимость может быть заменена другой реализацией и зависимый объект даже не почувствует разницу.
Например, если класс Foo на рисунке 1.2 знает о своей зависимости от Bar только через интерфейс, тогда для него совсем не важна фактическая реализация объекта Bar. Bar может быть как обычным локальным Java объектом, удаленным веб-сервисом, EJB, или реализацией-заглушкой для юнит тестирования - Foo не знает и не заботится об этом.
Если вы похожи на меня, то вам, вероятно, не терпится увидеть как это работает в коде. Чтобы угодить вам, без задержки рассмотрим...
Dependency injection в действии
Предположим, что ваш первоклассный маркетинговый отдел собрал результаты анализа рынка и принял решения, что вашим пользователям
необходим рыцарь - то есть, им нужен Java класс представляющий рыцаря. После уточнения требования вы открываете для себя, что они в конкретности хотят, чтобы был реализован класс, представляющий рыцаря Круглого Стола короля Артура, который отправляется в смелое и доблестное странствие в поисках Святого Грааля.
Это необычная просьба, но Вы привыкли к странным желаниям и прихотям маркетингового отдела. Поэтому, без колебаний, Вы запускаете свой любимый IDE и набиваете класс как в листинге 1.5.
Листинг 1.5 Компонент Рыцаря Круглого Стола
package com.springinaction.chapter01.knight;
public class KnightOfTheRoundTable {
private String name;
private HolyGrailQuest quest;
public KnightOfTheRoundTable(String name) {
this.name = name;
quest = new HolyGrailQuest();
}
public class KnightOfTheRoundTable implements Knight {
private String name;
private Quest quest;
public KnightOfTheRoundTable(String name) {
this.name = name;
quest = new HolyGrailQuest();
}
public Object embarkOnQuest() throws QuestFailedException {
return quest.embark();
}
}
В листинге 1.5 рыцарь нарекается в соответствии с параметром своего конструктора. Этот конструктор устанавливает странствие рыцаря, приписывая ему HolyGrailQuest. Реализация странствия HolyGrailQuest довольно проста, как показано в листинге 1.6.
Листинг 1.6 Испытания в компоненте HolyGrail, которые выпадут на долю рыцаря
package com.springinaction.chapter01.knight;
public class HolyGrailQuest {
public HolyGrailQuest() {}
public HolyGrail embark() throws GrailNotFoundException {
HolyGrail grail = null;
// Искать Грааль
...
return grail;
}
}
Довольные своей работой, Вы гордо публикуете код в системе контроля версий. Вы хотите показать свое творение маркетинговому отделу, но глубоко внутри Вас что-то терзает. Вы уже почти списываете это на пельмени с кетчупом, которыми позавтракали, когда осознаете
в чем дело: Вы не написали юнит тесты.
Рыцарское тестирование
Юнит тестирование является важной частью разработки. Оно не только проверяет, что каждый отдельный юнит функционирует так, как требуется, но также служит для особо тщательного документирования каждого юнита. Стремясь исправить вашу оплошность с написанием юнит тестов, вы собираете тесткейсы (листинг 1.7) для своего класса рыцаря.
Листинг 1.7 Тестирование рыцаря
package com.springinaction.chapter01.knight;
import junit.framework.TestCase;
public class KnightOfTheRoundTableTest extends TestCase {
public void testEmbarkOnQuest() throws GrailNotFoundException {
KnightOfTheRoundTable knight = new KnightOfTheRoundTable("Bedivere");
HolyGrail grail = knight.embarkOnQuest();
assertNotNull(grail);
assertTrue(grail.isHoly());
}
}
После написания этого тесткейса, вы приступаете к написанию тесткейса для HolyGrailQuest. Но даже не начав, вы понимаете, что тесткейс для KnightOfTheRoundTableTest косвенно тестирует HolyGrailQuest. Также Вы беспокоитесь, что все тестирование охватывает все случаи.
Что случится если метод embark() класса HolyGrailQuest вернет null? Или что, если он возбудит исключение GrailNotFoundException?
Кто кого вызывает?
Основная проблема, связанная с KnightOfTheRoundTable, заключается в том, как он получает HolyGrailQuest. Создается ли новый экземпляр HolyGrail или через JNDI получается уже существующий, но каждый рыцарь ответственен за приобретение своего странствия (как показано на рисунке 1.3). Таким образом, у Вас нет способа протестировать класс рыцаря в отдельности. В данных условиях каждый раз, когда Вы тестируете KnightOfTheRoundTable, Вы будете также косвенно тестировать HolyGrailQuest.
И более того, Вы не можете сказать HolyGrailQuest, чтобы он вел себя по-разному (т.е., возвращал null или возбуждал исключение GrailNotFoundException) для разных тестов. Что могло бы помочь, так это, если бы Вы могли бы создать заглушку для HolyGrailQuest, которая позволяла бы решать Вам, как она должна себя вести. Но даже если вы создадите такую заглушку, KnightOfTheRoundTable все равно получает свой собственный HolyGrailQuest, что означает, что Вам придется вносить изменения в KnightOfTheRoundTable, чтобы получить заглушку для странствия, используемую в целях тестирования (и затем заменить ее обратно на стадии выпуска).
Уменьшение связанности с помощью интерфейсов
Одним словом проблема заключается в связанности. С этой точки зрения, KnightOfTheRoundTable статично связан с HolyGrailQuest. Они так скованы вместе, что Вы не можете получить KnightOfTheRoundTable без HolyGrailQuest.
Связанность это двухглавое чудовище. С одной стороны, тесно связанный код трудно тестировать, трудно повторно использовать, трудно понимать и такой код зачастую обладает ошибками в стиле игры whack-a-mole (т.е. исправление одной ошибки приводит к созданию еще одной или нескольких новых ошибок). С другой стороны, совершенно несвязный код ничего не делает. Для того, чтобы делать что-то полезное, классы должны в определенной степени знать друг о друге. Связанность необходима, но с ней нужно обращаться осторожно.
Обычным методом, используемым для уменьшения связности, является скрытие деталей реализации за интерфейсами таким образом, чтобы класс с фактической реализацией мог быть заменен без влияния на класс клиента. К примеру предположим, что Вы создали
интерфейс Quest:
package com.springinaction.chapter01.knight;
public interface Quest {
abstract Object embark() throws QuestFailedException;
}
Затем Вы изменяете HolyGrailQuest, чтобы реализовать этот интерфейс. Также примите к сведению, что embark() теперь возвращает тип Object и возбуждает исключение QuestFailedException.
package com.springinaction.chapter01.knight;
public class HolyGrailQuest implements Quest {
public HolyGrailQuest() {}
public Object embark() throws QuestFailedException {
// Сделать все нужное, чтобы отправиться в странствие
return new HolyGrail();
}
}
К тому же следующий метод должен быть изменен в KnightOfTheRoundTable, чтобы он был совместим с этими типами Quest:
private Quest quest;
...
public Object embarkOnQuest() throws QuestFailedException {
return quest.embark();
}
Также Вы можете сделать KnightOfTheRoundTable реализующим следующий интерфейс Knight:
public interface Knight {
Object embarkOnQuest() throws QuestFailedException;
}
Скрытие реализации Вашего класса за интерфейсами определенно является шагом в нужном направлении. Но то, в чем многие разработчики терпят неудачу, это способ, которым они получают экзепляр Quest. Например, рассмотрим возможные изменения в KnightOfTheRoundTable:
Начало потерялось!!!
public HolyGrail embarkOnQuest() throws GrailNotFoundException {
return quest.embark();
}
}
В данном случае класс KnightOfTheRoundTable отправляется в странствие через интерфейс Quest. Но рыцарь все еще получает определенный тип странствия Quest (а именно HolyGrailQuest). Это немногим лучше прежней ситуации. KnightOfTheRoundTable может отправляться лишь в странствия за Святым Граалем и нет никаких других типов странствий.
Выдача и получение
Вопрос, который возникает на этом этапе, заключается в том, должен ли рыцарь быть ответственным за получение странствия, или рыцарю выдается странствие, в которое он должен отправится?
Рассмотрим следующие изменения в KnightOfTheRoundTable:
public class KnightOfTheRoundTable implements Knight {
private String name;
private Quest quest;
public KnightOfTheRoundTable(String name) {
this.name = name;
}
public Object embarkOnQuest() throws QuestFailedException {
return quest.embark();
}
public void setQuest(Quest quest) {
this.quest = quest;
}
}
Заметили разницу? Сравните рисунок 1.4 с рисунком 1.3, чтобы найти отличия в том, как рыцарь получает странствие. Теперь рыцарю выдается странствие вместо того, чтобы он производил его сам. KnightOfTheRoundTable больше не ответственен за производство своих странствий.
И от того, что он знает о странствии только через интерфейс Quest, Вы можете поручить рыцарю любую реализацию Quest, какую только пожелаете.
В одном случае, Вы можете выдать ему HolyGrailQuest (СтранствиеЗаСвятымГраалем). В другом, иная реализация Quest, как например RescueDamselQuest (СтранствиеЗаПрекраснойДамой), может быть поручена рыцарю. Подобным образом, в тесткейсе Вы можете задавать реализацию-заглушку Quest.
Вкратце, в этом и заключается DI: ответственность за координацию взаимодействия между зависимыми объектами выносится из самих объектов.
Присвоение странствия рыцарю
Теперь, когда Вы написали Ваш класс KnightOfTheRoundTable, которому может быть дан произвольный объект Quest,
как Вы можете конкретизировать, какой Quest должен быть передан?
Процесс создания связей между компонентами приложения называется связыванием. В Spring существует множество способов связать компоненты вместе, но наиболее общим подходом является использование XML. В листинге 1.8 показано содержимое простого конфигурационного файла Spring, knight.xml, который передает странствие (а именно, HolyGrailQuest) рыцарю KnightOfTheRoundTable.
Листинг 1.8 Встраивание странствия в рыцаря в конфигурационном XML файле Spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="quest"
class="com.springinaction.chapter01.knight.HolyGrailQuest" />
<bean id="knight"
class="com.springinaction.chapter01.knight.KnightOfTheRoundTable">
<constructor-arg value="Bedivere" />
<property name="quest" ref="quest" />
</bean>
</beans>
Это лишь несложный подход к связыванию компонентов. Пока не стоит слишком беспокоится о деталях. В главе 2 будет рассказано больше о том, что происходит на данном этапе, а также показаны дополнительные способы связывания Ваших компонентов в Spring.
Теперь, когда мы объявили отношения между рыцарем и странствием, нам нужно загрузить XML file и стартануть приложение.
Увидим как это работает
В приложении, построенном с использованием Spring, BeanFactory загружает определения компонентов и связывает компоненты вместе. Из-за того, что компоненты в примере для рыцаря объявлены в файле XML, подходящей фабрикой для этого примера является XmlBeanFactory. Метод main() в листинге 1.9 использует XmlBeanFactory, чтобы загрузить knight.xml и получить ссылку на объект Knight.
Листинг 1.9 Запуск примера с рыцарем
package com.springinaction.chapter01.knight;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class KnightApp {
public static void main(String[] args) throws Exception {
BeanFactory factory =
new XmlBeanFactory(new FileSystemResource("knight.xml"));
Knight knight = (Knight) factory.getBean("knight");
knight.embarkOnQuest();
}
}
Как только у приложения появляется ссылка на объект Knight, оно просто вызывает метод embarkOnQuest(), чтобы начать приключение рыцаря. Необходимо иметь в виду, что этот класс ничего не знает о странствии, в которое предстоит отправится рыцарю. Опять таки, единственная сущность, которой известен тип поручаемого рыцарю странствия, это файл knight.xml.
Хоть это и весело - посылать рыцарей в странствия с использованием внедрения зависимостей, но давайте все-таки рассмотрим использование DI в жизненных enterprise приложениях.
Внедрение зависимостей в enterprise приложениях
Допустим Вам поручили написание online-магазина. Частью веб-приложения является компонент обслуживания заказов, который обрабатывает все функции, связанные с размещением заказов. На рисунке 1.5 показано несколько способов, которым компонент Checkout веб-слоя (возможно, представленнный действием WebWork или страницей Tapestry) может производить доступ к сервису заказов.
- (1) Простой, но безыскусный, подход мог бы состоять в том, чтобы создавать экземпляр сервиса заказа тогда, когда это необходимо. Вместо прямого связывания веб-слоя с определенным классом сервиса такой подход в итоге приведет к расточительному созданию экземпляров класса OrderServiceImpl, когда достаточно разделяемого, не сохраняющего своего состояния синглтона.
- (2) Если сервис для заказов реализован в виде EJB версии 2.x, то для доступа к сервису Вам сначало надо будет получить базовый интерфейс посредством JNDI , который затем будет использован для доступа к реализации интерфеса сервиса для EJB. В таком случае, веб-слой больше привязан не к определенному интерфейсу, а к JNDI и модели програмирования EJB 2.x.
- (3) В форме компонента EJB 3 сервис для заказов может быть получен напрямую из JNDI (без доступа через базовый интерфейс). Опять таки, в этом случае не будет привязки к определенному классу реализации, но будет зависимость от JNDI.
- (4) При использовании EJB или без, Вам возможно захочется спрятать детали получения через JNDI за локатором сервиса. Это устранит видимые проблемы сцепки с другими подходами, но теперь веб-слой связан с Service Locator.
Ключевой вопрос со всеми из этих подходов является то, что компонент веб-слой слишком активно участвует в получении собственной зависимости. Он слишком много знает о том, откуда происходит заказ сервиса и как это осуществляется.
Если слишком сильное знание о вашей зависимости приводит к тесно связанному коду, то само собой разумеется, что зная как можно меньше о зависимости приведет к слабо связанному коду. Рассмотрим рисунок 1.6, который показывает, как компонент Checkout может быть предоставлен OrderService вместо того, чтобы запрашивать его. Теперь давайте посмотрим, каким образом это будет реализовано с помощью DI:
private OrderService orderService;
public void doRequest(HttpServletRequest request) {
Order order = createOrder(request);
orderService.createOrder(order);
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
Не ищите код! Ссылка на OrderService (который является интерфейсом) предоставляется классом с помощью метода setOrderService(). Веб компонент
не знает или не заботится о том, откуда берется OrderService. Он может быть внедрен Spring-ом или он может быть внедрен вручную явным вызовом setOrderService(). Он также не имеет понятия о том, как реализуется OrderService — он знает только о нем через интерфейс OrderService. С DI ваши объекты приложения освобождаются от бремени извлечение их собственных зависимостей и способны сосредоточить внимание на своих задач, полагая, что их зависимости будут доступны, когда это необходимо.
Внедрение зависимостей является благом для слабосвязанных кода, сделав возможным держать ваши объекты приложения на расстоянии друг от друга.Однако мы лишь слегка дотронулись до контейнера Spring и DI. В главах 2 и 3 вы увидите больше способов связывания объектов в контейнере Spring.
Внедрение зависимости это только одна техника, которую Spring предлагает обьектам POJO в поддержку слабого связывания. Аспект-риентированное программирование обеспечивает различные виды разделения функциональности приложения (такие как безопасность и транзакции).......Давайте взглянем на то, как Spring поддерживает AOP.
Применение аспект-ориентированного программирования
Хотя DI делает возможным связать программные компоненты друг с другом свободно, аспекториентированное
программирование позволяет вам привлекать функциональные возможности многократно используемых компонентов повсюду в вашем приложениии
Введение в АОП.
Аспект-ориентированное программирование часто определяют как технику программирования, которая поддерживает разделение интересов в пределах системы программного обеспечения. Систем, состоящих из разных компонентов, каждый из которых ответственен за специфическую часть функциональности.
Однако часто эти компоненты также несут дополнительную ответственность сверх своей коренной функциональности. Системные сервисы, такие как логгирование, транзакции, управление, и безопасность, часто находят свой путь в компонентах, основная ответственность которых - что-нибудь еще. Такие системные сервисы обычно упоминают как cross-cutting проблемами потому что они имеют тенденцию идти вразрез с различными компонентами системы
С распространением этих проблем в разных компонентах, вы вводите два уровня сложности в ваш код:
- Код который реализует общесистемные проблемы дублируется в различных компонентах. Это означает, что если вы нуждаетесь в замене того, как проблема обрабатывается, вы будете вынуждены просматривать различные компоненты. Даже если вы абстрагируете проблему в отдельный модуль таким образом, чтобы воздействовать на ваши компоненты простым вызовом метода, этот простой вызов метода будет продублирован в разных местах.
- Ваши компоненты будут замусорены кодом, который не сочетается с их основной функциональностью. Метод добавления записи в адресную книгу должен быть озабочен тем, как добавить адрес а не тем, безопасно ли это или это использует транзакцию.
Рисунок 1.7 показывает эту сложность. Бизнес объекты слева слишком глубоко перепутаны с системными сервисами. Каждый объект не только должен знать что он будет регистрировать, обеспечивать безопасность и вовлекаться в транзакционный контекст, но также каждый объект будет сам ответственен за выполнение этих сервисов.
АОП дает возможность разбивать эти сервисы на блоки и затем применять их декларативно в тех компонентах, на которые они должны воздействовать. Это приводит к тому, что компоненты более связаны и что они сфокусированы каждый на своей проблеме, совершенно
не осведомлены о любом системном сервисе, который может быть внедрен. Вкратце, аспекты гарантируют простое сохранение POJOs.
Можно представить аспекты как обертки которые охватывают множество компонентов приложения, как показано на рисунке 1.8. Ядро приложения состоит из модулей, которые осуществляют бизнес функциональность (бизнес логику). Применяя АОП вы можете охватить ядро вашего приложения уровнями функциональности. Такие уровни могут применятся декларативно во всем вашем приложении в гибкой манере без того, чтобы ядро
приложения даже знало о их существовании. Это мощная концепция, поскольку она предотвращает задачи безопасности, регистрации и транзакций от засорения основной бизнес логикой приложения
Чтобы продемонстрировать как аспекты могут применяться в Spring, вернемся к примеру с рыцарем, добавим базовый аспект регистрации.
АОП в действии.
Итак, ваш любимый отдел маркетинга решил озадачить вас дополнительными требованиями. В этих новых требованиях менестрель должен аккомпанировать каждому рыцарю, вести хронику действий и дел рыцаря в песне.
Хммм...менестрель который поет о рыцаре? Это не должно звучать слишком тяжело. Начнем, создайте класс Minstrel, как показано в листинге 1.10
Листинг 1.10 Класс Minstrel, включающий музыкальное логгирование.
package com.springinaction.chapter01.knight;
import org.apache.log4j.Logger;
public class Minstrel {
private static final Logger SONG = Logger.getLogger(Minstrel.class);
public void singBefore(Knight knight) {
SONG.info("Fa la la; Sir " + knight.getName() + " is so brave!");
}
public void singAfter(Knight knight) {
SONG.info("Tee-hee-he; Sir " + knight.getName() + " did embark on a quest!");
}
}
Придерживаясь линии мышления Внедрения Зависимости, вы меняете KnightOfTheRoundTable предоставляющего экземпляр Minstrel:
public class KnightOfTheRoundTable implements Knight {
...
private Minstrel minstrel;
public void setMinstrel(Minstrel minstrel) {
this.minstrel = minstrel;
}
...
public HolyGrail embarkOnQuest()
throws QuestFailedException {
minstrel.singBefore(this);
HolyGrail grail = quest.embark();
minstrel.singAfter(this);
return grail;
}
}
Это должно сработать! Ох подождите...тут одна маленькая проблема. Как это, каждый рыцарь должен остановиться и сказать менестрелю спеть песню прежде чем рыцарь сможет продолжить свое путешествие (как на рисунке 1.9). Затем после путешествия, рыцарь должен не забыть напомнить менестрелю продолжить пение о своих делах. Необходимость помнить остановиться и сказать менестрелю что надо сделать, определенно препятсвует
выполнению квеста рыцарем.
В идеале, менестрель может взять больше инициативы и автоматически петь песни без явного указания делать это. Рыцарь не должен знать (или действительно даже заботится) что его дела будут записаны в песню. В конце-концов, у вас не может быть рыцаря опоздавшего на странствие из-за ленивого менестреля.
Короче говоря контроль за услугами менестреля выходят за рамки обязанностей рыцаря. Другой способ сказать об этом что услуги менестреля (записывать песни) ортогональны обязанностям рыцаря (выполнять задание). Поэтому есть смысл "завернуть" менестреля в аспект который добавит его услугу по записи песен рыцарю. Тогда услуги менестреля будут охватывать всю деятельность рыцаря, рыцарь даже не будет знать, что менестрель здесь, как
показано на рисунке 1.10
Как выясняется, это довольно легко превратить класса Minstrel в листинге 1.10 в аспект, используя поддержку Spring-ом АОП. Давайте посмотрим, каким образом.
Встраивание аспектов.
Есть несколько путей реализации аспектов в Spring, и мы будем ковыряться в каждом из них в главе 4. Но ради примера мы будем использовать новое пространство имен АОП введенное в Spring 2.0. Начиная работу вы должны убедиться, что вы объявили пространство имен в контексте определения XML:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
''https://www.springframework.org/schema/aop ''
''https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">''
...
</beans>
С объявлением пространства имен, мы готовы создать аспект. Часть XML в листинге 1.11 объявляет minstrel как bean в контексте Spring и затем создает аспект который уведомляет knight бин.
Листинг 1.11 Встраивание MinstrelAdvice в knight
<bean id="minstrel"
class="com.springinaction.chapter01.knight.Minstrel" />
<aop:config>
<aop:aspect ref="minstrel">
<aop:pointcut
id="questPointcut"
expression="execution(* *.embarkOnQuest(..) and target(bean)" />
<aop:before
method="singBefore"
pointcut-ref="questPointcut"
arg-names="bean" />
<aop:after-returning
method="singAfter"
pointcut-ref="questPointcut"
arg-names="bean" />
</aop:aspect>
</aop:config>
Там много чего есть в листинге 1.11, так что давайте разберем его по частям:
- Первое что мы найдем это объявление <bean>, создание бина minstrel в Spring. Это класс Minstrel из листинга 1.10. Minstrel не имеет никаких зависимостей, так что нет необходимости внедрять их куда либо.
- Следующий элемент <aop:config>. Этот елемент указывает, что мы собираемся делать какие-то вещи АОП. Большинство конфигурационных элементов АОП Spring должны содержаться в <aop:config>. Внутри <aop:config> есть элемент <aop:aspect>. Этот элемент показывает что мы объявляем аспект. Функциональные возможности аспекта определены в бине, на который указывает атрибут ref. Таким образом, бин minstrel, который Minstrel (имеется ввиду класс), будет обеспечивать функциональные возможности аспекта
- Аспект состоит из pointcuts - точек внедрения (места, где будут применяться функциональные возможности аспекта) и описание (как применяются функциональные возможности). Элемент <aop:pointcut> определяет pointcut которые вызываются при выполнении метода embarkOnQuest(). (Если вы знакомы с AspectJ, вы можете узнать pointcut как выражения AspectJ синтаксиса.)
- Наконец, есть две части описания AOP. Элемент <aop:before> объявляет что метод singBefore() класса Minstrel должен быть вызван перед pointcut, а элемент <aop:after> объявляет, что метод singAfter()должен быть вызван после pointcut. Pointcut в обоих случаях это ссылка на questPointcut (id="questPointcut"), который является выполнением embarkOnQuest().
Вот и все, что нужно сделать! Мы только что перевели Minstrel в аспект Spring. Не беспокойтесь, если здесь вы чего-то не поняли - вы увидите намного больше примеров по Spring АОП в главе 4 что должно помочь вам понять это. Сейчас есть два важных момента для извлечения из этого примера.
Во-первых, Minstrel по-прежнему POJO — нет ничего о Minstrel, указывающее на то, что он должен использоваться как аспект. Вместо этого Minstrel был превращен в аспект декларативно в контексте Spring.
Во вторых, и более важно, knight больше не нужно напоминать minstrel петь о его подвигах. Как аспект minstrel позаботиться об этом автоматически. На самом деле рыцарю даже не нужно знать о существовании менестреля. Следовательно классу KnightOfTheRoundTable можно вернуться к более простой форме как прежде:
public class KnightOfTheRoundTable implements Knight {
private String name;
private Quest quest;
public KnightOfTheRoundTable(String name) {
this.name = name;
}
public HolyGrail embarkOnQuest() throws QuestFailedException {
return quest.embark()
}
public void setQuest(Quest quest) {
this.quest = quest;
}
}
Используя АОП вести хронику рыцарской деятельности стало веселее. Но АОП Spring-а может использоваться для еще более практичных вещей, чем сочинение нестареющих сонетов о рыцарях. Как вы увидите позже, Spring использует АОП для обеспечения сервисов предприятия
таких как описание транзакций (глава 6) и безопасность (глава 7).
Резюме
Теперь вы должны иметь достаточно хорошее представление о том, что Spring приносит на стол. Spring
стремится сделать разработку enterprise Java проще и поддерживает слабосвязанный код.
Важным для этого является внедрение зависимостей и АОП.
В этой главе мы сделаем маленький тест внедрения зависимости в Spring.
DI это метод связывания объектов приложения так, что объекты не должны знать
откуда их зависимости появляются или как они реализуются. Вместо того, чтобы
получать зависимости от них самих, зависимые объекты получают объекты
которые от них зависят. Так как зависимые объекты знают о своих
внедренных объектах часто только через интерфейсы, связь сохраняется очень слабо.
В дополнение внедрения зависимости мы также видим блестящую поддержку АОП Spring-ом.
АОП позволяет вам сконцентрировать логику, которая обычно бывает разбросана
по всему приложению, в одном месте - аспекте. Когда Spring связывает ваши
бины друг с другом, такие аспекты могут быть вплетены во время выполнения, фактически предоставляя
бинам новое поведение.
Внедрение зависимости и АОП это центр всего в Spring. Таким образом вы
должны понимать, как использовать эти основные функции Spring чтобы иметь возможность использовать
оставшуюся часть платформы. В этой главе мы только прикоснулись к
особенностям DI и AOP Spring-а. В следующих нескольких главах мы углубимся в DI и АОП.
Без лишних слов, давайте перейдем к главе 2, чтобы узнать, как связываются
вместе объекты в Spring используя внедрение зависимости.
------------------------
ТРИО теплый пол отзыв
Заработок на сокращении ссылок
Earnings on reducing links
Код PHP на HTML сайты
Категория: Книги по Java
Комментарии |