Я смотрю на какой-то существующий код приложения, и я запутался из-за кода там, вот сценарий:
В классе ABC у меня есть автомонтаж как:
@Autowired
RestTemplate restTemplate;
Затем в моем файле конфигурации Spring bean я определяю bean как:
<bean id = "restTemplate" class = "org.springframework.web.client.RestTemplate"/>
В классе ABC я использую шаблон отдыха как:
restTemplate.postForObject(url, requestObject, String.class);
Мои вопросы:
Основной класс:
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




@Autowired не создает bean-компонент, он берет существующее объявление bean-компонента, которое соответствует ожидаемому типу, и помещает его в поле. В XML у вас есть объявление вашего bean-компонента, без него ваш bean-компонент не будет существовать (если вы не используете Spring Boot, и в этом случае он может быть автоматически создан, если он не существует).
Теперь вы можете использовать @Inject вместо @Autowired, начиная с JSR-299, и если у вас есть последняя версия зависимостей Spring и javax.inject. Оба делают одно и то же, но @Inject более понятен.
@pjj, если он работает, когда вы комментируете элемент в XML, значит, он создается другой фабрикой, но в Spring есть bean-компонент этого типа. В противном случае вы получите исключение при выполнении приложения.
@LuiggiMendoza Я тестировал это с помощью отдельной Java-программы, и у меня есть определение bean-компонента в XML-файле, а также автозапуск и запуск программы, и я вижу, что у меня есть объект.
@pjj вы используете только Spring Boot или Spring?
@LuiggiMendoza Я думаю, только весна. Я создал автономную программу Java и включил Spring JAR в путь к классам во время работы.
@pjj, пожалуйста, обновите свой вопрос и предоставьте свой pom.xml. В мире программирования не существует такой вещи, как магия.
@pjj Я вижу код и все в порядке. Вы определяете свой bean-компонент SpringTest, украшая его аннотацией @Component, а затем в XML-файле указываете <context:component-scan base-package = "com.learn.stackoverflow.general"/>. Это не волшебство, это Spring 101.
@LuiggiMendoza Я никогда не говорил, что это волшебство. Я хочу понять, что происходит, не могли бы вы мне объяснить.
@pjj, который объясняется в руководствах. SO не предназначен для написания руководств для людей. Пожалуйста, объясните, в чем ваш конкретный вопрос.
@LuiggiMendoza Я уже упоминал свой вопрос в своем посте, о том, что я пытаюсь понять.
@LuiggiMendoza Кстати, очень скромно, все написано в учебниках, но люди отправляют вопросы, чтобы получить помощь, чтобы понять, когда они не могут понять в учебниках, если то, что вы думаете, правда, тогда не должно быть вопросов по SO.
@pjj ваш первоначальный вопрос был о том, почему был внедрен экземпляр RestTemplate. Я попросил ваш pom.xml посмотреть, что может случиться, и вы публикуете что-то совершенно другое, что касается Spring управления определенными вами beans. Вы не сосредотачиваетесь на исходном вопросе и вместо этого просите понять, как работает Spring. Сайт работает не так. Если бы ваш исходный вопрос был бы «Мне нужно понять, как работает @Autowired» или аналогичный, то я бы ответил иначе.
@LuiggiMendoza Я думаю, вы не поняли мой вопрос, мой вопрос был "когда я использую остальной шаблон, то объект, который я использую, является тем, который вызван автоматическим подключением, или тем, который определен в файле конфигурации bean-компонента?" ...
@pjj, и эти ответы, и я утверждаем, что @Autowired не будет создавать экземпляр bean-компонента, вместо этого Spring вставит экземпляр этого bean-компонента в это поле. Когда вы просите пример кода, чтобы лучше понять этот случай, вы предоставляете что-то совершенно другое ...
@LuiggiMendoza У меня нет файла pom.xml, как я могу вам его предоставить? Основываясь на вашем комментарии, я предоставил вам минималистичный воспроизводимый пример кода, я не знаю, чего еще вы ожидали.
@pjj Я запрашиваю минимальный пример, в котором вы вводите RestTemplate, у вас есть компонент RestTemplate, определенный в XML, список используемых вами библиотек и все, что у вас есть, чтобы мы могли воспроизвести проблему на наших машинах.
@LuiggiMendoza Я смоделировал то же поведение, используя автономную программу Java и объекты POJO, разве не нормально? Если вам нужен конкретный пример с RestTemplate, извините, я не смогу его предоставить, для создания такого примера потребуется время, поэтому я быстро смоделировал все поведение в автономной программе Java.
@pjj это не нормально, потому что вы утверждаете, что если вы прокомментируете определение bean-компонента, ваша программа все равно будет работать. Итак, bean-компонент RestTemplate должен быть определен где-то еще, чтобы он работал, а в вашем примере легче понять, почему он работает и как заставить его работать. Извините за вас, потому что тогда мы не сможем вам помочь.
Конфигурационный файл сообщает Spring Создайте bean-компонент с идентификатором restTemplate, который имеет тип org.springframework.web.client.RestTemplate. Область видимости по умолчанию - это единственный экземпляр bean-компонента.
Затем в коде, поскольку существует bean-компонент тип RestTemplate, bean-компонент (который имеет идентификатор restTemplate) автоматически подключается.
restTemplate в качестве идентификатора или атрибута класса, который также имеет restTemplate в качестве имени, не имеет значения. Бин может быть введен, поскольку он одного типа. (А также потому, что такого типа только 1.)
даже если я удалю определение компонента XML, объект останется у меня из-за автоматического подключения
Вы выполнили полную очистку / сборку и все еще можете запустить приложение? Это даже не должно начинаться.
@pjj autowiring означает, что Spring предоставит вам значение набор. Чтобы установить это, Spring Context должен был создать bean-компонент этого типа. Возможно, кто-то переопределил bean-компонент, возможно, чтобы использовать для него настраиваемые свойства, но у bean-компонента есть значение по умолчанию и фабрика для его создания.
@AndrewS Хорошо. Пожалуйста, поправьте меня, насколько я понимаю - поскольку мой файл конфигурации Spring bean имеет restTemplate, поэтому этот экземпляр вводится в мой автоматически подключенный Person person;. И когда я удалю это определение bean из моего файла конфигурации Spring bean, тогда контейнер IoC Spring создаст экземпляр RestTemplate и введет то же самое?
Когда контейнер 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 и сканированием компонентов.
@NyamiouTheGaleanthrope Это очень очевидно, намерение состояло не в том, чтобы рассказать пошаговый процесс о том, как Spring создаст экземпляр, а в том, чтобы объяснить, что искал OP.
Я не думаю, что это правда, потому что даже если я удалю определение компонента XML, объект останется у меня из-за автоматического подключения.