Хабрахабр

Введение в Spring, или что делать, если по всему проекту @Autowired и @Component, а вы не понимаете, что это

Приветствую тебя, Хабр!

И, возможно, даже не один раз: знание Spring Framework, как минимум, фигурирует в описаниях множества вакансий для джавистов. Эта статья будет полезна тем, кто уже начал изучать Java и даже успел добиться некоторых успехов в понимании Java Core, и вот услышал слово Spring. Эта статья поможет вам взобраться на самую первую ступеньку: понять общую идею столь популярного фреймворка.

Существует такое понятие как Inversion of Control, по-русски – Инверсия управления, сокращенно – IoC. Начнем издалека. IoC — это делегирование части наших обязанностей внешнему компоненту. IoC — один из принципов, приближающий наш код к слабосвязанности.

Что это такое, название говорит само за себя, так что раскрыть ее я постараюсь на примере. Существуют разные реализации IoC подхода, нас интересует одна из них — Dependency Injection, внедрение зависимостей. Есть классы Shop (магазин) и Seller (продавец). Мы пишем приложение, автоматизирующее работу сети магазинов. Вот мы и столкнулись с зависимостью: Seller зависит от Shop. У класса Seller имеется поле типа Shop — магазин, в котором работает продавец. Есть варианты: Теперь задумаемся, как в объект Seller попадет объект Shop?

  • Внедрить его через конструктор и сразу, при создании продавца, указывать магазин, в котором он работает:

public class Seller
}

  • Создать сеттер и с помощью его вызова устанавливать продавцу магазин:

public class Seller { private Shop shop; public void setShop(Shop shop) { this.shop = shop; }
}

Перечисленные два способа — это реализация Dependency Injection. И, наконец, мы подобрались к спрингу: он предоставляет еще один способ внедрять зависимости.

Существует и Spring MVC для быстрого создания веб-приложений, и Spring Security для реализации авторизации в приложении, и Spring Data для работы с базами данных и еще куча всего. Вообще говоря, Spring — это очень широкий набор библиотек на многие случаи жизни. Spring IoC заслуживает внимания в самом начале изучения библиотек спринга по еще одной причине. Но отдельно стоит Spring IoC — это базовый вид спринга, который реализует изучаемую нами тему — внедрение зависимостей. Как вы увидите в процессе практической работы с другими видами спринга, для всех остальных спрингов Spring IoC используется как каркас.

