Java for Beginner
675 subscribers
558 photos
156 videos
12 files
851 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Принципы Dependency Injection (DI)

Dependency Injection (DI) — один из основных паттернов проектирования, который активно применяется в Spring Framework. Это концепция, которая обеспечивает инверсию управления (Inversion of Control, IoC), позволяя передавать зависимости объектам извне, а не создавать их внутри самого объекта.

Что такое Dependency Injection?
Dependency Injection (внедрение зависимостей) — это процесс передачи зависимостей объекту, который их использует, извне. Зависимостью считается любой объект, который необходим классу для выполнения своей работы. Принцип DI нарушает традиционное создание зависимостей внутри класса (например, через оператор new) и передает управление их созданием IoC-контейнеру Spring.

Таким образом, класс сам по себе не знает и не управляет созданием своих зависимостей. Это делает код более гибким, тестируемым и облегчает его поддержку. Контейнер Spring отвечает за:
Создание объектов (бинов).
Связывание объектов между собой.
Управление жизненным циклом объектов.
Разрешение зависимостей при инициализации объектов.


Пример простой зависимости: допустим, у нас есть класс Car, который зависит от класса Engine. При традиционном подходе мы бы создавали экземпляр Engine внутри Car, что создает сильную связанность между классами. С DI мы можем передать объект Engine извне, обеспечив слабую связанность и улучшив модульность кода.

Принципы Dependency Injection

Основные принципы DI включают:
Инверсия управления (IoC) — изменение направления управления зависимостями. Объекты не управляют своими зависимостями, а получают их извне.
Слабая связанность — классы не зависят от конкретных реализаций своих зависимостей, а используют интерфейсы. Это облегчает замену компонентов и тестирование.
Разделение обязанностей — каждый класс сосредоточен на выполнении своих задач, а создание зависимостей выполняется другим компонентом.
Упрощение конфигурации — зависимости можно легко управлять с помощью XML-конфигураций, аннотаций или Java-кода.


Типы Dependency Injection

В Spring Framework существует два основных способа внедрения зависимостей:
Внедрение через конструктор (Constructor Injection).
Внедрение через сеттер (Setter
Injection).

1. Внедрение зависимостей через конструктор


Constructor Injection — это тип DI, при котором зависимости передаются классу через его конструктор. Данный подход рекомендуется использовать, если зависимость обязательна и объект не может функционировать без нее. В Spring внедрение через конструктор легко реализуется с помощью аннотации @Autowired или через конфигурацию XML.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// Интерфейс для сервиса отправки сообщений
interface MessageService {
void sendMessage(String message);
}

// Реализация MessageService, отправляющая сообщения по email
@Component
class EmailService implements MessageService {
@Override
public void sendMessage(String message) {
System.out.println("Отправка сообщения по Email: " + message);
}
}

// Класс-клиент, зависящий от MessageService
@Component
class MessageProcessor {

private final MessageService messageService;

// Внедрение зависимости через конструктор
@Autowired
public MessageProcessor(MessageService messageService) {
this.messageService = messageService;
}

public void process(String message) {
messageService.sendMessage(message);
}
}


Преимущества внедрения через конструктор:
Обеспечивает неизменяемость объекта, так как все зависимости передаются при создании.
Явно указывает на наличие зависимостей, что улучшает читаемость кода.
Избегает создания объекта с незавершенным состоянием.


Недостатки:
Сложнее работать с опциональными зависимостями, так как все зависимости должны быть переданы через конструктор.

#Java #Training #Spring #Dependency_Injection
2. Внедрение зависимостей через сеттер

Setter
Injection — это тип DI, при котором зависимости передаются через методы-сеттеры после создания объекта. Этот подход удобен для опциональных зависимостей, когда объект может функционировать без определенной зависимости.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// Класс-клиент, зависящий от MessageService
@Component
class NotificationProcessor {

private MessageService messageService;

// Внедрение зависимости через сеттер
@Autowired
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}

public void sendNotification(String message) {
messageService.sendMessage(message);
}
}


Преимущества внедрения через сеттер:
Удобно для опциональных зависимостей.
Позволяет изменить зависимости после создания объекта.
Упрощает работу с большими классами, у которых много зависимостей.


Недостатки:
Может привести к созданию объекта в несогласованном состоянии, если сеттеры не вызываются.
Усложняет тестирование, так как зависимости могут меняться в течение жизненного цикла объекта.
Когда использовать какой тип DI?


Constructor Injection используется, когда:
Зависимость обязательна и необходима для корректной работы объекта.
Необходима неизменяемость объекта.
Количество зависимостей невелико и их можно передать через конструктор.


Setter Injection используется, когда:
Зависимость опциональна.
Зависимость должна изменяться после создания объекта.
Количество зависимостей велико, и передача через конструктор делает код нечитаемым.


Практическое использование аннотаций

