Почему ApplicationContext.getBean(beanName) возвращает null, когда оно явно существует?

#java #spring

#java #spring

Вопрос:

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

Затем я определил другой простой класс, присвоил ему свойство и аннотацию компонента. Я также автоматически подключаю этот класс к классу jmx bean, просто чтобы убедиться, что компонент этого типа существует.

Затем я запустил службу SpringBoot.

Затем я вызвал метод из VisualVM.

Сбой, говорящий о том, что не удалось найти компонент с таким именем.

Итак, теперь более подробно.

Вот первый класс:

 @Component
@ManagedResource
public class JMXDemonstration {
    @Autowired
    private ApplicationContext  applicationContext;
    @Autowired
    private SomeRandomThing thing;

    @Value("${jmxDemonstration.name}")
    private String name;

    @ManagedAttribute
    public String getName() { return name; }

    @ManagedAttribute
    public void setName(String name) { this.name = name; }

    @ManagedOperation
    public String buildHelloWorldMessage() {
        return "Hello, "   name   ": "   thing.getId();
    }

    @ManagedOperation
    public void assignValueToBeanProperty(String beanName, String propertyName, String expression) {
        Object  bean    = applicationContext.getBean(beanName);

        ExpressionParser        parser      = new SpelExpressionParser();
        SimpleEvaluationContext evalContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

        parser.parseExpression(propertyName).setValue(evalContext, bean, expression);
    }
}
  

И вот другой класс:

 @Component
public class SomeRandomThing {
    private String  id;

    public String getId() { return id; }

    public void setId(String id) { this.id = id; }
}
  

Когда я вызываю метод из VisualVM, я передаю «SomeRandomThing», «id» и «xxx».

Это приводит к сбою с:

 org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'SomeRandomThing' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685)
  

Я также установил точку останова в методе и посмотрел на «this» и подтвердил, что существует допустимое свойство «thing» (если бы его не было, служба не запустилась бы).

Итак, у меня неверный алгоритм имен компонентов по умолчанию? В это трудно поверить, потому что я также провел тест «JMXDemonstration», «name» и «George», и это сработало отлично.

Обновить:

Также обратите внимание, что вызов «ApplicationContext.getBean(SomeRandomThing.class )» возвращает экземпляр компонента этого класса, а также вызов «ApplicationContext.getbeandefinitionname()» возвращает массив, который не содержит «SomeRandomThing», но он содержит «JMXDemonstration».

Почему SomeRandomThing доступен как компонент через автоматическое подключение и через тип, но не через его имя компонента?

Обновить:

О, потому что имя компонента «someRandomThing», а не «SomeRandomThing». Думаю, изначально я ожидал первого, но когда я увидел, что имя компонента для «JMXDemonstration» было «JMXDemonstration», я предположил, что тогда это будет «SomeRandomThing», а не «someRandomThing».

Комментарии:

1. Упоминается ли в документации Spring, что имя компонента автоматически является именем класса? Я могу представить, что оно включает пакет, то есть становится com.project.SomeRandomThing . Я бы проверил внутреннюю работу atComponent или atComponentScan, чтобы найти подробную информацию о том, как Spring создает компоненты.

Ответ №1:

Именование компонента

@Component — это аннотация уровня класса. Во время проверки компонентов Spring Framework автоматически обнаруживает классы, помеченные @Component.

 @Component
class CarUtility {
   // ...
}
  

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

Конфигурация на основе аннотаций

Для компонента на основе стереотипных аннотаций, если имя явно не указано в поле значения стереотипных аннотаций, то имя снова генерируется с помощью AnnotationBeanNameGenerator , которая является реализацией интерфейса стратегии BeanNameGenerator здесь

Если значение аннотации не указывает на имя компонента, соответствующее имя будет создано на основе краткого имени класса (с первой буквой в нижнем регистре). Например:

 com.xyz.FooServiceImpl -> fooServiceImpl
  

При сканировании компонентов в classpath Spring генерирует имена компонентов для неназванных компонентов, следуя правилам, описанным ранее: по сути, берется простое имя класса и преобразуется его начальный символ в нижний регистр. Однако, в (необычном) особом случае, когда имеется более одного символа и оба первого и второго символа являются заглавными, исходный регистр сохраняется. Это те же правила, которые определены в документе java.beans.Introspector.decapitalize (который Spring использует здесь).

Комментарии:

1. Хорошо, тогда можете ли вы объяснить, почему имя компонента для класса JMXDemonstration было JMXDemonstration?

2. Обновлено к вашему вопросу проверьте ссылку на документ, просто скопируйте это содержимое и найдите его. Вы найдете это @DavidM.Karr