Читайте также: |
|
Под контейнером объектов в рамках лекции будем подразумевать некоторый компонент, реализующий шаблон ООП типа Assembler (или сборщик объектов). Иными словами, контейнер объектов отвечает за создание экземпляров объектов, поиск нужных экземпляров объектов, и связь этих экземпляров друг с другом, согласно модели структурных связей (создание и инициализация коллабораторов). Т.е. фактически контейнер объектов отвечает за создание и управление жизненным циклом графа объектов системы.
В ООП важным моментом при проектировании классов и систем состоящих из них является проектирование классов как можно более независимыми друг от друга (low-coupling). Положительным следствием такого принципа является относительная лёгкость взаимозамены одной реализации класса другой, при условии что оба класса реализуют общий интерфейс. Этим же продиктована и рекомендация проектирования классов и их взаимодействий на основании интерфейсов. Т.е. при разработке рекомендуется избегать прямого обращения к конкретной реализации класса и использовать вместо этого общий интерфейс. Например при использовании коллекций в Java, вместо конкретных реализаций рекомендуется использовать интерфейсы, так вместо прямого обращения к ArrayList рекомендуется использовать интерфейс List или ещё лучше – интерфейс Collection. Это позволит развязать зависимые реализации классов и подставить при необходимости, например вместо ArrayList LinkedList.
Напомними основные обпределение IoC/DI:
Collaborator – объект или компонент, участвующий в структурных связях и взаимодействиях с целевым объектом (под целевым объектом в данном контексте понимается рассматриваемый класс или объект).
Dependency Injection – техника внедрения объектов Collaborator’ов в целевые объекты извне, т.е. при помощи дополнительных классов, выступающих в роли Assembler’ов.
Dependency Injection позволяет избавиться от необходимости жёстко кодировать реализацию конкретного интерфейса непосредственно в целевом классе.
SpringFramework состоит из набора функциональный групп организованных в примерно 20 модулей. Модули сгруппированы в:
Интерфейс org.springframework.context.ApplicationContext представляет для программиста IoC контейнер и отвечает за создание, конфигурирование и сборку графов объектов, которые используются в приложениях. Контейнер получает инструкции о том какие объекты и в каких связях создавать с помощью конфигурационных механизмов. Конфигурация может быть представлена тремя способами
SpringFramework поставляется сразу с несколькими уже готовыми реализациями интерфейса ApplicationContext:. ClassPathXmlApplicationContext FileSystemXmlApplicationContext. Первый предназначен для загрузки конфигурации контекста (графа объектов) из classpath, второй для загрузки из файловой системы.
В большинстве случаев от программиста не требуется написание какого либо кода для того чтобы инициализировать контекст. Например, работая с web-приложением, программист может воспользоваться специальным фильтром или сревлетом, входящим в комплект поставки SpringFramework, для того, что бы инициализировать контекст их XML файла. Для этого необходимо в конфигурационном файле web.xml указать:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> </web-app> |
При этом файл с конфигурацией контекста следует положить в /WEB-INF/applicationContext.xml
Тем не менее, если речь идёт о простом консольном приложении, работающем внеконтекста (например Web-контейнера), то программисту, для инициализации контекста и получения доступа к нему необходимо в программе сделать следующее:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext1.xml", "applicationContext2.xml"}); |
При этом, если файлов с описанием конфигурации контекста больше одного, они могут быть перечислены через запятую. В данном примере, так как используется ClassPathXmlApplicationContext, то искомые файлы будут загружаться с помощью механизма class loader’а. Т.е. по сути должны быть доступны в classpath.
XML файл конфигурации контекста имеет определённую структуру:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions go here --> </beans> |
Id задаёт идентификатор бина в контексте, а class – задаёт полное имя Java класса, который используется для создания бина.
В процессе работы над приложением, при увеличении его размера, размер XML файла может увеличиваться до размеров неудобных для работы. SpringFramework позволяет разделять конфигурацию контекста на несколько файлов. Это так же бывает полезно при логическом разделении конфигурации контекста (например разные файлы для разных подсистем или архитектурных слоёв – один для сервисов, другой для DAL и т.п.):
<beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> </beans> |
Для этого используется элемент <import>. При этом в качестве имени импортируемого файла с контекстом принимается либо абсолютное (если имя начинается с “/”), либо относительное. Абсолютность или относительность сопоставляются в зависимости от типа контекста: если это файловый контекст – то речь идёт о файлах, а если о classpath, то о ресурсах доступных через механизм classloader.
Чтобы создать контекст из файла конфигурации находящимся в classpath можно использовать конструкцию:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); |
Далее, для получение экземпляра бина из контекста по имени можно использовать метод getObject():
SequenceGenerator generator =(SequenceGenerator) context.getBean("sequenceGenerator"); |
При этом весь код Main-класса будет выглядеть следующим образом:
package com.apress.springrecipes.sequence;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SequenceGenerator generator = (SequenceGenerator) context.getBean("sequenceGenerator"); System.out.println(generator.getSequence()); System.out.println(generator.getSequence()); }} |
Для начала надо создать класс, на основе которого будет создаваться бин:
package com.apress.springrecipes.sequence;public class SequenceGenerator { private String prefix; private String suffix; private int initial; private int counter; public SequenceGenerator() {} public SequenceGenerator(String prefix, String suffix, int initial) { this.prefix = prefix; this.suffix = suffix; this.initial = initial; } public void setPrefix(String prefix) { this.prefix = prefix; } public void setSuffix(String suffix) { this.suffix = suffix; } public void setInitial(int initial) { this.initial = initial; } public synchronized String getSequence() { StringBuffer buffer = new StringBuffer(); buffer.append(prefix); buffer.append(initial + counter++); buffer.append(suffix); return buffer.toString(); }} |
Далее необходимо создать XML файл с конфигурацией контекста:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean name="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix"> <value>30</value> </property> <property name="suffix"> <value>A</value> </property> <property name="initial"> <value>100000</value> </property> </bean></beans> |
Важно отметить, что элементы <propperty> позволяют задавать значения в экземпляре класса. Это даёт возможность гибко конфигурировать экземпляры классов в конкретном контексте. Важно так же заметить, что для успешного задания полей класса, класс должен корректно, в соответвии с договорённостями об именовании методов и полей, правильно задать методы set и get для полей/свойств класса. Эти договорённости описаны в спецификации Java Beans Specification. Коротко, они задают именования полей класса должны начинаться с маленькой буквы, camelStyle, а имена методов доступа к полям должны префиксоваться get-, set-, is-. Т.е. если поле называется myField, то методы должны называться соответвенно setMyField(), getMyField(). В том случае, если поле имеет тип Boolean, то допускается вместо названия метода get- префиксовать is-, например isMyField().
В таком случае, SpringFramework, используя механизм reflection устанавливает поля в заданные значения.
Интересным и удобным является то, что, неспотря на строковое представление значений, подставляемых в поля класса в XML файле конфигурации контекста, если тип поля не строка, а например int, то происходит автоматическая конвертация. Т.е. поле initial, в примере будет правильно инициализировано числовым значением. Это так же даостигается с использованием механихма reflection. Spring определяет тип, который ожидает метод set- и пытается привести строковое представление значения в файле к этому типу. Для этого Spring использует механизм PropertyEditor’ов или конвертеров. Для каждого типа можно зарегистрировать свой конвертер.
Вышеприведённый пример можно сократить, используя более компактную нотацию XML файла конфигурации:
<bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <property name="prefix" value="30" /> <property name="suffix" value="A" /> <property name="initial" value="100000" /></bean> |
Более того, начиная с версии 2.0, когда в Spring стали доступны механизмы расширения синтаксиса путём подключения дополнительных схем, стал доступен ещё более краткий способ:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator" p:prefix="30" p:suffix="A" p:initial="100000" /></beans> |
Всё тоже самое доступно и с помощью задания свойств через параметры конструктора:
<bean name="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <constructor-arg> <value>30</value> </constructor-arg> <constructor-arg> <value>A</value> </constructor-arg> <constructor-arg> <value>100000</value> </constructor-arg></bean> |
<bean name="sequenceGenerator" class="com.apress.springrecipes.sequence.SequenceGenerator"> <constructor-arg value="30" /> <constructor-arg value="A" /> <constructor-arg value="100000" /></bean> |
Таким образом мы видим, что SpringFramework достигает одной из основных целей: non-intrusive approach, т.е. подход с минмальным вмешательством в структуру кода. Т.е. если класс уже создан и у него есть конструктор с параметрами, Spring позволяет его использовать вполне естественно. Если класс использует поля с типами отличными от строк, Spring автоматически производит конвертацию, не вынуждая перепроектировать класс. Для того чтобы стать бином, класс не должен быть каким либо специальным образом модифицирован.
Дата добавления: 2015-10-24; просмотров: 192 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Экстремальное Программирование (Extreme Programming) | | | Область видимости бина |