— bean). Знакомство со Spring IoC начнем с главного термина: бин (англ. Самыми простыми словами,

Бин — создаваемый Spring-ом объект класса, который можно внедрить в качестве значения поля в другой объект.

Хотите словами посложнее? А пожалуйста:

Бин — объект класса, представляющий собой завершенный программный элемент с определенной бизнес-функцией либо внутренней функцией Spring'а, жизненным циклом которого управляет контейнер бинов.

Как вы уже поняли, для того, чтобы в Seller можно было внедрить Shop, Shop должен стать бином. Существует несколько способов рассказать приложению, какие объекты имеют гордое право называться бинами, все они приводят нас к понятию ApplicationContext. ApplicationContext — это сердце спринга. Как правило, он создается в самом начале работы приложения («поднимается») и управляет жизненным циклом бинов. Поэтому его еще называют контейнером бинов.

Каким образом нам необходимо переписать наши классы, чтобы Spring IoC и его слуга ApplicationContext подставили значение поля Shop объекту Seller? Подбираемся к главному. Вот таким:

@Component
public class Shop {
}

@Component
public class Seller { @Autowired private Shop shop;
}

Просто? Куда уж проще! Элегантно? Вполне. Здесь произошло следующее: аннотация Component сказала спрингу, что класс, который ей аннотируем, это бин. Аннотация Autowired попросила Spring в поле, которое она аннотирует, подставить значение. Эта операция называется «инжектнуть» (inject). Какое именно значение будет подставлено? Об этом чуть позже, сначала разберемся, как вообще классы становятся бинами.

Он-то и создает сразу все бины. Мы уже знаем, что в начале работы приложения должен подняться хранитель всех бинов ApplicationContext. Дело в том, что по умолчанию любой бин имеет внутриспринговое свойство scope в значении singleton. Почти все. Он является синглтоном для спринга: при поднятии контекста Spring создаст ровно один объект-бин из указанного класса. Внутриспринговое, так как синглтоном в прямом смысле слова он не является. Если вы хотите изменить такое поведение — пожалуйста, Spring разрешает управлять временем создания бина и их количеством для одного класса, но сейчас не об этом.

Давайте выясним, а собственно где живет контекст и самое главное: как он определяет, из каких классов необходимо создавать бины. Итак, при поднятии ApplicationContext создаются все бины. Вот его пример: Вариантов несколько, для простоты изложения мы поговорим про один из них: конфигурирование с помощью файла xml.

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:beans="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="product" class="main.java.Product"></bean> <context:component-scan base-package="main"/>
</beans>

В этом файле продемонстрирован запуск создания бинов двумя путями. Первый, скажем так, ручной. Видите, здесь есть тег bean с указанием класса. Это и есть бин. Из всего, что прописано в этом файле с тегом bean, будут созданы бины.

Помните, над классами мы поставили аннотацию Component. Второй путь менее многословен. Благодаря этой строке из xml-файла: Из всех классов, аннотированных этой аннотацией, будут созданы бины.

<context:component-scan base-package="main"/>

Она говорит спрингу: просканируй весь пакет main и из всего, над чем будет стоять аннотация Component (или другие аннотации, являющиеся наследниками Component), создай бины. Компактно, не правда ли? Просто говорим, в каких пакетах содержатся классы, из которых нужно создавать бины, и аннотируем эти классы.

Поднять контекст с использованием xml-файла можно следующей строчкой кода:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

где beans.xml — путь к xml-нику, о котором шла речь выше.

Каким же образом Spring заполнит поле Shop при создании Seller’а? С созданием бинов разобрались. Также создается бин-объект класса Seller, он же тоже аннотирован Component. При поднятии контекста создается бин-объект класса Shop. Аннотация Autowired говорит спрингу: в это поле нужно инжектнуть бин. У него есть поле типа Shop, аннотированное Autowired. Он и будет проинжектен в объект Seller, что и требовалось. В нашем случае у нас есть всего один бин, подходящий на эту роль, то есть тип которого совпадает с типом поля: это бин — экземпляр класса Shop. Spring умен, но требует того же и от нас. Я понимаю, сейчас вопросики полезли как червячки: а что будет, если Spring не найдет нужный бин, или найдет несколько подходящих (особенно учитывая, что инжектить можно также по интерфейсу, а не по классу). Нам нужно либо иметь в системе ровно один бин, подходящий под каждый Autowired, либо обучать Spring действиям при таких конфликтах (об этом мы сейчас не будем, вы и так устали, крепитесь, статья подходит к концу).

Если бы он был не бином, а создавался через new, то автоматически бы ничего в него не проинжектнулось. Заметьте, что Seller – это тоже бин.

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

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

Парочка хинтов напоследок:

  • Если вы реализовали проект и сейчас в недоумении, как же получить бин у спринга, чтобы посмотреть на него, сделайте вот так:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Seller seller = (Seller) context.getBean(Seller.class);

Это легальный способ получить бин, хотя в современных реалиях так обычно не делают. Но для учебного примера можно.

  • Поскольку Spring — это фреймворк, необходимо подключить его в свой проект. Я создаю приложение с помощью maven и добавляю в файл pom.xml зависимости spring-core и spring-context.

Spring IoC содержит в себе огромные возможности по созданию, настройке и инжекту бинов. Мы рассмотрели крохотную часть верхушки айсберга, один из способов работы со спрингом, но я надеюсь, что нам удалось окинуть его острым взором и получить общее представление о происходящем.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть