Продолжим тему фреймворка Spring и сегодня рассмотрим spring dependency injection или внедрение зависимостей как его еще иногда называют. Для того, чтобы разобраться как работает внедрение зависимостей в данном java фреймворке нужно разобрать понятие Inversion of Control (далее IoC) инверсия управления.
IoC — принцип объектно-ориентированного программирования, который предназначен для уменьшения связанности модулей и классов программы. Данный принцип входит в пятерку SOLID принципов. Если не вдаваться в подробности, то в нем говориться что модули верхних уровней не должны зависеть от модулей нижних уровней. Данные модули должны зависеть от абстракций. А если еще проще, то нужно пытаться программировать на уровне абстракций, а не реализаций. Тогда, когда Ваша реализация будет меняться, ее изменения не затронут верхние уровни.
Dependency injection (DI) — как раз и есть одной из реализаций IoC. Spring Framework имеет очень отличную поддержку DI. Как раз ее реализацию в Spring сейчас на примере и посмотрим.
Мы уже программировали на уровне абстракций. В статье Spring MVC первое веб приложение мы предоставляли доступ к нашей сущности Order через интерфейс OrderRepository и его реализацию OrderRepositoryImpl. Далее мы использовали OrderRepository в сервис слое и уже OrderService мы предоставили как конечную точку доступа к сущности Order.
На первый взгляд такая архитектура кажется избыточной, но если рассмотреть подробнее — ее реализация очень удобная в случае добавления или изменения функционала. Допустим Вы решили, что теперь будете использовать хранение данный в базе данных. Для этого Вам не нужно менять сервис и его использование. Достаточно поменять только класс OrderRepositoryImpl. Для тех, кто не в курсе, что это за класс:
-
package com.javamaster.repository;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
import com.javamaster.domain.Order;
-
-
public class OrderRepositoryImpl implements OrderRepository {
-
-
private List<Order> orders = new ArrayList<Order>();
-
-
public OrderRepositoryImpl() {
-
Order order = new Order();
-
order.setId(1);
-
order.setPrice(234d);
-
order.setTitle("Pizza peperoni");
-
Order order2 = new Order();
-
order2.setId(2);
-
order2.setPrice(123d);
-
order2.setTitle("Sushi Philadelfia");
-
orders.add(order);
-
orders.add(order2);
-
}
-
-
public void save(Order order) {
-
orders.add(order);
-
}
-
-
public void delete(Order order) {
-
orders.remove(order);
-
}
-
-
public List<Order> getAll() {
-
return orders;
-
}
-
-
return orders.get(id);
-
}
-
}
Код выше — простой DAO класс для стандартных операций над сущностью. Здесь для примера хранение происходило в списке. Если Вы решите изменить хранение данных на другую систему, поменять нужно будет только класс выше. Наш сервис ничего не знает о реализации. Он просто хранит и предоставляет методы.
Теперь о том, как реализована DI в Spring. Для того, чтобы Spring мог делать внедрения в классе есть три базовые аннотации: @Component, @Service, @Repository.
Эти аннотации нужно добавить над теми классами, которые Вы хотите внедрить в зависимости. На примете простого Spring MVC приложения попробуем реализовать данные аннотации.
Класс OrderRepositoryImpl пометим аннотацией @Repository:
-
package com.javamaster.repository;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
import org.springframework.stereotype.Repository;
-
-
import com.javamaster.domain.Order;
-
-
public class OrderRepositoryImpl implements OrderRepository {
-
-
private List<Order> orders = new ArrayList<Order>();
-
-
public OrderRepositoryImpl() {
-
Order order = new Order();
-
order.setId(1);
-
order.setPrice(234d);
-
order.setTitle("Pizza peperoni");
-
Order order2 = new Order();
-
order2.setId(2);
-
order2.setPrice(123d);
-
order2.setTitle("Sushi Philadelfia");
-
orders.add(order);
-
orders.add(order2);
-
}
-
-
public void save(Order order) {
-
orders.add(order);
-
}
-
-
public void delete(Order order) {
-
orders.remove(order);
-
}
-
-
public List<Order> getAll() {
-
return orders;
-
}
-
-
return orders.get(id);
-
}
-
}
Класс OrderServiceImpl соответственно аннотацией @Service:
-
@Service
-
public class OrderServiceImpl implements OrderService {
Теперь, чтобы использовать реализацию OrderRepository нужно в сервисе воспользоваться аннотацией @Autowired. Теперь в сервисе вместо private OrderRepository orderRepository = new OrderRepositoryImpl(); будет
-
@Autowired
-
private OrderRepository orderRepository;
Теперь, чтобы использовать сервис в контроллере можно заменить строку: private OrderService orderService = new OrderServiceImpl(); на
@Autowired
private OrderService orderService;
Spring сам найден нужную реализацию выше указанных интерфейсов.
Аннотации @Component, @Service, @Repository всего лишь обозначают для спринг, что это реализации соответствующих интерфейсов.
В данной статье не будет рассмотрено внедрение зависимостей через конструкторы и сеттеры. Я считаю, что непосредственное внедрение зависимостей (на примере выше) вполне достаточно для начального уровня изучающих Spring и дальнейшее усложнение может запутать читателя.
Еще пример dependency injection на видео ниже: