
Продолжим тему фреймворка 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 на видео ниже:
Добрый день! Подскажите пожалуйста, а если у нас несколько реализаций OderRepository, как Spring’у указывать нужную реализацию? Где-то в конфигах прописывается?
Если в конфигах, то можно ли их менять на «лету», без перезагрузки сервера…..или я как-то не так понимаю работу этой системы?
Заранее спасибо!
Здравствуйте. Если у нас есть несколько реализаций некоего класса или объекта, то можно воспользоваться аннотацией @Qualifier
Для одного OderRepository указываем @Qualifier(«order_repository1») для другого @Qualifier(«order_repository2») для примера. Далее, когда нам нужно внедрить зависимость в какой-то класс. Мы под аннотацией @Autowired добавляем аннотацию @Qualifier(«order_repository1») или @Qualifier(«order_repository2 «) в зависимости от того какой реализацией OderRepository хотим воспользоваться.