← Назад к вопросам

Какой принцип SOLID реализуется в ApplicationContext?

2.0 Middle🔥 121 комментариев
#SOLID и паттерны проектирования#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

SOLID принципы в Spring ApplicationContext

Основной Ответ

ApplicationContext в Spring Framework реализует несколько SOLID принципов, но главный — это Dependency Inversion Principle (DIP) и Inversion of Control (IoC).

1. Dependency Inversion Principle (DIP)

Это главный принцип, который Spring ApplicationContext воплощает:

Проблема без DIP

// Плохо: класс зависит от конкретной реализации
public class PaymentProcessor {
    private PostgresDatabase database;  // ЖЕСТКАЯ зависимость!
    private StripePaymentGateway gateway;  // ЖЕСТКАЯ зависимость!
    
    public PaymentProcessor() {
        this.database = new PostgresDatabase();  // Создаем сами
        this.gateway = new StripePaymentGateway();  // Создаем сами
    }
    
    public void processPayment(Payment payment) {
        database.save(payment);
        gateway.charge(payment.getAmount());
    }
}

// Проблемы:
// 1. Невозможно протестировать (нет mocks)
// 2. Невозможно поменять реализацию (затвердели на Stripe)
// 3. Класс отвечает за создание зависимостей (Single Responsibility Violation)

Решение с DIP и ApplicationContext

// Хорошо: зависимость от абстракций, а не реализаций
public interface PaymentDatabase {
    void save(Payment payment);
}

public interface PaymentGateway {
    void charge(BigDecimal amount);
}

// Реализации - их может быть несколько
public class PostgresPaymentDatabase implements PaymentDatabase {
    @Override
    public void save(Payment payment) { /* ... */ }
}

public class MongoPaymentDatabase implements PaymentDatabase {
    @Override
    public void save(Payment payment) { /* ... */ }
}

public class StripePaymentGateway implements PaymentGateway {
    @Override
    public void charge(BigDecimal amount) { /* ... */ }
}

public class PayPalPaymentGateway implements PaymentGateway {
    @Override
    public void charge(BigDecimal amount) { /* ... */ }
}

// Класс зависит от АБСТРАКЦИЙ, не реализаций!
@Service
public class PaymentProcessor {
    private final PaymentDatabase database;  // Зависимость от interface!
    private final PaymentGateway gateway;  // Зависимость от interface!
    
    // Spring инжектит зависимости через конструктор
    @Autowired
    public PaymentProcessor(
        PaymentDatabase database,
        PaymentGateway gateway
    ) {
        this.database = database;
        this.gateway = gateway;
    }
    
    public void processPayment(Payment payment) {
        database.save(payment);
        gateway.charge(payment.getAmount());
    }
}

Как ApplicationContext реализует DIP

// Spring конфигурация
@Configuration
public class PaymentConfig {
    
    // Выбираем реализацию в одном месте!
    @Bean
    public PaymentDatabase paymentDatabase() {
        return new PostgresPaymentDatabase();  // Или MongoPaymentDatabase()
    }
    
    @Bean
    public PaymentGateway paymentGateway() {
        return new StripePaymentGateway();  // Или PayPalPaymentGateway()
    }
    
    @Bean
    public PaymentProcessor paymentProcessor(
        PaymentDatabase database,
        PaymentGateway gateway
    ) {
        return new PaymentProcessor(database, gateway);
    }
}

// ApplicationContext управляет всем это!
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(PaymentConfig.class);
        PaymentProcessor processor = context.getBean(PaymentProcessor.class);
        processor.processPayment(new Payment(1000));
        // PaymentProcessor не знает, какую реализацию он получит!
        // Это решает ApplicationContext!
    }
}

2. Inversion of Control (IoC) — главная фишка

IoC означает, что контроль над созданием и управлением объектами передается фреймворку, а не приложению.

Без IoC (старый способ)

public class Application {
    public static void main(String[] args) {
        // МЫ ответственны за создание каждого объекта
        PaymentDatabase db = new PostgresPaymentDatabase();
        PaymentGateway gateway = new StripePaymentGateway();
        PaymentProcessor processor = new PaymentProcessor(db, gateway);
        UserRepository repo = new UserRepository(db);
        UserService service = new UserService(repo, processor);
        
        // Если нужно 50 сервисов — 50 new операций!
        // Если поменялся конструктор — переписываем везде!
    }
}

С IoC через ApplicationContext

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // Spring создает ApplicationContext
        ApplicationContext context = SpringApplication.run(Application.class, args);
        
        // Все бины уже созданы и сконфигурены!
        UserService service = context.getBean(UserService.class);
        // Всё работает, мы только запросили нужный bean
    }
}

