Какой объект будет использоваться, когда у меня есть определение bean-компонента в файле конфигурации, а также при использовании аннотации @Autowire

Я смотрю на какой-то существующий код приложения, и я запутался из-за кода там, вот сценарий:

В классе ABC у меня есть автомонтаж как:

@Autowired
RestTemplate restTemplate;

Затем в моем файле конфигурации Spring bean я определяю bean как:

<bean id = "restTemplate" class = "org.springframework.web.client.RestTemplate"/>

В классе ABC я использую шаблон отдыха как:

restTemplate.postForObject(url, requestObject, String.class);

Мои вопросы:

  • когда я использую остальной шаблон, то объект, который я использую, является тем, который вызван автоматическим подключением, или тем, который определен в файле конфигурации bean-компонента?
  • В случае автосоединения, поскольку я не указал область видимости, это будет синглтон, верно?


As per comments and answers, below is minimalistic reproducible code which shows that even when I have auto-wiring and bean definition in bean config file, I can run the program and there are no issues.

Основной класс:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringTest {

    @Autowired
    Person person;

    public static void main(String[] args) {
        generalTest();
    }

    private static void generalTest() {
        testApplicationContext();
    }

    private static void testApplicationContext() {
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("bean.xml");

        SpringTest springTest = (SpringTest) applicationContext.getBean("springTest");

        if (springTest.person == null){
            System.out.println("person is NULL");
        } else{
            System.out.println("person is not NULL");
        }

    }

}

Bean-файл:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context = "http://www.springframework.org/schema/context"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package = "com.learn.stackoverflow.general"/>
    <!-- as such below is useless because The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. 
    There is usually no need to include the <context:annotation-config> element when using <context:component-scan>. -->
    <context:annotation-config /> 

   <bean id = "person" class = "com.learn.stackoverflow.general.Person" scope = "singleton">
   </bean>

</beans>

Класс человека:

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "singleton")
public class Person implements InitializingBean, Human {
    @Autowired
    SpringTest springTest;

    static{
        System.out.println("Static initialization from Person");
    }

    {
        System.out.println("Instance initialization  from Person");
    }

    public Person(){
        System.out.println("Constructor  from Person");
    }

    public void sayHello(){
        System.out.println("Hello");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean.afterPropertiesSet  from Person");
    }

    @Override
    public void breathe() {
        System.out.println("person is breathing...");
    }

}

Выход person is not NULL

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
65
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

@Autowired не создает bean-компонент, он берет существующее объявление bean-компонента, которое соответствует ожидаемому типу, и помещает его в поле. В XML у вас есть объявление вашего bean-компонента, без него ваш bean-компонент не будет существовать (если вы не используете Spring Boot, и в этом случае он может быть автоматически создан, если он не существует).

Теперь вы можете использовать @Inject вместо @Autowired, начиная с JSR-299, и если у вас есть последняя версия зависимостей Spring и javax.inject. Оба делают одно и то же, но @Inject более понятен.

Я не думаю, что это правда, потому что даже если я удалю определение компонента XML, объект останется у меня из-за автоматического подключения.

pjj 30.03.2018 18:44

@pjj, если он работает, когда вы комментируете элемент в XML, значит, он создается другой фабрикой, но в Spring есть bean-компонент этого типа. В противном случае вы получите исключение при выполнении приложения.

Luiggi Mendoza 30.03.2018 18:49

@LuiggiMendoza Я тестировал это с помощью отдельной Java-программы, и у меня есть определение bean-компонента в XML-файле, а также автозапуск и запуск программы, и я вижу, что у меня есть объект.

pjj 30.03.2018 18:52

@pjj вы используете только Spring Boot или Spring?

Luiggi Mendoza 30.03.2018 18:53

@LuiggiMendoza Я думаю, только весна. Я создал автономную программу Java и включил Spring JAR в путь к классам во время работы.

pjj 30.03.2018 18:54

@pjj, пожалуйста, обновите свой вопрос и предоставьте свой pom.xml. В мире программирования не существует такой вещи, как магия.

