Понимание того, как Spring работает за кулисами - Глава I

Если вы немного знакомы с экосистемой Java, вы, должно быть, уже знакомы со Spring и другими дополнительными фреймворками и библиотеками, но знаете ли вы, как она структурирована и как за кулисами управляет всеми нашими bean-компонентами?

Эта статья - первая из небольшой серии, в которой подробно объясняется, как работает Spring и как вы можете улучшить свой ежедневный код. Начнем с основного - IoC.

Инверсия контроля

Это можно считать краеугольным камнем весны. Вначале название не могло быть самоочевидным, поэтому давайте начнем с метафоры:

Вы готовите торт, но толком не знаете, что вам нужно. Итак, исходя из того, что вы видели в некоторых кулинарных телепрограммах, вы будете использовать яйца, муку, масло и молоко. Затем смешайте все ингредиенты и готово! перевод его в синтаксис псевдокода может выглядеть примерно так:

class Cake {
    
    milk
    butter
    eggs
    flour
    
    prepare() {
        milk + butter + eggs + flour
    }
    
}

Хорошо, это годится. Мы контролируем необходимые ингредиенты и должны их предоставлять. Теперь предположим, что вы хотите приготовить тот же торт месяц спустя, но забыли купить молоко.

Это не хорошо, правда? Но что произойдет, если вместо того, чтобы угадывать основные ингредиенты, вы просто следите за квитанцией о бронировании? Теперь в книге указано, что вам нужно для приготовления торта, и вы просто несете ответственность за то, чтобы купить ингредиенты в супермаркете. Теперь книга под контролем, верно? Собственно, это и есть принцип инверсии управления. В переводе на псевдокод это будет примерно так:

class Cake {
    
    Cake(milk, butter, eggs, flour) {
        milk + butter + eggs + flour
    }
    
}

Итак, мы можем возобновить IoC, заставляя сам объект указывать свои собственные зависимости. Следуя примеру, мы могли бы предположить, что Cake является нашим объектом и имеет собственную спецификацию зависимостей на уровне конструктора, поэтому мы не сможем создать новый Cake, если у нас нет всех зависимостей, готовых к использованию. Для их предоставления мы могли:

  • Используйте то, что есть на кухне.
  • Сходи в супермаркет и купи их.

Точно так же работает Spring. Spring - единственный, кому рекомендуется управлять зависимостями объектов, эти зависимости могут быть готовы к использованию (уже созданы в контексте Spring), или ему нужно получить новый и сохранить его в контексте. Не волнуйтесь, если слово контекст сейчас звучит странно, мы подробно расскажем о нем позже. Итак, перевод нашего примера в логику Spring будет примерно таким:

  • Используйте то, что есть на кухне - ›Получите существующий объект из контекста
  • Сходите в супермаркет и купите их - ›Создайте новый объект и сохраните его в контексте.

Итак, каков сам контекст?

Понимание контекста Spring

Сам контекст - это слово, используемое для определения вашего текущего состояния в зависимости от времени, пространства и ситуации. Подобно вам, у приложений также есть контекст, когда они находятся во время выполнения.

Контекст Spring не заменяет ваш контекст приложения, но создает дополнительный уровень для хранения созданных bean-компонентов и всех связей между ними. Он не только существует, но и создается в результате хорошо структурированного процесса, сканирования вашего кода и установления иерархических уровней.

Создание контекста

Контекст приложения создается при запуске приложения. Итак, в этот момент нам нужно подготовить наш код для сканирования инициализатором контекста Spring и позволить фреймворку создать необходимые объекты:

Как видите, инструкции по настройке являются основой для добавления наших POJO (простой старый объект Java) в контекст Spring. Например, у нас есть следующий java-код:

class Cake {
    
    public Cake(Milk milk, Butter butter, List<Egg> eggs, Flour flour) {
        // return prepared cake;
    }
}
class CakeService {
    
    public Cake prepare() {
        Milk milk = new Milk();
        Butter butter = new Butter();
        List<Egg> eggs = new ArrayList<>();
        Flour flour = new Flour();
        
        Cake cake = new Cake(milk, butter, eggs, flour);
        return cake;
    }
}

Мы применяем принцип IoC, но если вы посмотрите дважды, вы увидите, что мы создаем новое молоко, масло, яйца и муку каждый раз, когда вызываем метод подготовки. Поскольку мы уже знаем, что масло всегда будет маслом, молоко всегда будет молоком и так далее с другими ингредиентами, было бы неплохо иметь их готовыми к использованию, когда они нам понадобятся? Пришло время познакомить вас с некоторыми инструкциями по настройке:

@Configuration
class ApplicationConfig {
    
    @Bean
    public Milk milk() {
        return new Milk();
    }
    
    @Bean
    public Butter butter() {
        return new Butter();
    }
    
    @Bean
    public Flour flour() {
        return new Flour();
    }
    
    @Bean
    public List<Egg> eggs() {
        return new ArrayList<Egg>();
    }
    
}

По сути, мы создаем ApplicationConfig класс, помеченный как @Configuration. Эта аннотация будет использоваться Spring, чтобы определить, какие классы нужно сканировать в первую очередь, и исследовать внутренние элементы. Пока не беспокойтесь о том, как выполняется этот процесс, просто имейте в виду, что эта аннотация является отправной точкой для создания контекста Spring.

Теперь, как указывает само название, нам нужно добавить инструкцию по настройке внутри класса. Настоящее значение конфигурации - это объект, хранящийся в контексте Spring.

На этом этапе вы можете создать и назначить настраиваемые атрибуты для каждого bean-компонента (объекта), хранящегося в контексте Spring, например, назначить синий цвет муке. Наконец, @Bean аннотация также будет просканирована и сообщит Spring: Эй, сохраните этот объект в своем контексте для меня, я хотел бы использовать его позже.

Теперь, если мы обновим наш код, используя bean-компоненты, которые мы только что сохранили в нашем контексте Spring, у нас будет что-то вроде:

class Cake {
    private Milk milk;
    private Butter butter;
    private List<Egg> eggs;
    private Flour flour;
    
    @Autowired
    public Cake(Milk milk, Butter butter, List<Egg> eggs, Flour flour) {
        // return prepared cake;
    }
}

Теперь торт определяет свои собственные зависимости и получает их из контекста Spring. Как? Использование аннотации @Autowired. Пока не беспокойтесь об этом, мы рассмотрим процесс автоматического подключения Spring в следующих статьях. К настоящему времени основная цель - понять, что такое контекст Spring и как он создается на высоком уровне.

Следует помнить о некоторых вещах:

  • Избегайте дублирования bean-компонентов в контексте Spring.
  • При необходимости вы можете создать несколько классов конфигурации.
  • Вы можете управлять стратегией хранилища контекста компонента.
  • Контекст приложения также может быть создан через файл xml.

Это основные сведения о том, как работает контейнер Spring. Теперь вы готовы пойти дальше и подготовиться к следующей статье. Я настоятельно рекомендую прочитать:



Дополнительная информация о контексте Spring:

Https://www.baeldung.com/spring-application-context

И дополнительная информация об областях действия Spring bean:

Https://javarevisited.blogspot.com/2012/05/what-is-bean-scope-in-spring-mvc.html

Это первая статья из серии Spring Core, которую я опубликую в этом месяце. Если вы сочтете это полезным, подпишитесь на меня и следите за обновлениями следующих глав:

Обновлять

Доступна следующая глава!



Кроме того, не стесняйтесь высказывать свои сомнения. Я более чем счастлив помочь вам в этом удивительном путешествии.