Знаете ли вы эти вопросы о внедрении зависимостей Spring?

Spring
Знаете ли вы эти вопросы о внедрении зависимостей Spring?

предисловие

Содержание этой главы в основном состоит в том, чтобы обсудить некоторые моменты знаний о внедрении зависимостей в процессе разработки Spring. Заинтересованные читатели могут сначала рассмотреть следующие вопросы:

  • @Autowired, @Resource, @InjectРазница между тремя аннотациями
  • когда вы используете@Autowired, было ли когда-нибудьField injection is not recommendedпредупреждение? Ты знаешь почему?
  • Каковы способы внедрения зависимостей Spring? Какова официальная рекомендация по использованию?

Если вы знаете все о вышеперечисленных проблемах, то я лично считаю, что ваш опыт разработки должен быть хорошим👍.

Ниже мы по очереди ответим на вышеперечисленные вопросы и подытожим полученные знания.

@Autowired, @Resource, @InjectРазница между тремя аннотациями

Spring поддерживает использование@Autowired, @Resource, @InjectТри аннотации для внедрения зависимостей. Давайте представим разницу между этими тремя аннотациями.

@Autowired

@AutowiredАннотации, предусмотренные для среды Spring, необходимо импортировать пакетыorg.springframework.beans.factory.annotation.Autowired.

Вот пример кода для простого объяснения:

public interface Svc {

    void sayHello();
}

@Service
public class SvcA implements Svc {

    @Override
    public void sayHello() {
        System.out.println("hello, this is service A");
    }

}

@Service
public class SvcB implements Svc {

    @Override
    public void sayHello() {
        System.out.println("hello, this is service B");
    }

}

@Service
public class SvcC implements Svc {

    @Override
    public void sayHello() {
        System.out.println("hello, this is service C");
    }
}

Тестовый класс:

@SpringBootTest
public class SimpleTest {

    @Autowired
    // @Qualifier("svcA")
    Svc svc;

    @Test
    void rc() {
        Assertions.assertNotNull(svc);
        svc.sayHello();
    }

}

Порядок сборки:

  1. в соответствии сtypeНайдите соответствующий компонент в контексте

    查找type为Svc的bean
    
  2. Если есть несколько бобов, следуйтеnameсоответствовать

    1. Если есть@Qualifierобратите внимание, согласно@Qualifierназначенныйnameсоответствовать

      查找name为svcA的bean
      
    2. Если нет, сопоставьте по имени переменной

      查找name为svc的bean
      
  3. Если они не совпадают, будет сообщено об ошибке. (@Autowired(required=false), если установленоrequiredзаfalse(по умолчаниюtrue), в случае сбоя инъекции не будет выдано никаких исключений)

@Inject

В весенней среде,@Injectи@AutowiredТакие же, потому что их внедрение зависимостей выполняется с использованиемAutowiredAnnotationBeanPostProcessorиметь дело с.

@Injectспецификация, определенная JSR-330, если используется таким образом, переключитесь наGuiceэто тоже хорошо.

Guice — это облегченный DI-фреймворк с открытым исходным кодом от Google.

Если вы настаиваете на различии между ними, прежде всего@InjectОн находится в пакете Java EE и должен быть представлен отдельно в среде SE. Другое отличие состоит в том, что@Autowiredможно установитьrequired=falseи@Injectне имеет этого свойства.

@Resource

@Resourceявляется аннотацией, определенной JSR-250. ВеснаCommonAnnotationBeanPostProcessorправильно понялJSR-250обработка аннотаций, в том числе@Resource.

@ResourceЕсть два важных свойства:nameиtype, а весна будет@Resourceаннотированныйnameсвойства разрешаются в имя компонента, аtypeСвойство разрешается в тип bean-компонента.

Порядок сборки:

  1. Если также указаноnameиtype, единственный соответствующий bean-компонент находится в контексте Spring для сборки, и если он не найден, генерируется исключение.
  2. если указаноname, затем ищите компонент с совпадающим именем (идентификатором) в контексте сборки и выдавайте исключение, если он не найден.
  3. если указаноtype, из контекста для сборки найден единственный bean-компонент с соответствующим типом, если он не найден или найдено более одного, будет выдано исключение.
  4. Если ни то, ни другое не указаноname, без указанияtype, то по умолчаниюbyNameсборка; если нет совпадения, следуйтеbyTypeСобрать.

ИДЕЯ СоветыField injection is not recommended

При использовании IDEA для разработки Spring, когда вы используете поле@AutowiredПри аннотировании вы обнаружите, что IDEA будет иметь предупреждение:

Field injection is not recommended

Inspection info: Spring Team Recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".

Это переводится так:

Инъекция в полевых условиях не рекомендуется.

Команда разработчиков Spring рекомендует: Всегда используйте внедрение зависимостей на основе конструктора в свои Spring Beans. Всегда используйте утверждения для необходимых зависимостей.

Например следующий код:

@Service
public class HelpService {
    @Autowired
    @Qualifier("svcB")
    private Svc svc;

    public void sayHello() {
        svc.sayHello();
    }
}

public interface Svc {
    void sayHello();
}

