Spring IoC - введение

 

Spring framework является первым фреймворком Java в котором появился Di, IoC, от спринга то и появился стандарт в java описывающий как должен работать IoC.

IoC Spring поддерживает несколько методов внедрения.

  1. Через конструктор
  2. Через метод сеттер
  3. Через метод поиска

Мы в этом уроке рассмотрим внедление через конструктор на примере приложения HelloWorld на Spring.

Для начала напишем всем привычный Hello World на Java.

// Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Hello world");
}
}

Обычный hello world, который мы привыкли видеть во всех книгах, мы пойдем другим путем и обсудим для начала этот hello world, его правильность, а затем напишем свой.

Этот Hello world нарушает принципы ООП. Здесь у нас что? Здесь у нас и поставка сообщения и его отображение, по-хорошему надо иметь два класса, MessageProvider и MessageRenderer, первый поставляет сообщения, второй их выводит. Напишем это

// MessageProviderInterface.java

package spring.ioc;

public interface MessageProviderInterface {
String getMessage();
}

// MessageProviderImpl.java

package spring.ioc;

public class MessageProviderImpl implements MessageProviderInterface {
@Override
public String getMessage() {
return "Hello world";
}
}

// MessageRendererInterface.java

package spring.ioc;

public interface MessageRendererInterface {
void render();
}

// MessageRendererImpl.java

package spring.ioc;

public class MessageRendererImpl implements MessageRendererInterface {
private MessageProviderInterface messageProvider;

MessageRendererImpl(MessageProviderInterface messageProvider) {
this.messageProvider = messageProvider;
}

@Override
public void render() {
System.out.println(messageProvider.getMessage());
}
}

// Main.java

package spring.ioc;

public class Main {
public static void main(String[] args) {
MessageProviderInterface msgProvider = new MessageProviderImpl();
MessageRendererInterface msgRenderer = new MessageRendererImpl(msgProvider);

msgRenderer.render();
}
}

Мы объявили два интерфейса, MessageProviderInterface и MessageRendererInterface первый нам поставляет сообщения, второй их выводит. Теперь с точки зрения ООП все гуд. Есть одна проблема, когда приложение будет разростаться, нам придется искать все места где у нас создается MessageRenderer и менять ему провайдера, если нам это понадобится. Решить это можно с помощью фабрики. Давайте ее напишем.

// MessageRendererFactory.java

package spring.ioc;

public class MessageRendererFactory {
public static MessageRendererInterface createRenderer() {
MessageProviderInterface msgProvider = new MessageProviderImpl();
MessageRendererInterface msgRenderer = new MessageRendererImpl(msgProvider);

return msgRenderer;
}
}

// Main.java

package spring.ioc;

public class Main {
public static void main(String[] args) {
MessageRendererInterface msgRenderer = MessageRendererFactory.createRenderer();
msgRenderer.render();
}
}

Теперь вроде все нормально. Но проблемы не закончились, если вдруг мы решим поменять название фабрики? То нам придется опять же везде по проекту искать эту фабрику и менять название, если Вы используете IDE, то проблем это не доставит. Но есть другая, более неприятная проблема, теперь чтобы поддерживать инкапсуляцию логики создания объектов нам надо заводить кучу фабрик, а это по-моему не удобно. Пора использовать Spring IoC.

Давайте создадим конфигурацию для спринга, чтобы он мог загружать нам бины. Создаем файл spring-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.0.xsd">

</beans>

Здесь мы создали основу конфигурации бинов в Spring. Рассказывать здесь особо нечего, если вы используете IDE, я использую IntellijIdea, то вам даже запомнить не стоит эту основу, так как IDE можем вам создать ее сама.

Теперь добавим туда команду для сканирования нашего пакета на аннотации.

<?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">

<context:component-scan base-package="spring.ioc" />

</beans>

Таким должен стать файл spring-context.xml. Здесь мы говорим спрингу, что у нас есть базовый пакет - spring.ioc в котором есть аннотации Spring IoC. Давайте загрузим этот контекст.

