Одним из наиболее значимых принципов объектно-ориентированного программирования является инверсия управления или принцип инверсии управления. Так же этот принцип в шутку назван «Голливудским принципом», смысл которого раскрывается в определении: «Не звоните нам, мы сами вам позвоним». Данный принцип позволяет существенно снизить связанность между компонентами программного обеспечения, а также изменить процесс его разработки таким образом, чтобы сторонние фреймворки могли использовать компоненты, разрабатываемые другими разработчиками.
Если мы представим себе процесс разработки какого-нибудь проекта, например REST-сервиса, с чистого листа без использования каких-либо фреймворков, то выглядеть он будет следующим образом. Первым делом мы реализуем HTTP-сервер, который будет принимать входящие запросы и маршрутизировать их в компоненты, способные их обработать.
Однако давайте зададим себе вопрос: «А когда в последний раз мы писали собственный HTTP-сервер для REST-сервиса?», наверняка большинство из вас ответит: «Никогда!». Вместо этого мы используем какие-то готовые решения для обслуживания HTTP-запросов, к которым относятся Java EE/Jakarta EE Servlet API, Netty, Spring Framework, Helidon, Micronaut и многие другие фреймворки. Таким образом мы отдаём контроль над потоком исполнения кода сторонним фреймворкам, разрабатывая только специфичные для наших задач компоненты, которые должны быть использованы фреймворками. Именно в передаче контроля над потоком исполнения кода фреймворкам является инверсией управления: фреймворки управляют исполнением кода нашего приложения, а не приложение управляет фреймворками.
Основными способами реализации инверсии управления являются шаблоны проектирования «Шаблонный метод», «Стратегия» и «Локатор служб». Так Servlet API реализует как раз шаблон проектирования «Стратегия», предоставляя интерфейс Servlet
, в реализации которого разработчик может разместить свой код для обработки HTTP-запроса. Spring Framework и проекты, входящие в экосистему Spring, тоже широко используют шаблон проектирования «Стратегия»: в интерфейсе HandlerFunction
— для обработки HTTP-запросов в функциональном стиле, в UserDetailsService
— для получения информации об аутентифицирующемся пользователе и т.д.
В этой короткой статье я не стану углубляться в детали реализаций, так как каждую из них нужно рассматривать по отдельности и подробно, так что это всё будет в следующих статьях.