@Service
public class SvcB implements Svc {
    @Override
    public void sayHello() {
        System.out.println("hello, this is service B");
    }
}

поставить курсор на@Autowiredгде, использоватьAlt + EnterПосле быстрой модификации код станет методом инъекции на основе конструктора,после модификации:

@Service
public class HelpService {
    private final Svc svc;
    
    @Autowired
    public HelpService(@Qualifier("svcB") Svc svc) {
        // Assert.notNull(svc, "svc must not be null");
        this.svc = svc;
    }
    
    public void sayHello() {
        svc.sayHello();
    }
}

Если, как было предложено командой Spring, еслиsvcявляется обязательной зависимостью и должна использоватьсяAssert.notNull(svc, "svc must not be null")подтвердить.

Это предупреждение легко исправить, но я думаю, важнее понять, почему команда Spring сделала такое предложение? Что плохого в использовании этой полевой инъекции напрямую?


Прежде всего, нам нужно знать, что в Spring существует 3 способа внедрения зависимостей.:

  • Внедрение на основе поля (внедрение свойств)
  • На основе внедрения сеттера
  • На основе внедрения конструктора (внедрение конструктора)

1. На основе закачки в поле

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

@Autowired
private Svc svc;

2. Инжекция методом сеттера

через соответствующую переменнуюsetXXX()методы и использование аннотаций к методам для завершения внедрения зависимостей. Например:

private Helper helper;

@Autowired
public void setHelper(Helper helper) {
    this.helper = helper;
}

Примечание: вSpring 4.3и более поздние версии, указанный выше сеттер@AutowiredАннотации необязательны.

3. На основе внедрения конструктора

Поместите все необходимые зависимости в параметры аннотированного конструктора и завершите инициализацию соответствующих переменных в конструкторе.Этот метод основан на внедрении конструктора. Например:

private final Svc svc;
    
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
    this.svc = svc;
}

существуетSpring 4.3В и более поздних версиях, если класс имеет только один конструктор, то этот конструктор также можно опустить.@Autowiredаннотация.

Преимущества закачки в полевых условиях

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

Недостатки полевой закачки

Победа или поражение не имеет значения

Хотя инъекция на месте проста, она может вызвать много проблем. Эти проблемы часто возникают, когда я обычно разрабатываю и читаю код проекта.

  • Легко нарушать принцип единой ответственностиИспользуя этот метод, основанный на внедрении полей, очень просто добавлять зависимости.Даже если в вашем классе более десятка зависимостей, вы можете почувствовать, что в этом нет никакой проблемы.Обычные разработчики, скорее всего, добавят много зависимостей в класс. класс неосознанно. Но при использовании внедрения конструктора в определенный момент параметров в конструкторе становится так много, что становится очевидно, что что-то не так. Наличие слишком большого количества зависимостей обычно означает, что у ваших классов больше обязанностей, что является явным нарушением принципа единой ответственности (SRP).

    Эта проблема очень распространена в коде нашего проекта.

  • Внедрение зависимостей связано с самим контейнером

    Одна из основных идей инфраструктуры внедрения зависимостей заключается в том, что класс, управляемый контейнером, не должен зависеть от зависимостей, используемых контейнером. Другими словами, класс должен быть простым POJO (обычным обычным Java-объектом), который может быть создан отдельно, и вы также можете предоставить ему необходимые зависимости.

    Эта проблема может быть конкретно выражена в:

    • Ваш класс сильно связан с контейнером зависимостей и не может использоваться вне контейнера.
    • Ваши классы не могут быть созданы путем обхода отражения (например, в модульном тестировании), они должны быть созданы через контейнер зависимостей, который больше похож на интеграционный тест.
  • Неизменяемые объекты не могут быть созданы с использованием внедрения свойств (finalмодифицированная переменная)

Рекомендации от команды разработчиков Spring

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.

Проще говоря, это

  • Принудительные зависимости используют метод конструктора

  • Необязательные изменяемые зависимости вводятся с помощью сеттеров.

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

Давайте посмотрим, почему Spring рекомендует это именно так, сначала на основе внедрения конструктора,

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Команда Spring выступает за использование внедрения на основе конструктора, потому что, с одной стороны,Вставьте зависимость в неизменяемую переменную (примечание:finalмодифицированная переменная), с другой стороны тоже можноУбедитесь, что значение этих переменных не будет нулевым. Кроме того, компоненты, которые завершают внедрение зависимостей с помощью методов построения (Примечание: например, каждыйservice), может вызываться при вызовеУбедитесь, что все они готовы. В то же время с точки зрения качества кодаОгромный конструктор обычно представляет собой запах кода, и класс может брать на себя слишком много обязанностей..

А для инъекций на основе сеттера они говорят следующее:

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.

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

резюме

Вышеизложенное — это все содержание этой статьи, и я надеюсь, что после прочтения этой статьи у вас будет более глубокое понимание внедрения зависимостей в Spring.

Если эта статья была вам полезна, я надеюсь, что вы можете поставить лайк, это самая большая мотивация для меня 🤝🤝🤗🤗.

Ссылаться на