// Main.java

package spring.ioc

public class Main {
public static void main(String[] args) {
ApplicationContext appContext = new GenericXmlApplicationContext(
new ClassPathResource("spring-context.xml"));
}
}

Здесь мы указали спрингу что мы используем XML контекст, spring поддерживает несколько контекстов, на основе XML, properties файлов и YAML.

Мы в нашем цикле уроков будем использовать только xml, он является уже неким стандартом.

Далее мы пометим наш MessageRendererImpl и MessageProviderImpl как сервисы с помощью Spring аннтации @Service

// MessageProviderImpl.java

package spring.ioc;

@Service("messageProvider")
public class MessageProviderImpl implements MessageProviderInterface {
@Override
public String getMessage() {
return "Hello world";
}
}
// MessageRendererImpl.java

package spring.ioc;

@Service("messageRenderer")
public class MessageRendererImpl implements MessageRendererInterface {
private MessageProviderInterface messageProvider;

@Autowired
MessageRendererImpl(MessageProviderInterface messageProvider) {
this.messageProvider = messageProvider;
}

@Override
public void render() {
System.out.println(messageProvider.getMessage());
}
}

Здесь мы помечаем наши классы аннотацией @Service("<NAME>"), которая говорит спрингу, что этот класс можно получить через ApplicationContext. По-мимо этой анотации мы здесь использовали аннотацию @Autowired в классе MessageRendererImpl, она указывает на то, что в конструктор должна внедриться зависимость MessageProviderInterface, имя важно! Прошу заметить что у нас в конструкторе класса MessageRendererImpl название переменной messageProvider совпадает с названием сервиса в MessageProviderImpl, это важный момент, так как spring ищет сервисы именно по названию переменных, но это поведение можно изменить, об этом в следующих статьях.

Теперь мы можем получить MessageRenderer из IoC контейнера, для этого напишем следующий код

// Main.java

package spring.ioc;

public class Main {
public static void main(String[] args) {
ApplicationContext appContext = new GenericXmlApplicationContext(
new ClassPathResource("spring-context.xml"));

MessageRendererInterface messageRenderer = appContext.getBean(
"messageRenderer", MessageRenderInterface.class);
messageRenderer.render();
}
}

Если запустить данный код, то на консоле мы увидим "Hello World". Теперь в кратце расскажу что здесь происходит.

Сначала мы объявляем конфигурацию спринга в которой говорим, что у нас есть аннотированный код в проекте, что надо сканировать пакет spring.ioc на аннотации. Далее мы помечаем наши классы аннтацией @Service, это назначает так скажем уникальный id классу по которому его можно потом получить appContext.getBean("<BEAN_BANE>"), либо внедрить. Далее мы внедряем наш MessageProviderImpl в наш MessageRendererImpl через конструктор, конструктор помечаем аннотацией @Autowired, что говорит спрингу о том, что здесь надо внедрить зависимости.

Далее Spring ищет зависимость по названию переменной MessageProviderInterface messageProvider, здесь messageProvider - это id сервиса MessageProviderImpl, именно этот сервис внедрится в MessageRenderer, так как название переменной и id MessageProviderImpl совпадают.

Затем в классе Main методе main мы загружаем контекст приложения из xml файла и просим Spring отдать нам бин messageRenderer (название которое мы писали в аннотации @Service), бин - это все что контролируется Spring IoC. 

Дальше Spring ищет нам этот бин, внедряет все его зависимости и отдает нам. Второй параметр в методе getBean нужен для типизации результата метода getBean, можно было написать так

MessageRendererInterface msgRenderer = (MessageRendererInterface) appContext.getBean("messageRenderer");

На этом все, надеюсь урок был полезен, если код не рабочий или остались вопросы, пишите в комментариях - отвечу.

Так же я в примерах кода не делал import'ы, это сделает за вас IDE, советую писать в IntellijIdea, так как у нее наверное самая хорошая поддержка Spring Framework.

Пожаловаться Подписаться
0 ответов
авторизуйтесь чтобы ответить