Другие весенние статьи, добро пожаловать, нажмитеСерый блог-весенняя тема
При внедрении bean-компонента через интерфейс, если есть несколько bean-компонентов подкласса, какой bean-компонент будет внедрен? Могут ли быть в системе два bean-компонента с одинаковым именем? Если да, то как выбрать для его введения? Если нет, то как избежать вышеуказанных проблем?
I. Выбор мультиэкземплярных компонентов
Можно сказать, что этот сценарий является относительно распространенным. Сейчас мы выступаем за интерфейсно-ориентированное программирование. Когда интерфейс имеет несколько экземпляров, нам нужно уделять особое внимание тому, как внедрять и ссылаться на него.
1. Базовая поза
Сначала определите интерфейс и два простых класса реализации и продемонстрируйте наше обычное использование
Выходной интерфейс определяется следующим образом
public interface IPrint {
void print(String msg);
}
соответствует двум реализациям
@Component
public class ConsolePrint implements IPrint {
@Override
public void print(String msg) {
System.out.println("console print: " + msg);
}
}
@Slf4j
@Component
public class LogPrint implements IPrint {
@Override
public void print(String msg) {
log.info("log print: {}", msg);
}
}
Ниже приведен наш общий метод цитирования.
-
@Autowired
При аннотировании имя атрибута является именем компонента по умолчанию, как показано ниже.logPrint
это получитьbeanName=logPrint
фасоль -
@Resource(name=xxx)
Укажите имя bean-компонента напрямую, чтобы однозначно выбрать соответствующий bean-компонент.
@Component
public class NormalPrintDemo {
@Resource(name = "consolePrint")
private IPrint consolePrint;
@Autowired
private IPrint logPrint;
@PostConstruct
public void init() {
consolePrint.print(" console print!!!");
logPrint.print(" log print!!!");
}
}
Выше приведены две распространенные позы, в дополнение к@Primary
аннотация для объявления инжектированного bean-компонента по умолчанию
2. @Primary
аннотация
Эта аннотация предназначена для решения, когда есть несколько bean-компонентов, соответствующих условиям внедрения, выбирается экземпляр с этой аннотацией.
Согласно приведенному выше описанию функции очевидно, что
@Primary
Использование аннотаций требует уникальности: в соответствии с приведенным выше случаем в подклассе интерфейса только одна реализация может иметь на себе эту аннотацию.
Предположим, что эта аннотация помещена вLogPrint
После вышеизложенного следующим образом
@Slf4j
@Component
@Primary
public class LogPrint implements IPrint {
@Override
public void print(String msg) {
log.info("log print: {}", msg);
}
}
Сочетая приведенные выше общие позы и добавляя эту аннотацию, наши тестовые примеры должны содержать как минимум следующие
-
@Resource
Указывает, будет ли beanName@Primary
Оказать влияние - фронт
@Autowired
Метод аннотации + имя атрибута, выбирается ли он в соответствии с методом в первом разделе, или он выбран для@Primary
Экземпляр удостоверения -
@Autowired
+ Случайное свойство, отличное от beanName, проверьте, будет ли оно выбрано@Primary
Аннотация логотипа
@Component
public class PrintDemoBean {
@Resource(name = "logPrint")
private IPrint print;
/**
* 下面的注解不指定name,则实例为logPrint
*/
@Autowired
private IPrint consolePrint;
// logPrint的选择,由@Primary注解决定
@Autowired
private IPrint logPrint;
// logPrint的选择,由@Primary注解决定
@Autowired(required = false)
private IPrint xxxPrint;
@PostConstruct
public void init() {
print.print("expect logPrint for [print]");
consolePrint.print(" expect logPrint for [consolePrint]");
logPrint.print("expect logPrint for [logPrint]");
xxxPrint.print("expect logPrint for [xxxPrint]");
}
}
Результат выполнения следующий
2018-10-22 19:42:40.234 INFO 61966 --- [ main] c.g.h.b.b.choose.sameclz.LogPrint : log print: expect logPrint for [print]
2018-10-22 19:42:40.235 INFO 61966 --- [ main] c.g.h.b.b.choose.sameclz.LogPrint : log print: expect consolePrint for [consolePrint]
2018-10-22 19:42:40.235 INFO 61966 --- [ main] c.g.h.b.b.choose.sameclz.LogPrint : log print: expect logPrint for [logPrint]
2018-10-22 19:42:40.235 INFO 61966 --- [ main] c.g.h.b.b.choose.sameclz.LogPrint : log print: expect logPrint for [xxxPrint]
3. Резюме
По предыдущему исполнению можно узнать, что способ выбора бобов следующий
существует@Primary
При аннотации
-
@Resource
Когда в аннотации указано имя, соответствующий bean-компонент ищется по имени. -
@Autowired
Примечания, использовать все@Primary
Аннотация логотипа -
@Primary
Аннотация требует уникальности (необобщенная уникальность не означает, что можно использовать только один @Primary, подробности см. на лицевой стороне)
не существует@Primary
При аннотации
-
@Resource
Когда в аннотации указано имя, соответствующий bean-компонент ищется по имени. -
@Autowired
При аннотировании проверяйте соответствующий Bean по имени атрибута, если не может его найти, выбрасывайте исключение, если находит, то это он
2. Проблема одноименных бобов
В нашем фактическом бизнес-разработке относительно часто должно быть несколько исключений с именем xxx, что означает, что не должно быть двух bean-компонентов с одинаковым именем; но рассмотрим следующий сценарий
Службы A зависят от служб B и C; B и C являются двумя полностью независимыми сторонними службами, каждая из которых предоставляетbeanName=xxxService
Для A будет конфликт BeanName в контейнере Spring, и этот сценарий неуправляем для A. Что мне делать в этом случае?
1. Одноименный боб
Давайте возьмем случай, чтобы продемонстрировать ситуацию с bean-компонентами с одинаковым именем.Определим два bean-компонента следующим образом.За исключением разных путей пакетов, имена классов одинаковы.@Component
Компонент объявлен аннотацией, поэтому beanName обоих компонентовSameA
package com.git.hui.boot.beanorder.choose.samename.a;
import org.springframework.stereotype.Component;
/**
* Created by @author yihui in 21:32 18/10/22.
*/
@Component
public class SameA {
private String text ;
public SameA() {
text = "a sameA!";
}
public void print() {
System.out.println(text);
}
}
package com.git.hui.boot.beanorder.choose.samename.b;
import org.springframework.stereotype.Component;
/**
* Created by @author yihui in 21:33 18/10/22.
*/
@Component
public class SameA {
private String text;
public SameA() {
text = "B SameA";
}
public void print() {
System.out.println(text);
}
}
Затем проверьте ссылку, чтобы увидеть, есть ли проблема
package com.git.hui.boot.beanorder.choose.samename;
import com.git.hui.boot.beanorder.choose.samename.a.SameA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* Created by @author yihui in 21:32 18/10/22.
*/
@Component
public class SameDemo {
@Autowired
private SameA sameA;
@PostConstruct
public void init() {
sameA.print();
}
}
После выполнения неожиданно возникло исключение, и информация о стеке выглядит следующим образом.
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.git.hui.boot.beanorder.Application]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'sameA' for bean class [com.git.hui.boot.beanorder.choose.samename.b.SameA] conflicts with existing, non-compatible bean definition of same name and class [com.git.hui.boot.beanorder.choose.samename.a.SameA]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:184) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:316) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:271) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:91) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:532) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:330) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1258) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at com.git.hui.boot.beanorder.Application.main(Application.java:15) [classes/:na]
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'sameA' for bean class [com.git.hui.boot.beanorder.choose.samename.b.SameA] conflicts with existing, non-compatible bean definition of same name and class [com.git.hui.boot.beanorder.choose.samename.a.SameA]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:348) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:286) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:288) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:202) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:170) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
... 12 common frames omitted
2. Уход от одноименной проблемы
Если вышеуказанная проблема действительно возникает, как ее решить? Если эти bean-компоненты находятся под нашим контролем, самый простой способ — не иметь одинаковое имя и указать beanName при определении, как показано ниже.
@Component("aSameA")
public class SameA {
private String text ;
public SameA() {
text = "a sameA!";
}
public void print() {
System.out.println(text);
}
}
Что, если он полностью вышел из-под контроля? Как было сказано выше, мне приходится полагаться на два сторонних сервиса, но у них есть bean-компоненты с одинаковым именем, как его можно сломать?
Одним из решений является исключение автоматической загрузки одного из бинов с таким же именем и регистрация бина активной регистрацией.
Способ исключения автоматически сканируемых bean-компонентов следующий: добавьте аннотации в класс запуска@ComponentScan
и указать, какой изexcludeFilters
Атрибуты
@SpringBootApplication
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = SameA.class)})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Затем настройте класс конфигурации компонента
package com.git.hui.boot.beanorder.choose.samename;
import com.git.hui.boot.beanorder.choose.samename.a.SameA;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by @author yihui in 22:14 18/10/22.
*/
@Configuration
public class AutoConfig {
@Bean
public SameA mySameA() {
return new SameA();
}
}
Другой код такой же, как и раньше, выполните его снова, результат будет следующим, окончательный выводa sameA!
экземпляр bean-компонента выбирается в соответствии с типом
II. Другое
А. Больше сообщений в блоге
Основы
- 181009-Основное определение и использование компонентов в SpringBoot Basics
- 181012-Spring Boot Basics Bean Автоматическая загрузка
- 181013-Динамическая регистрация компонентов Spring Boot Basics
- 181018-SpringBoot Basics Условная инъекция Bean-компонента Положение использования @Condition
- 181019-@ConditionalOnBean и @ConditionalOnClass компонентов SpringBoot Basics
- 181019-SpringBoot Basics Bean Условное внедрение @ConditionalOnProperty
- 181019-SpringBoot Basics Bean Условное внедрение @ConditionalOnExpression
- 181022-Выбор нескольких экземпляров SpringBoot Basics Bean
Применение
б. Исходный код проекта
- проект:spring-boot-demo
- module: 008-beanorder
1. Серый блог
- One Grey BlogПерсональный блогblog.hhui.top
- Серый блог - специальный весенний блогspring.hhui.top
Серый личный блог, записывающий все посты блога по учебе и работе, приглашаю всех в гости
2. Заявление
Это не так хорошо, как письмо. Вышеупомянутое содержание чисто из семьи. Из-за ограниченных личных способностей неизбежно есть упущения и ошибки. Если вы найдете ошибки или у вас есть лучшие предложения, вы можете критиковать и исправлять их. Спасибо ты очень.
3. Сканируйте внимание
серый блог