#spring
#spring
Вопрос:
Можем ли мы иметь повторяющиеся имена для одного и того же идентификатора компонента, который упоминается в XML? Если нет, то как нам переопределить компонент в Spring?
Комментарии:
1. Можете ли вы опубликовать какой-нибудь пример xml, чтобы точно показать, что вы имеете в виду
Ответ №1:
Любой данный контекст Spring может иметь только один компонент для любого заданного идентификатора или имени. В случае атрибута XML id
это обеспечивается проверкой схемы. В случае name
атрибута это обеспечивается логикой Spring.
Однако, если контекст создается из двух разных файлов XML-дескрипторов, и id
используется обоими файлами, то один будет «переопределять» другой. Точное поведение зависит от порядка файлов, когда они загружаются контекстом.
Поэтому, хотя это возможно, это не рекомендуется. Он подвержен ошибкам и хрупок, и вы не получите никакой помощи от Spring, если измените идентификатор одного, но не другого.
Комментарии:
1. Если они находятся на одном уровне иерархии, это рискованно, но можно использовать
profiles
(см. Мой ответ), чтобы получить лучший контроль над ним.2. Есть ли способ указать Spring выдавать ошибку, если он встречает какие-либо компоненты, которые имеют одинаковый идентификатор (т. Е. категорически не допускают переопределения)? Это для
contextConfigLocation
свойстваServletContextHandler
.3. Для тех, кто использует Java Config, в аннотации
@Primary
указано, что предпочтение следует отдавать компоненту, когда несколько кандидатов квалифицированы для автоматического подключения однозначной зависимости.4. итак, каков ожидаемый порядок при использовании
<import resource="first-context.xml">
? выигрывает ли последний?
Ответ №2:
Я добавлю, что если вам нужно просто переопределить свойство, используемое вашим компонентом, подход id работает также, как объяснил Скаффман :
В вашем первом вызванном файле конфигурации XML :
<bean id="myBeanId" class="com.blabla">
<property name="myList" ref="myList"/>
</bean>
<util:list id="myList">
<value>3</value>
<value>4</value>
</util:list>
В вашем втором вызываемом файле конфигурации XML :
<util:list id="myList">
<value>6</value>
</util:list>
Затем ваш компонент «myBeanId» будет создан со свойством «myList» для одного элемента, который равен 6.
Ответ №3:
Не уверен, что это именно то, что вам нужно, но мы используем профили для определения среды, в которой мы работаем, и конкретного компонента для каждой среды, так что это что-то вроде этого:
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="originalValue" />
</bean>
<beans profile="DEV, default">
<!-- Specific DEV configurations, also default if no profile defined -->
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="overrideValue" />
</bean>
</beans>
<beans profile="CI, UAT">
<!-- Specific CI / UAT configurations -->
</beans>
<beans profile="PROD">
<!-- Specific PROD configurations -->
</beans>
Итак, в этом случае, если я не определяю профиль или определяю его как «DEV», MyBean получит «переопределяющее значение» для своего аргумента name. Но если я установлю для профиля значение «CI», «UAT» или «PROD», он получит «OriginalValue» в качестве значения.
Комментарии:
1. Является ли «default» специальным автоматически включаемым профилем в Spring или вы должны включить его явно?
2. Это, как следует из названия, профиль по умолчанию. Тот, который вы получаете автоматически, если не измените его вручную.
3. Что, если включить другие профили, которые не связаны с SIT / UAT env? Например, в нашем приложении есть профили для подключения к базе данных (JDBC; JNDI), кластеризации (ОДНОУЗЛОВОЙ; CLUSTER), аутентификации (DB; AD; SSO). Я полагаю, что если я включу профиль аутентификации, я не смогу использовать профиль «по умолчанию» для службы базы данных. Но спасибо за ответ, потому что я все равно собираюсь продолжить расследование
4. @RoeeGavirel — гарантируется ли, что компонент, определенный внутри профиля, всегда будет переопределять другой. В моем случае у меня есть другой файл определения XML-компонента, который я загружаю на основе выбранного профиля, и я хочу убедиться, что spring всегда использует переопределенный компонент, если профиль активен. Не могли бы вы, пожалуйста, предоставить ссылку, если это возможно, если spring утверждает порядок переопределения определений компонента. Спасибо за вашу помощь
Ответ №4:
Пример из официального руководства spring:
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
Это то, что вы искали?
Обновленная ссылка
Ответ №5:
Начиная с Spring 3.0 вы можете использовать @Primary
аннотации. Согласно документации:
Указывает, что компоненту следует отдать предпочтение, когда несколько кандидатов имеют право автоматически подключать однозначную зависимость. Если среди кандидатов существует ровно один «основной» компонент, это будет значение с автоматической настройкой. Эта аннотация семантически эквивалентна основному атрибуту элемента в Spring XML.
Вы должны использовать его для определения компонента следующим образом:
@Bean
@Primary
public ExampleBean exampleBean(@Autowired EntityManager em) {
return new ExampleBeanImpl(em);
}
или вот так:
@Primary
@Service
public class ExampleService implements BaseServive {
}
Ответ №6:
Другой хороший подход, не упомянутый в других сообщениях, заключается в использовании PropertyOverrideConfigurer на случай, если вы просто хотите переопределить свойства некоторых компонентов.
Например, если вы хотите переопределить источник данных для тестирования (т. Е. использовать базу данных в памяти) в другой конфигурации xml, вам просто нужно использовать <context:property-override ..."/>
в новой конфигурации .properties
файл, содержащий ключевые значения, в формате, beanName.property=newvalue
переопределяющем основные реквизиты.
application-mainConfig.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="org.postgresql.Driver"
p:url="jdbc:postgresql://localhost:5432/MyAppDB"
p:username="myusername"
p:password="mypassword"
destroy-method="close" />
application-testConfig.xml:
<import resource="classpath:path/to/file/application-mainConfig.xml"/>
<!-- override bean props -->
<context:property-override location="classpath:path/to/file/beanOverride.properties"/>
beanOverride.properties:
dataSource.driverClassName=org.h2.Driver
dataSource.url=jdbc:h2:mem:MyTestDB
Ответ №7:
Можем ли мы объявить тот же идентификатор компонента в другом xml для другой ссылки, например.x.
Servlet-Initialize.xml
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
Другой xml (Document.xml )
<bean id="inheritedTestBean" class="org.springframework.beans.Document">
<property name="name" value="document"/>
<property name="age" value="1"/>
</bean>
Комментарии:
1. разрешено ли это в spring или нет? Извините, но мне это непонятно.
2. @Buchi Да, это разрешено. Компонент из файла, прочитанный позже в последовательности, полностью переопределит предыдущее определение, которое вообще не будет создано.
3. Вы уверены в этом? Вы уверены, что spring инициализирует / запускает конструктор компонента в конце сканирования? Итак, когда мы внедряем
inheritedtTestBean
в другой компонент, будет внедрена реализация whitch? первый или второй?
Ответ №8:
Вопрос был больше о XML, но поскольку аннотации в наши дни более популярны, и это работает аналогично, я покажу на примере. Давайте создадим класс Foo
:
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
и два файла конфигурации (вы не можете создать один):
@Configuration
public class Configuration1 {
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration1");
return foo;
}
}
и
@Configuration
public class Configuration2 {
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration2");
return foo;
}
}
и давайте посмотрим, что происходит при вызове foo.getName()
:
@SpringBootApplication
public class OverridingBeanDefinitionsApplication {
public static void main(String[] args) {
SpringApplication.run(OverridingBeanDefinitionsApplication.class, args);
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(
Configuration1.class, Configuration2.class);
Foo foo = applicationContext.getBean(Foo.class);
System.out.println(foo.getName());
}
}
в этом примере результатом является: configuration2
.
Контейнер Spring получает все источники метаданных конфигурации и объединяет определения компонента в этих источниках. В этом примере их два @Bean
. Порядок, в котором они загружаются в ApplicationContext
, определяет. Вы можете перевернуть, new AnnotationConfigApplicationContext(Configuration2.class, Configuration1.class);
и результат будет configuration1
.