Luiggi Mendoza 30.03.2018 18:55

@pjj Я вижу код и все в порядке. Вы определяете свой bean-компонент SpringTest, украшая его аннотацией @Component, а затем в XML-файле указываете <context:component-scan base-package = "com.learn.stackoverflow.general"/>. Это не волшебство, это Spring 101.

Luiggi Mendoza 30.03.2018 19:04

@LuiggiMendoza Я никогда не говорил, что это волшебство. Я хочу понять, что происходит, не могли бы вы мне объяснить.

pjj 30.03.2018 19:05

@pjj, который объясняется в руководствах. SO не предназначен для написания руководств для людей. Пожалуйста, объясните, в чем ваш конкретный вопрос.

Luiggi Mendoza 30.03.2018 19:06

@LuiggiMendoza Я уже упоминал свой вопрос в своем посте, о том, что я пытаюсь понять.

pjj 30.03.2018 19:08

@LuiggiMendoza Кстати, очень скромно, все написано в учебниках, но люди отправляют вопросы, чтобы получить помощь, чтобы понять, когда они не могут понять в учебниках, если то, что вы думаете, правда, тогда не должно быть вопросов по SO.

pjj 30.03.2018 19:09

@pjj ваш первоначальный вопрос был о том, почему был внедрен экземпляр RestTemplate. Я попросил ваш pom.xml посмотреть, что может случиться, и вы публикуете что-то совершенно другое, что касается Spring управления определенными вами beans. Вы не сосредотачиваетесь на исходном вопросе и вместо этого просите понять, как работает Spring. Сайт работает не так. Если бы ваш исходный вопрос был бы «Мне нужно понять, как работает @Autowired» или аналогичный, то я бы ответил иначе.

Luiggi Mendoza 30.03.2018 19:12

@LuiggiMendoza Я думаю, вы не поняли мой вопрос, мой вопрос был "когда я использую остальной шаблон, то объект, который я использую, является тем, который вызван автоматическим подключением, или тем, который определен в файле конфигурации bean-компонента?" ...

pjj 30.03.2018 19:15

@pjj, и эти ответы, и я утверждаем, что @Autowired не будет создавать экземпляр bean-компонента, вместо этого Spring вставит экземпляр этого bean-компонента в это поле. Когда вы просите пример кода, чтобы лучше понять этот случай, вы предоставляете что-то совершенно другое ...

Luiggi Mendoza 30.03.2018 19:17

@LuiggiMendoza У меня нет файла pom.xml, как я могу вам его предоставить? Основываясь на вашем комментарии, я предоставил вам минималистичный воспроизводимый пример кода, я не знаю, чего еще вы ожидали.

pjj 30.03.2018 19:19

@pjj Я запрашиваю минимальный пример, в котором вы вводите RestTemplate, у вас есть компонент RestTemplate, определенный в XML, список используемых вами библиотек и все, что у вас есть, чтобы мы могли воспроизвести проблему на наших машинах.

Luiggi Mendoza 30.03.2018 19:21

@LuiggiMendoza Я смоделировал то же поведение, используя автономную программу Java и объекты POJO, разве не нормально? Если вам нужен конкретный пример с RestTemplate, извините, я не смогу его предоставить, для создания такого примера потребуется время, поэтому я быстро смоделировал все поведение в автономной программе Java.

pjj 30.03.2018 19:24

@pjj это не нормально, потому что вы утверждаете, что если вы прокомментируете определение bean-компонента, ваша программа все равно будет работать. Итак, bean-компонент RestTemplate должен быть определен где-то еще, чтобы он работал, а в вашем примере легче понять, почему он работает и как заставить его работать. Извините за вас, потому что тогда мы не сможем вам помочь.

Luiggi Mendoza 30.03.2018 19:26

Конфигурационный файл сообщает Spring Создайте bean-компонент с идентификатором restTemplate, который имеет тип org.springframework.web.client.RestTemplate. Область видимости по умолчанию - это единственный экземпляр bean-компонента.

