Решение и выбор нескольких экземпляров Bean-компонентов с одинаковым именем в SpringBoot Basics

задняя часть Spring контейнер

Другие весенние статьи, добро пожаловать, нажмитеСерый блог-весенняя тема

При внедрении 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-компонента выбирается в соответствии с типом

同名bean修复演示

II. Другое

А. Больше сообщений в блоге

Основы

Применение

б. Исходный код проекта

1. Серый блог

  • One Grey BlogПерсональный блогblog.hhui.top
  • Серый блог - специальный весенний блогspring.hhui.top

Серый личный блог, записывающий все посты блога по учебе и работе, приглашаю всех в гости

2. Заявление

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

3. Сканируйте внимание

серый блог

QrCode