// Spring сам обнаружит @Service, @Repository, @Component и создаст их
@Service
public class UserService {
    @Autowired  // Spring инжектит!
    private UserRepository repo;
    
    @Autowired  // Spring инжектит!
    private PaymentProcessor processor;
}

3. Single Responsibility Principle (SRP)

ApplicationContext помогает разделить ответственность:

// Плохо: класс отвечает за слишком много
public class UserController {
    public void createUser(UserRequest req) {
        // Валидация
        if (req.getName().isEmpty()) throw new Exception();
        
        // Создание объекта
        User user = new User(req.getName());
        
        // Сохранение в БД
        Connection conn = DriverManager.getConnection("jdbc:postgres://...");
        PreparedStatement stmt = conn.prepareStatement("INSERT INTO users...");
        stmt.executeUpdate();
        
        // Отправка email
        JavaMailSender sender = new JavaMailSender();
        sender.send(createWelcomeEmail(user));
        
        // Отправка уведомления
        RabbitMQClient client = new RabbitMQClient();
        client.publish("user.created", user);
    }
}

// Хорошо: разделили ответственность, ApplicationContext управляет
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;  // SRP: только HTTP слой
    
    @PostMapping
    public ResponseEntity<UserDTO> createUser(@RequestBody UserRequest req) {
        User user = userService.createUser(req);
        return ResponseEntity.status(201).body(UserDTO.from(user));
    }
}

@Service
public class UserService {
    @Autowired
    private UserRepository repository;  // Репозиторий для БД
    
    @Autowired
    private EmailService emailService;  // Сервис для email
    
    @Autowired
    private EventPublisher eventPublisher;  // Publisher для событий
    
    @Transactional
    public User createUser(UserRequest req) {
        // SRP: только бизнес-логика
        validateUser(req);
        User user = new User(req.getName());
        User saved = repository.save(user);
        
        // Асинхронные задачи
        emailService.sendWelcomeEmail(saved);
        eventPublisher.publish(new UserCreatedEvent(saved));
        
        return saved;
    }
}

@Service
public class EmailService {
    @Autowired
    private JavaMailSender mailSender;  // SRP: только email
    
    public void sendWelcomeEmail(User user) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(user.getEmail());
        message.setSubject("Welcome!");
        message.setText("Hello " + user.getName());
        mailSender.send(message);
    }
}

4. Open/Closed Principle (OCP)

ApplicationContext позволяет легко расширять функционал без изменения кода:

// Interface для платежных систем
public interface PaymentGateway {
    void charge(BigDecimal amount);
}

// Существующие реализации
public class StripePaymentGateway implements PaymentGateway { }
public class PayPalPaymentGateway implements PaymentGateway { }

// Новая реализация — БЕЗ изменения старого кода!
public class CryptoPaymentGateway implements PaymentGateway {
    @Override
    public void charge(BigDecimal amount) {
        // Bitcoin/Ethereum логика
    }
}

// Просто добавляем в конфиг
@Configuration
public class PaymentConfig {
    @Bean
    @ConditionalOnProperty(name = "payment.gateway", havingValue = "crypto")
    public PaymentGateway cryptoGateway() {
        return new CryptoPaymentGateway();
    }
}

5. Liskov Substitution Principle (LSP)

ApplicationContext гарантирует, что любая реализация interface может заменить другую:

// Это работает благодаря LSP
@Service
public class PaymentProcessor {
    @Autowired
    private PaymentGateway gateway;  // Может быть Stripe, PayPal, Crypto
    
    public void process(Payment payment) {
        gateway.charge(payment.getAmount());  // Работает с любой реализацией!
    }
}

Сравнение SOLID принципов в ApplicationContext

ПринципApplicationContextКак реализуется
S (SRP)Да ✓Каждый бин отвечает за одно
O (OCP)Да ✓@ConditionalOnProperty, @Profile для расширения
L (LSP)Да ✓Interface injection гарантирует substitutability
I (ISP)Да ✓Разделение на interface вместо fat beans
D (DIP)Да ✓Главный — зависимость от абстракций, не конкреции

Вывод

Dependency Inversion Principle (DIP) — главный SOLID принцип, который реализуется в Spring ApplicationContext:

  1. Класс зависит от абстракций (interface/abstract class)
  2. ApplicationContext инжектит конкретные реализации
  3. Это позволяет менять реализацию без изменения кода
  4. Это позволяет тестировать с mock-объектами
  5. Это основа всего инверсии управления в Spring

Без DIP нет IoC, без IoC нет Spring. Это фундамент.

Какой принцип SOLID реализуется в ApplicationContext? | PrepBro