Затем в коде, поскольку существует bean-компонент тип RestTemplate, bean-компонент (который имеет идентификатор restTemplate) автоматически подключается.

restTemplate в качестве идентификатора или атрибута класса, который также имеет restTemplate в качестве имени, не имеет значения. Бин может быть введен, поскольку он одного типа. (А также потому, что такого типа только 1.)

даже если я удалю определение компонента XML, объект останется у меня из-за автоматического подключения

pjj 30.03.2018 18:47

Вы выполнили полную очистку / сборку и все еще можете запустить приложение? Это даже не должно начинаться.

Andrew S 30.03.2018 18:50

@pjj autowiring означает, что Spring предоставит вам значение набор. Чтобы установить это, Spring Context должен был создать bean-компонент этого типа. Возможно, кто-то переопределил bean-компонент, возможно, чтобы использовать для него настраиваемые свойства, но у bean-компонента есть значение по умолчанию и фабрика для его создания.

Luiggi Mendoza 30.03.2018 18:50

@AndrewS Хорошо. Пожалуйста, поправьте меня, насколько я понимаю - поскольку мой файл конфигурации Spring bean имеет restTemplate, поэтому этот экземпляр вводится в мой автоматически подключенный Person person;. И когда я удалю это определение bean из моего файла конфигурации Spring bean, тогда контейнер IoC Spring создаст экземпляр RestTemplate и введет то же самое?

pjj 30.03.2018 19:18
Ответ принят как подходящий

Когда контейнер IoC находит определение компонента, определенное в файле конфигурации компонента, то такое же определение используется для внедрения экземпляра, и если контейнер IoC не находит его, он создает экземпляр и внедряет его.

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

SpringTest:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringTest {
    @Autowired
    Person person;

    public static void main(String[] args) {
        generalTest();
    }

    private static void generalTest() {
        testApplicationContext();
    }

    private static void testApplicationContext() {
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("C:\\E_Drive\\Projects\\Workspace\\Test\\CS101\\src\\com\\learn\\stackoverflow\\general\\bean.xml");
        SpringTest springTest = (SpringTest) applicationContext.getBean("springTest");

        if (springTest.person == null){
            System.out.println("person is NULL");
        } else{
            System.out.println("person is not NULL");
            springTest.person.sayHello(); // here output will be “Hello, ppj” if you keep the config file bean definition and when you remove that bean definition then output is “Hello, null”
        }

    }
}

Конфигурация бина:

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns = "http://www.springframework.org/schema/beans"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context = "http://www.springframework.org/schema/context"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package = "com.learn.stackoverflow.general"/>

    <!-- once you get comment out below, IoC will instatiate a bean and will inject the same in SpringTest's autowiring of Person class  -->
   <!-- <bean id = "person" class = "com.learn.stackoverflow.general.Person" scope = "singleton">
      <constructor-arg name = "name" value = "ppj"></constructor-arg>
   </bean> -->

</beans>

Человек:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "singleton")
public class Person {
    private String name;

    @Autowired
    SpringTest springTest;

    public Person(){
        System.out.println("Constructor from Person");
    }

    public Person(String name){
        System.out.println("String Constructor from Person");
        this.name = name;
    }

    public void sayHello(){
        System.out.println("Hello, " + name);
    }

}

«если контейнер IoC не находит его, он создает экземпляр». Это неверно, Spring не будет создавать определение bean из воздуха для вас (если вы не используете Spring Boot, и даже тогда он создает только определенные bean-компоненты в соответствии с его автоконфигурацией), здесь у вас есть определение bean-компонента Person, потому что оно было создается аннотацией @Component и сканированием компонентов.

Nyamiou The Galeanthrope 02.04.2018 14:01

@NyamiouTheGaleanthrope Это очень очевидно, намерение состояло не в том, чтобы рассказать пошаговый процесс о том, как Spring создаст экземпляр, а в том, чтобы объяснить, что искал OP.

hagrawal 02.04.2018 15:44

Другие вопросы по теме