В Spring внедрение через конструктор и сеттеры можно легко управлять с помощью аннотации @Autowired:
Для конструктора: @Autowired размещается над конструктором.
Для сеттера:
@Autowired размещается над методом-сеттером.
Кроме того, можно использовать аннотацию
@Required для указания, что определенная зависимость должна быть передана через сеттер, что гарантирует корректную конфигурацию бина.

#Java #Training #Spring #Dependency_Injection
Что выведет код?

Задание на #Dependency_Injection

public class Task181024 {
public static void main(String[] args) {
ServiceA serviceA = new ServiceA(new ServiceB());
serviceA.printMessage();
}
}

interface Service {
void printMessage();
}

class ServiceA implements Service {
private final ServiceB serviceB;

public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}

@Override
public void printMessage() {
System.out.println("Message from " + serviceB.getMessage());
}
}

class ServiceB {
public String getMessage() {
return "ServiceB";
}
}


#Tasks
Spring. Внедрение зависимостей через конструктор

Введение в концепцию Dependency Injection (DI)

Dependency Injection (DI) — это один из основных принципов разработки программного обеспечения, основанный на инверсии управления. Этот принцип позволяет классу не создавать свои зависимости самостоятельно, а получать их извне, обычно через конструктор, сеттеры или интерфейсы. DI способствует слабой связанности компонентов и улучшает тестируемость кода.

Spring Framework построен на основе DI, что делает его важным аспектом для понимания того, как работает этот фреймворк. Внедрение зависимостей через конструктор — это один из самых распространённых и рекомендуемых подходов, так как он позволяет создавать неизменяемые объекты и упрощает тестирование.

Что такое внедрение зависимостей через конструктор?

Внедрение зависимостей через конструктор означает, что зависимости класса передаются ему через его конструктор во время создания объекта. В этом подходе зависимости становятся неизменяемыми после создания объекта, что делает код более безопасным и предсказуемым.

Когда Spring создает объект (или бин), он автоматически передает все необходимые зависимости в конструктор, используя конфигурацию, определённую в XML-файлах или аннотациях.

Пример внедрения зависимостей через конструктор

Допустим, у нас есть класс NotificationService, который зависит от другого класса MessageService. Вот как можно использовать внедрение через конструктор:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class NotificationService {

private final MessageService messageService;

// Внедрение зависимости через конструктор
@Autowired
public NotificationService(MessageService messageService) {
this.messageService = messageService;
}

public void sendNotification(String message) {
messageService.sendMessage(message);
}
}

@Component
public class MessageService {

public void sendMessage(String message) {
System.out.println("Отправка сообщения: " + message);
}
}
В данном примере класс NotificationService получает зависимость MessageService через конструктор. Аннотация @Autowired сообщает Spring, что нужно автоматически внедрить MessageService в этот конструктор.


#Java #Training #Spring #Dependency_Injection_via_constructor
Конфигурация с использованием аннотаций

Spring поддерживает автоматическое сканирование компонентов и конфигурацию бинов с помощью аннотаций, таких как
@Component и @Autowired. Это позволяет избежать необходимости явного определения бинов в XML-файлах.

Создадим основной класс приложения, где Spring контейнер будет автоматически создавать и управлять зависимостями:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// Получаем бин NotificationService
NotificationService notificationService = context.getBean(NotificationService.class);

// Используем сервис для отправки уведомления
notificationService.sendNotification("Привет, Spring!");
}
}


Конфигурация с использованием XML


Кроме аннотаций, Spring также поддерживает конфигурацию бинов через XML-файлы. Вот как можно задать внедрение зависимостей через конструктор в XML-конфигурации:
<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.xsd">

<!-- Определение бина MessageService -->
<bean id="messageService" class="com.example.MessageService"/>

<!-- Определение бина NotificationService с внедрением через конструктор -->
<bean id="notificationService" class="com.example.NotificationService">
<constructor-arg ref="messageService"/>
</bean>
</beans>
При такой конфигурации Spring автоматически передаст бин messageService в конструктор бина notificationService.


Преимущества внедрения через конструктор

Иммутабельность (неизменяемость): Параметры, переданные через конструктор, становятся неизменными, что улучшает безопасность кода.
Явная зависимость: Использование конструктора делает зависимости явными, так как при создании объекта сразу видно, какие зависимости необходимы для его работы.
Тестируемость: Классы, которые используют внедрение через конструктор, проще тестировать с помощью Mock-объектов (например, с помощью фреймворков Mockito или JUnit), поскольку зависимости можно легко передать в тестах.
Невозможность создания "полуинициализированных" объектов: Все зависимости обязательно передаются в момент создания объекта, что исключает возможность использования объекта без всех необходимых ресурсов.


Когда использовать внедрение через конструктор?

Необходимые зависимости: Когда зависимость обязательна для работы класса, её лучше передавать через конструктор. Это гарантирует, что объект не может быть создан без необходимых зависимостей.
Неизменяемые объекты: Если объект не должен изменяться после создания (например, если его состояние зависит от переданных параметров), лучше использовать конструктор для передачи зависимостей.


