-
Notifications
You must be signed in to change notification settings - Fork 56
Вводная
File -> New project -> Maven -> Java Application
"Пробежимся" по окнам:
- Слева вверху - окно с элементами входящими в проект (исходники, другие проекты, тесты, зависимости и т.д)
- Слева внизу - окно свойств
- Справа вверху - окно редактирования
- Справа внизу - окно вывода (вывод maven или консольный вывод приложения)
Описание проекта для Maven располагается в файле pom.xml. Pom.xml - это xml-файл определённой структуры. В этом файле описывается всё, что связано с процессом сборки проекта:
- плагины сборки
- параметры/свойства
- зависимости
- цели сборки
Для начала осмотримся и посмотрим где задаются зависимости:
<dependency>
. . .
</dependency>
Например, зависимость со Spring Framework последней версии задаётся так:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
Maven скачивает зависимости из репозитория. По умолчанию используется репозиторий Maven central http://search.maven.org/#browse , но URL используемого репозитория можно изменить.
NetBeans при работе с проектами Maven обеспечивает автодополнение не только по синтаксису pom, но и по наименованию библиотек, версий и плагинов.
Открываем pom.xml на редактирование и добавляем следующий код, который декларирует зависимость от Spring Framework. Так как версии компонентов spring будут скорее всего одинаковыми, то версию можно вынести в свойства проекта.
<properties>
<spring.framework.version>3.1.2.RELEASE</spring.framework.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.framework.version}</version>
</dependency>
В закладке Зависимости(Dependencies) сразу же после редактирования pom появляются наши зависимости, а также зависимости зависимостей. В случае, если зависимость не появилась, можно нажать правую кнопку мыши и выбрать пункт "Загрузить объявленные зависимости". Кроме того, для зависимостей можно загрузить исходные коды и JavaDoc. NetBeans в данном случае "подхватывает" документацию и исходный код автоматически.
Краеугольным камнем Spring приложения является описание контекста, в котором описаны зависимости между бинами, а также настройки для Spring-приложения.
Создадим XML-файл в NetBeans по адресу src/main/resources/app-context.xml
.
В качестве предметной области возьмём книги и авторов. Объект книги агрегирует объект автора.
У книги можно задать следующие поля:
- Название (
Book::title
) - Автор (
Book::author
) - Количество страниц (
Book::pages
)
Автор характеризуется:
- Именем (
Author::name
) - Возрастом (
Author::age
)
Выберем пункт создать Java-класс в NetBeans.
Исходный код для классов (на первом этапе) будет следующим:
Author.java
public class Author {
protected String name;
@Override
public String toString()
{
return this.name;
}
}
Book.java
package a1s.learn;
public class Book {
protected String title;
protected int pages;
protected Author author;
@Override
public String toString(){
return
this.title
+ " with "
+ this.pages
+ " by "
+ this.author.toString();
}
}
Для поддержки DI добавим ещё зависимости в pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
Отредактируем файл контекста приложения (src/main/resources/app-context.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.1.xsd">
<bean id="author1" class="a1s.learn.Author">
<property name="name" value="Alexander Pushkin" />
</bean>
</beans>
Рассмотрим XML-код чуть-чуть подробнее:
-
<beans />
- корневой элемент для описания bean'ов. У элемента beans заданы настройки пространства имён, что позволит NetBeans проверять корректность файла, а также осуществлять автоподстановку. -
<bean />
- описание бина. Бин характеризуется идентификаторомid
, а также классомclass
. -
<property />
- элемент задаёт настройку поля классаAuthor
. Свойство задаётся именемname
и значениемvalue
. В качестве значений выступают строки, но Spring через механизм property editor'ов и конвертеров может производить преобразование типов из строк к нужному типу.
Таким образом, был объявлен бин author1
типа a1s.learn.Author
и в качестве свойства имя (name
) была установлена строка Alexander Pushkin
.
Контекст в Spring представлен интерфейсом ApplicationContext
. Кроме того, существует ряд реализаций данного интерфейса.
В качестве основы возьмём класс GenericXmlApplicationContext
, которому в качестве аргумента конструктора передаётся название файла (или коллекция имён файлов) с описанием бинов.
По умолчанию Maven собирает проект так, что GenericXmlApplicationContext
будет искать файлы контекста относительно /src/main/resources/
.
Получить бин возможно с помощью метода ApplicationContext::getBean()
, которому идентификатор бина, имя бина или alias-бина, хотя есть возможность создавать бин по типу.
App.java
package a1s.learn;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext genericXmlApplicationContext;
genericXmlApplicationContext = new GenericXmlApplicationContext("app-context.xml");
Author author1=(Author)genericXmlApplicationContext.getBean("author1");
System.out.println(author1.toString());
}
}
Если запустить данное приложение, то на консоль получим следующий вывод:
окт 27, 2012 9:38:18 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 9:38:19 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@1dc0d09: startup date [Sat Oct 27 21:38:19 MSK 2012]; root of context hierarchy
окт 27, 2012 9:38:20 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@f94d3b: defining beans [author1]; root of factory hierarchy
окт 27, 2012 9:38:20 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@f94d3b: defining beans [author1]; root of factory hierarchy
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'author1' defined in class path resource [app-context.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'name' of bean class [a1s.learn.Author]: Bean property 'name' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1396)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:609)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:469)
at org.springframework.context.support.GenericXmlApplicationContext.<init>(GenericXmlApplicationContext.java:71)
at a1s.learn.App.main(App.java:11)
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'name' of bean class [a1s.learn.Author]: Bean property 'name' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1064)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:924)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:58)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1393)
... 12 more
Java Result: 1
Причиной данному поведению является отсутствие метода установки свойства, что приводит к возбуждению исключения. Таким образом, необходимо реализовать метод установки свойства name
через метод Author::setName()
.
Обновлённый файл Author.java будет выглядеть так:
Author.java
package a1s.learn;
public class Author {
protected String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
}
Снова запустим приложение и получим:
окт 27, 2012 6:33:13 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 6:33:13 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@eec35c: startup date [Sat Oct 27 18:33:13 MSK 2012]; root of context hierarchy
окт 27, 2012 6:33:13 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6ace81: defining beans [author1]; root of factory hierarchy
Alexander Pushkin
Вывод соответствует ожиданиям.
Кроме установки свойств через setter'ы, в Sprng можно устанавливать свойства через конструктор.
Объявим новый бин в файле контекста.
app-context.xml
<bean id="author2" class="a1s.learn.Author">
<constructor-arg value="John R.R. Tolkien" />
</bean>
В данном случае мы используем элемент <constructor-arg />
, который настраивает встраивание зависимостей через аргумент конструктора.
Добавим получение бина в App.java
App.java
Author author2=(Author)genericXmlApplicationContext.getBean("author2");
System.out.println(author2.toString());
Попробуем запустить приложение и получим исключение похожее на исключение из предыдущего пункта. Всему виной отсутствие необходимого конструктора в классе Author
.
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'author2' defined in class path resource [app-context.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
Нам необходимо объявить конструктор по умолчанию, а так же конструктор для настройки поля name
.
Author.java
package a1s.learn;
public class Author {
protected String name;
public Author(){
}
public Author(String name){
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
}
После сборки и запуска вывод будет ожидаемо таким:
окт 27, 2012 6:39:56 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 6:39:56 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@15bf05f: startup date [Sat Oct 27 18:39:56 MSK 2012]; root of context hierarchy
окт 27, 2012 6:39:56 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1bd829e: defining beans [author1,author2]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Добавим к нашему классу Author
поле возраст и реализуем установку свойств с помощью constructor based injection. Нам понадобиться реализовать конструктор с 2-мя аргументами возраста и имени.
Author.java
package a1s.learn;
public class Author {
protected String name;
protected int age = 0;
public Author(){
}
public Author(String name){
this.name = name;
}
public Author(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString()
{
if (age == 0) {
return this.name;
} else {
return this.name + " who was "+this.age;
}
}
}
Описание бина в таком случае пусть будет таким:
<bean id="author3" class="a1s.learn.Author">
<constructor-arg value="100" />
<constructor-arg value="Roger Jelyazni" />
</bean>
В таком случае Sping не может выбрать необходимый конструктор о чём и сообщить с помощью исключения.
окт 27, 2012 6:44:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 6:44:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@101f8f4: startup date [Sat Oct 27 18:44:33 MSK 2012]; root of context hierarchy
окт 27, 2012 6:44:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1bd829e: defining beans [author1,author2,author3]; root of factory hierarchy
окт 27, 2012 6:44:34 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1bd829e: defining beans [author1,author2,author3]; root of factory hierarchy
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'author3' defined in class path resource [app-context.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:250)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:609)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:469)
at org.springframework.context.support.GenericXmlApplicationContext.<init>(GenericXmlApplicationContext.java:71)
at a1s.learn.App.main(App.java:11)
Java Result: 1
Spring "понимает" не только позиции аргументов конструктора, но и имена аргументов. Таким образом, для исключения неоднозначности можно поступить одним из следующих методов:
<bean id="author3" class="a1s.learn.Author">
<constructor-arg name="age" value="100" />
<constructor-arg name="name" value="Roger Jelyazni" />
</bean>
или
<bean id="author3" class="a1s.learn.Author">
<constructor-arg index="1" value="100" />
<constructor-arg value="Roger Jelyazni" />
</bean>
В обоих случаях вывод будет корректным:
окт 27, 2012 6:48:00 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 6:48:01 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@101f8f4: startup date [Sat Oct 27 18:48:01 MSK 2012]; root of context hierarchy
окт 27, 2012 6:48:01 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@d75f7a: defining beans [author1,author2,author3]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Roger Jelyazni who was 100
Теперь рассмотрим как реализовать инъекцию одного бина в другой. Реализуем setter'ы всех свойств в классе Book
.
Опишем сеттеры для класса Book
Book.java
package a1s.learn;
public class Book {
protected String title;
protected int pages;
protected Author author;
public void setTitle(String title) {
this.title = title;
}
public void setPages(int pages) {
this.pages = pages;
}
public void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString(){
return
this.title
+ " with "
+ this.pages
+ " by "
+ this.author.toString();
}
}
Опишем бин для книги, который ссылается на созданного автора. Ссылаться на уже описанный бин возможно с помощью свойства ref
, в котором указывается имя бина.
app-context.xml
<bean id="captain_daugher" class="a1s.learn.Book">
<property name="author" ref="author1" />
</bean>
Добавим вывод данных по бину книги в App.java.
App.java
Book captain_daugher=(Book)genericXmlApplicationContext.getBean("captain_daugher");
System.out.println(captain_daugher.toString());
Вывод будет примерно следующим:
окт 27, 2012 6:54:56 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 6:54:57 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@3b96bb: startup date [Sat Oct 27 18:54:57 MSK 2012]; root of context hierarchy
окт 27, 2012 6:54:57 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1efbf25: defining beans [author1,author2,author3,captain_daugher]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Roger Jelyazni who was 100
null with 0 by Alexander Pushkin
Как видно из вывода, мы установили не все свойства класса Book
, что повлияло на вывод. Необходимо проверять, что для класса все зависимости были внедрены, это будет рассмотрено ниже.
Существует краткая форма задания свойств бина. Данная методика подразумевает использование p-namespace вместо элемента <property />
.
Для активации p-namespace необходимо описать использование оного в app-context.xml
.
<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.1.xsd">
В описании элемента <beans />
появилась строка xmlns:p="http://www.springframework.org/schema/p"
, которая активирует использование p-namespace.
Описание бина будет иметь следующий вид:
<bean
id="captain_daugher" class="a1s.learn.Book"
p:title="Captain daughter"
p:pages="153"
p:author-ref="author1"
/>
Рассмотрим задание свйоств чуть-чуть подробнее:
-
p:<name>="<value>"
установить для свойства с именемname
значениеvalue
. Пример:p:title="Captain daughter"
-
p:<name>-ref="<bean>"
установить в качестве значения свойства другой бин. Пример:p:author-ref="author1"
Вывод приложения будет таким:
окт 27, 2012 6:59:44 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 6:59:44 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@3b96bb: startup date [Sat Oct 27 18:59:44 MSK 2012]; root of context hierarchy
окт 27, 2012 6:59:45 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1238cce: defining beans [author1,author2,author3,captain_daugher]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Roger Jelyazni who was 100
Captain daughter with 153 by Alexander Pushkin
Кроме краткой формы для свойств существует краткая форма для инъекций через конструктор (c-namespace).
Для начала укажем Spring на использование c-namespace, добавив строку xmlns:c="http://www.springframework.org/schema/c"
к элементу <beans />
.
<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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
Создадим бин с инъекцией через конструктор с помощью c-namespace.
<bean id="author4" class="a1s.learn.Author" c:age="200" c:name="Alexander Green" />
App.xml
Author author4=(Author)genericXmlApplicationContext.getBean("author4");
System.out.println(author4.toString());
Вывод приложения будет таким:
окт 27, 2012 7:02:20 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 7:02:21 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@24e39f: startup date [Sat Oct 27 19:02:21 MSK 2012]; root of context hierarchy
окт 27, 2012 7:02:21 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5805bc: defining beans [author1,author2,author3,captain_daugher,author4]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Roger Jelyazni who was 100
Captain daughter with 153 by Alexander Pushkin
Alexander Green who was 200
В java есть абстракция для файлов свойств (.properties). Такие файлы можно читать и писать с помощью класса java.util.Properties
.
Spring поддерживает возможность выносить часть настроек в файлы properties и адресоваться к свойствам с использованием выражений SpEL (Spring Expression Language). В такие файлы удобно выносить, например, настройки подключения к БД.
Создадим файл свойств по адресу src/main/resources/authors.properties
authors.properties
authors.prishvin.name=Alexander Prishvin
authors.prishvin.age=60
Необходимо добавить context namespace к файлу контекста.
app-context.xml
<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"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
Добавились следующие строки:
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="...
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd"
Добавим указание, что свойства необходимо читать из соответствующего файла:
<context:property-placeholder location="authors.properties" />
Теперь с использованием выражения ${key}
можно адресоваться к значениям свойств.
Создадим бин с использованием properties placeholders, которые используют SpEL:
<bean id="author5" class="a1s.learn.Author" c:age="${authors.prishvin.age}" c:name="${authors.prishvin.name}" />
Добавим получение бина в App.java.
App.java
Author author5=(Author)genericXmlApplicationContext.getBean("author5");
System.out.println(author5.toString());
Вывод приложения будет следующим:
окт 27, 2012 7:28:21 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 27, 2012 7:28:21 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@1dc0d09: startup date [Sat Oct 27 19:28:21 MSK 2012]; root of context hierarchy
окт 27, 2012 7:28:21 PM org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [authors.properties]
окт 27, 2012 7:28:21 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ed2f0: defining beans [author1,author2,author3,captain_daugher,author4,author5,org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Roger Jelyazni who was 100
Captain daughter with 153 by Alexander Pushkin
Alexander Green who was 200
Alexander Prishvin who was 60
Spring поддерживает возможность проводить инъекции коллекций. Для этого необходимо указать в качестве дочернего для элемента <property />
один из зарезервированных элементов <list />
, <map />
, <props />
, <set />
.
Рассмотрим на примере <list />
.
Объявим класс книг с множеством авторов MultiAuthorBook
.
MultiAuthorBook.java
package a1s.learn;
import java.util.List;
public class MultiAuthorBook {
public List<Author> authors;
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
@Override
public String toString(){
String s="";
for (Author a:authors) {
s+=a.toString()+" ";
}
return s;
}
}
Добавим в контекст создание соответствующего бина:
app-context.xml
<bean id="book2" class="a1s.learn.MultiAuthorBook">
<property name="authors">
<list>
<ref bean="author1" />
<ref bean="author2" />
<bean id="author6" class="a1s.learn.Author" c:age="120" c:name="Vladimir Lenin" />
</list>
</property>
</bean>
В данном примере для свойства MultiAuthorBook::authors
декларируем список из 3-х бинов(2-ссылок на другие бины и один новый бин).
-
<ref bean="author1" />
- ссылка на бин author1 -
<bean id="author6" class="a1s.learn.Author" c:age="120" c:name="Vladimir Lenin" />
- создание новго бина
Осталось только реализовать код для вызова метода toString
у бина.
App.java
MultiAuthorBook book2=(MultiAuthorBook)genericXmlApplicationContext.getBean("book2");
System.out.println(book2.toString());
Запускаем и видом на выводе:
окт 28, 2012 10:31:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [app-context.xml]
окт 28, 2012 10:31:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericXmlApplicationContext@1ac8210: startup date [Sun Oct 28 22:31:33 MSK 2012]; root of context hierarchy
окт 28, 2012 10:31:33 PM org.springframework.core.io.support.PropertiesLoaderSupport loadProperties
INFO: Loading properties file from class path resource [authors.properties]
окт 28, 2012 10:31:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1bdc523: defining beans [author1,author2,author3,captain_daugher,author4,author5,book2,org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0]; root of factory hierarchy
Alexander Pushkin
John R.R. Tolkien
Roger Jelyazni who was 100
Captain daughter with 153 by Alexander Pushkin
Alexander Green who was 200
Alexander Prishvin who was 60
Alexander Pushkin John R.R. Tolkien Vladimir Lenin who was 120
Вывод оказался ожидаемым и последней строкой перечислены авторы книги с множеством авторов.
В spring имеется возможность выполнения некоторых действий в зависимости от стадии жизненного цикла бина. Наиболее важными событиями является события после инициализии свойств и перед удалением объекта.
Рассмотрим 3 способа "подцепиться" к этим событиям:
- реализовать интерфейсы
InitializingBean
иDisposableBean
- использование аннотаций
@PostConstruct
и@PreDestroy
- добавление указания на соответствующие методы в XML-описании
Пример вызывающего класса App будет выглядеть так:
package a1s.learn;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App
{
public static void main( String[] args ) throws InterruptedException
{
GenericXmlApplicationContext genericXmlApplicationContext;
genericXmlApplicationContext = new GenericXmlApplicationContext("app-context.xml");
Book b3=(Book)genericXmlApplicationContext.getBean("book3");
System.out.println(b3.toString());
genericXmlApplicationContext.destroy();
}
}
Реализуем интерфесы InitializingBean
и DisposableBean
классом Book
.
package a1s.learn;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Book implements InitializingBean, DisposableBean {
...
public void afterPropertiesSet() throws Exception {
System.out.println("Post construct method invoked");
}
public void destroy() throws Exception {
System.out.println("Before destroy method invoked");
}
}
При запуске приложения в консоль будут выведены строки Post construct method invoked
и Before destroy method invoked
, что свидетельствует о вызове соответствующих методов.
Для начала добавим в app-context.xml указание, что необходимо обрабатывать аннотации:
app-context.xml
<context:annotation-config />
Добавим методы и аннотируем их с помощью аннотаций @PostConstruct
и @PreDestroy
package a1s.learn;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class Book {
...
@PostConstruct
protected void beforeConstruct()
{
System.out.println("Post construct method invoked");
}
@PreDestroy
protected void beforeDestroy()
{
System.out.println("Before destroy method invoked");
}
}
Запуск приложения приводит к таким же результатам, как и предыдущий пример.
app-context.xml
<bean id="book3" class="a1s.learn.Book" init-method="beforeConstruct" destroy-method="beforeDestroy">
<property name="author" value="SomeAuthor:20" />
</bean>
Book.java
package a1s.learn;
public class Book {
...
protected void beforeConstruct()
{
System.out.println("Post construct method invoked");
}
protected void beforeDestroy()
{
System.out.println("Before destroy method invoked");
}
}
Аннотации удалены, в XML-файл контекста добавлены настройки соответствующих методов через атрибуты init-method
и destroy-method
для бина.
Если запустить приложение, то можно убедиться, что результат соответствует 2-м предыдущим примерам.
XML-файлы контекста поддерживают возможность импорта данных из стороннего XML-файла.
Например, можно рганизовать структуру XML-файлов контекста:
- app-context-xml - основной файл контекста
- dao.xml - подключаемый файл описания DAO
- domain.xml - подлкючаемый файл с объектами предметной области
Вставка файлов осуществляется с помощью элемента <import />
Скопируем из app-context.xml
описание первых 3-х бинов в файл imported.xml
и заменим на импорта соответствующего файла <import resource="imported.xml" />
app-context.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<import resource="imported.xml" />
<bean
id="captain_daugher" class="a1s.learn.Book"
p:title="Captain daughter"
p:pages="153"
p:author-ref="author1"
/>
<bean id="author4" class="a1s.learn.Author" c:age="200" c:name="Alexander Green" />
<bean id="author5" class="a1s.learn.Author" c:age="${authors.prishvin.age}" c:name="${authors.prishvin.name}" />
<bean id="book2" class="a1s.learn.MultiAuthorBook">
<property name="authors">
<list>
<ref bean="author1" />
<ref bean="author2" />
<bean id="author6" class="a1s.learn.Author" c:age="120" c:name="Vladimir Lenin" />
</list>
</property>
</bean>
<context:property-placeholder location="authors.properties" />
</beans>
imported.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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="author1" class="a1s.learn.Author">
<property name="name" value="Alexander Pushkin" />
</bean>
<bean id="author2" class="a1s.learn.Author">
<constructor-arg value="John R.R. Tolkien" />
</bean>
<bean id="author3" class="a1s.learn.Author">
<constructor-arg name="age" value="100" />
<constructor-arg name="name" value="Roger Jelyazni" />
</bean>
</beans>
Если запустить приложение, то вывод не изменится, что свидетельствует о правильности подключения бинов.
Для внедрения зависимостей с использованием аннотаций в коде используются следующие аннотации;
-
@Resource
- внедение бина по имени -
@Service
- описание бина -
@Value
- внедрение значения
Для активации возможности внедрения бинов через аннотации необходимо в XML-файле контекста указать, что будет использоваться внедрение через аннотации. Для этого необходимо добавить элемент <context:component-scan />
с указанием пакета, в котором будет осуществлён поиск классов с аннотациями.
Добавить элемент <context:component-scan />
в файл контекста app-context.xml
.
app-context,xml
<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"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
. . .
<context:component-scan base-package="a1s.learn" />
. . .
</beans>
В качестве атрибута base-package
для элемента <context:component-scan />
указывается пакет, в котором будет осуществлён поиск классов с аннотациями.
В таком случае можно переписать классы на использование аннотаций, для внедрения зависимостей: Book.java
package a1s.learn;
import javax.annotation.Resource;
public class Book {
protected Author author;
@Resource(name="author1")
public void setAuthor(Author author) {
this.author = author;
}
@Override
public String toString(){
return "Book with author "+author.toString();
}
}
Author.java
package a1s.learn;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service("author1")
public class Author {
@Value("Alexander Pushkin")
protected String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString()
{
return "author is "+name;
}
}
Вывод будет вполне ожидаемым:
Book with author author is Alexander Pushkin
Бывают ситуации, когда бину необходим контекст из которого он был попрождён. Например, бин выполнеят получение зависимостей путём вызова метода ApplicationContext::getBean()
.
Для внедрения контекста необходимо реализовать интерфейс ApplicationContextAware
.
package a1s.learn;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Book implements ApplicationContextAware {
...
protected ApplicationContext ctx;
@Override
public String toString(){
return
this.title
+ " with "
+ this.pages
+ " by "
+ this.author.toString()
+ " with context "+ ctx.getDisplayName();
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
}
Для внедрения имени бина в объект необходимо реализовать интерфейс BeanNameAware
.
Реализуем данный интерфейс для класса book.
package a1s.learn;
import org.springframework.beans.factory.BeanNameAware;
public class Book implements BeanNameAware {
...
protected String name;
...
@Override
public String toString(){
return
this.title
+ " with "
+ this.pages
+ " by "
+ this.author.toString()
+ " from bean "+ name;
}
public void setBeanName(String name) {
this.name = name;
}
}
Если запустить приложение, то мы увидим, что bean указывает своё имя при выводе на консоль.
null with 0 by SomeAuthor who was 20 but log null from bean book3