предисловие
Содержание этой главы в основном состоит в том, чтобы обсудить некоторые моменты знаний о внедрении зависимостей в процессе разработки 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();
}
}
Порядок сборки:
-
в соответствии с
type
Найдите соответствующий компонент в контексте查找type为Svc的bean
-
Если есть несколько бобов, следуйте
name
соответствовать-
Если есть
@Qualifier
обратите внимание, согласно@Qualifier
назначенныйname
соответствовать查找name为svcA的bean
-
Если нет, сопоставьте по имени переменной
查找name为svc的bean
-
-
Если они не совпадают, будет сообщено об ошибке. (
@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-компонента.
Порядок сборки:
- Если также указано
name
иtype
, единственный соответствующий bean-компонент находится в контексте Spring для сборки, и если он не найден, генерируется исключение. - если указано
name
, затем ищите компонент с совпадающим именем (идентификатором) в контексте сборки и выдавайте исключение, если он не найден. - если указано
type
, из контекста для сборки найден единственный bean-компонент с соответствующим типом, если он не найден или найдено более одного, будет выдано исключение. - Если ни то, ни другое не указано
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.
Если эта статья была вам полезна, я надеюсь, что вы можете поставить лайк, это самая большая мотивация для меня 🤝🤝🤗🤗.