#Java #Training #Spring #Dependency_Injection_via_constructor
Spring. Внедрение зависимостей через сеттеры

Что такое внедрение зависимостей через сеттеры?

Внедрение через сеттеры — это подход, при котором зависимости передаются в объект с помощью методов-сеттеров. В отличие от внедрения через конструктор, в этом случае зависимости могут быть переданы (или изменены) после создания объекта. Это обеспечивает большую гибкость, особенно если зависимость не обязательна для создания объекта, но может быть добавлена позже.

Spring поддерживает автоматическое внедрение зависимостей через сеттеры с использованием аннотации @Autowired на уровне метода-сеттера или с помощью XML-конфигурации.

Пример внедрения через сеттеры

Давайте рассмотрим пример, где тот же класс NotificationService использует внедрение зависимости через сеттер:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class NotificationService {

private MessageService messageService;

// Внедрение через сеттер
@Autowired
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}

public void sendNotification(String message) {
messageService.sendMessage(message);
}
}

@Component
public class MessageService {

public void sendMessage(String message) {
System.out.println("Отправка сообщения: " + message);
}
}
Здесь MessageService внедряется через метод-сеттер setMessageService() с использованием аннотации @Autowired. В отличие от внедрения через конструктор, здесь зависимость может быть изменена в любое время после создания объекта.



Конфигурация через XML


Если вам необходимо использовать XML-конфигурацию для внедрения зависимости через сеттеры, это можно сделать так:
<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.xsd">

<!-- Определение бина MessageService -->
<bean id="messageService" class="com.example.MessageService"/>

<!-- Определение бина NotificationService с внедрением через сеттер -->
<bean id="notificationService" class="com.example.NotificationService">
<property name="messageService" ref="messageService"/>
</bean>
</beans>
В этом случае Spring вызовет сеттер setMessageService() и передаст бин messageService в качестве зависимости.


#Java #Training #Spring #Dependency_Injection_via_setters
Преимущества и недостатки внедрения через сеттеры

Преимущества:
Гибкость: Внедрение через сеттеры предоставляет большую гибкость, так как зависимости можно изменить или передать после создания объекта.
Удобство для опциональных зависимостей: Если зависимость не обязательна для работы класса и может быть добавлена позже, использование сеттеров становится более подходящим вариантом.
Возможность поздней инициализации: Вы можете инициализировать или менять зависимости в процессе работы приложения.


Недостатки:
Возможность "полуинициализированного" объекта: Так как зависимости могут быть переданы после создания объекта, существует риск того, что объект может быть использован до того, как все зависимости будут установлены.
Явная зависимость менее очевидна: В отличие от внедрения через конструктор, где зависимости видны при создании объекта, здесь необходимо знать, что сеттеры должны быть вызваны после создания объекта, что может быть не так очевидно.


Когда использовать внедрение через сеттеры?

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


Сравнение внедрения через конструктор и через сеттеры

Необходимые зависимости: Внедрение через конструктор лучше подходит для обязательных зависимостей, так как они передаются в момент создания объекта и не могут быть изменены. Сеттеры же подходят для опциональных зависимостей.
Иммутабельность: Конструкторы способствуют созданию неизменяемых объектов, в то время как сеттеры допускают изменение состояния объекта после создания.
Тестируемость: Оба подхода легко тестируются с помощью фреймворков типа Mockito, однако конструкторы делают зависимости более очевидными и уменьшают количество методов для подмены в тестах.



#Java #Training #Spring #Dependency_Injection_via_setters
Что выведет код?

Задача по Spring. Тема: #Dependency_Injection_via_setters и #Dependency_Injection_via_constructor. Сложность средняя.

Подробный разбор через 30 минут!🫡

import org.springframework.context.annotation.*;

@Configuration
class Config2410 {
@Bean
public MyService2410 myService2410() {
return new MyService2410();
}

@Bean
public MyBean2410 setterInjectedBean2410(MyService2410 myService) {
MyBean2410 bean = new MyBean2410();
bean.setMyService2410(myService);
return bean;
}

@Bean
public MyBean2410 constructorInjectedBean2410(MyService2410 myService) {
return new MyBean2410(myService);
}
}

class MyService2410 {
public void serve() {
System.out.println("Service called");
}
}

class MyBean2410 {
private MyService2410 myService;

public MyBean2410(MyService2410 myService) {
this.myService = myService;
System.out.println("Constructor injection");
}

public MyBean2410() {
System.out.println("No-arg constructor");
}

public void setMyService2410(MyService2410 myService) {
this.myService = myService;
System.out.println("Setter injection");
}

public void useService2410() {
myService.serve();
}
}

public class Task241024_2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config2410.class);

MyBean2410 setterBean = context.getBean("setterInjectedBean2410", MyBean2410.class);
MyBean2410 constructorBean = context.getBean("constructorInjectedBean2410", MyBean2410.class);

setterBean.useService2410();
constructorBean.useService2410();

context.close();
}
}


#TasksSpring