@Configuration
– это аннотация на уровне класса, указывающая на то, что объект является источником определений бина. Классы, аннотированные @Configuration
, объявляют бины через методы, аннотированные @Bean
. Вызовы методов @Bean
для классов @Configuration
также могут быть использованы для определения межбиновых зависимостей.
Внедрение зависимостей между бинами
Если бины зависят друг от друга, выразить эту зависимость так же просто, как сделать так, чтобы один метод бина вызывал другой, как это показано в следующем примере:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
@Configuration
class AppConfig {
@Bean
fun beanOne() = BeanOne(beanTwo())
@Bean
fun beanTwo() = BeanTwo()
}
В предыдущем примере beanOne
получает ссылку на beanTwo
через внедрение на основе конструктора.
@Bean
, объявлен внутри класса, аннотированного @Configuration
. Нельзя объявлять межбиновые зависимости, используя обычные классы с аннотацией @Component
.Внедрение зависимости через метод поиска
Как отмечалось ранее, внедрение зависимости через метод – это продвинутая функция, которую не следует слишком часто использовать. Она полезна в случаях, если бин, находящийся в области видимости на уровне экземпляра-одиночки, имеет зависимость от бина, находящегося в области видимости на уровне прототипа. Использование Java для такого типа конфигурации обеспечивает естественные средства для реализации такого паттерна. В следующем примере показано, как использовать внедрение зависимости через метод поиска:
public abstract class CommandManager {
public Object process(Object commandState) {
// создаем новый экземпляр соответствующего интерфейса Command
Command command = createCommand();
// устанавливаем состояние для (как ожидается, совершенно нового) экземпляра Command
command.setState(commandState);
return command.execute();
}
// хорошо... но где реализация этого метода?
protected abstract Command createCommand();
}
abstract class CommandManager {
fun process(commandState: Any): Any {
// создаем новый экземпляр соответствующего интерфейса Command
val command = createCommand()
// устанавливаем состояние для (как ожидается, совершенно нового) экземпляра Command
command.setState(commandState)
return command.execute()
}
// хорошо... но где реализация этого метода?
protected abstract fun createCommand(): Command
}
Используя конфигурацию Java, вы можете создать подкласс CommandManager
, в котором абстрактный метод createCommand()
переопределяется таким образом, что он ищет новый (прототип) объект команды. В следующем примере показано, как это сделать:
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// вводим зависимости здесь по мере необходимости
return command;
}
@Bean
public CommandManager commandManager() {
// возвращаем новую анонимную реализацию CommandManager с помощью createCommand()
// переопределяется для возврата нового прототипа объекта Command
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
@Bean
@Scope("prototype")
fun asyncCommand(): AsyncCommand {
val command = AsyncCommand()
// вводим зависимости здесь по мере необходимости
return command
}
@Bean
fun commandManager(): CommandManager {
// возвращаем новую анонимную реализацию CommandManager с помощью createCommand()
// переопределяется для возврата нового прототипа объекта Command
return object : CommandManager() {
override fun createCommand(): Command {
return asyncCommand()
}
}
}
Дополнительная информация о внутренней работе конфигурации на основе Java
Рассмотрим следующий пример, в котором метод, помеченный аннотацией @Bean
, вызывается дважды:
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
@Configuration
class AppConfig {
@Bean
fun clientService1(): ClientService {
return ClientServiceImpl().apply {
clientDao = clientDao()
}
}
@Bean
fun clientService2(): ClientService {
return ClientServiceImpl().apply {
clientDao = clientDao()
}
}
@Bean
fun clientDao(): ClientDao {
return ClientDaoImpl()
}
}
clientDao()
был вызван один раз в clientService1()
и один раз в clientService2()
. Поскольку этот метод создает новый экземпляр ClientDaoImpl
и возвращает его, обычно ожидается, что получится два экземпляра (по одному для каждой службы). Это определенно было создало проблемы: В Spring созданные бины по умолчанию имеют область видимости singleton.
Вот здесь-то и свершается магия: Все классы, аннотированные @Configuration,
подклассифицируются при запуске с помощью CGLIB.
В подклассе дочерний метод сначала проверяет контейнер на наличие кэшированных (входящих в область видимости) бинов, прежде чем вызвать родительский метод и создать новый экземпляр.
Начиная со Spring 3.2, отпала необходимость добавлять CGLIB в ваш classpath, поскольку классы CGLIB были перепакованы в org.springframework.cglib
и включены непосредственно в модуль spring-core JAR.
Существует несколько ограничений, связанных с тем, что CGLIB динамически добавляет функции во время запуска. В частности, конфигурационные классы не должны быть конечными. Однако, начиная с версии 4.3, для конфигурационных классов разрешены любые конструкторы, включая использование аннотации @Autowired
или единственное объявление конструктора не по умолчанию для внедрения по умолчанию.
Если вы предпочитаете избежать каких-либо ограничений, накладываемых CGLIB, рассмотрите возможность объявления методов, помеченных аннотацией @Bean
, для классов, неаннотированных @Configuration
(например, для обычных классов, аннотированных @Component
). Перекрестные вызовы между методами, аннотированными @Bean
, в этом случае не перехватываются, поэтому придется полагаться исключительно на внедрение зависимостей на уровне конструктора или метода.
Составление конфигураций на основе Java
Функция конфигурирования Spring на основе Java позволяет составлять аннотации, что может облегчить конфигурацию.
Использование аннотации @Import
Подобно элементу <import/>
, используемому в XML-файлах Spring, аннотация @Import
позволяет загружать определения, помеченные аннотацией @Bean
, из другого класса конфигурации для облегчения модульной организации конфигураций, как показано в следующем примере:
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
@Configuration
class ConfigA {
@Bean
fun a() = A()
}
@Configuration
@Import(ConfigA::class)
class ConfigB {
@Bean
fun b() = B()
}
Теперь, вместо того чтобы указывать ConfigA.class
и ConfigB.class
при создании экземпляра контекста, необходимо явно указывать только ConfigB
, как показано в следующем примере:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// теперь оба бина A и B будут доступны...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
import org.springframework.beans.factory.getBean
fun main() {
val ctx = AnnotationConfigApplicationContext(ConfigB::class.java)
// теперь оба бина A и B будут доступны...
val a = ctx.getBean<A>()
val b = ctx.getBean<B>()
}
Такой подход упрощает создание экземпляра контейнера, поскольку приходится работать только с одним классом, а не запоминать потенциально большое количество классов @Configuration
во время построения.
@Import
также поддерживает ссылки на обычные классы компонентов, аналогично методу AnnotationConfigApplicationContext.register
. Это особенно полезно, если нужно избежать сканирования компонентов, используя несколько классов конфигурации в качестве точек входа для явного определения всех ваших компонентов.Внедрение зависимостей для импортированных определений, аннотированных @Bean
Предыдущий пример рабочий, но является упрощением. В большинстве практических сценариев бины имеют зависимости друг от друга в разных конфигурационных классах. При использовании XML это не проблема, так как не задействован компилятор, и вы можете объявлять ref="someBean"
, доверив Spring решать эту проблему во время инициализации контейнера. При использовании классов, помеченных аннотацией @Configuration
, компилятор Java накладывает ограничения на конфигурационную модель, заключающиеся в том, что ссылки на другие бины должны иметь допустимый языком Java синтаксис.
К счастью, решить эту проблему просто. Метод, аннотированный @Bean
, может иметь произвольное количество параметров, которые описывают зависимости бина. Рассмотрим следующий более реальный сценарий с несколькими классами, помеченными аннотацией @Configuration
, каждый из которых зависит от бинов, объявленных в других:
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// возвращаем новый DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// все связывается по конфигурационным классам...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
import org.springframework.beans.factory.getBean
@Configuration
class ServiceConfig {
@Bean
fun transferService(accountRepository: AccountRepository): TransferService {
return TransferServiceImpl(accountRepository)
}
}
@Configuration
class RepositoryConfig {
@Bean
fun accountRepository(dataSource: DataSource): AccountRepository {
return JdbcAccountRepository(dataSource)
}
}
@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {
@Bean
fun dataSource(): DataSource {
// возвращаем новый DataSource
}
}
fun main() {
val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
// все связывается по конфигурационным классам...
val transferService = ctx.getBean<TransferService>()
transferService.transfer(100.00, "A123", "C456")
}
Есть и другой способ получить тот же результат. Помните, что классы, аннотированные @Configuration
, в конечном итоге являются лишь еще одним бином в контейнере: Это означает, что они могут использовать преимущества внедрения @Autowired
и @Value
и другие возможности так же, как и любой другой бин.
Убедитесь, что зависимости, которые вы внедряете таким способом, принадлежат исключительно к самому простому типу. Классы, аннотированные @Configuration
, обрабатываются на довольно раннем этапе во время инициализации контекста, и принудительное внедрение зависимости таким способом может привести к непредвиденной ранней инициализации. По возможности используйте внедрение на основе параметров, как в предыдущем примере.
Кроме того, будьте особенно осторожны с определениями BeanPostProcessor
и BeanFactoryPostProcessor
через @Bean
. Обычно их следует объявлять как static
методы, аннотированные @Bean
, не вызывающие создание экземпляра содержащего их конфигурационного класса. В противном случае аннотации @Autowired
и @Value
могут не сработать для самого конфигурационного класса, поскольку его можно создать как экземпляр бина раньше, чем AutowiredAnnotationBeanPostProcessor
.
В следующем примере показано, как один бин может быть автоматически обнаружен и связан с другим бином:
@Configuration
public class ServiceConfig {
@Autowired
private AccountRepository accountRepository;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// возвращаем новый DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// все связывается по конфигурационным классам...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
import org.springframework.beans.factory.getBean
@Configuration
class ServiceConfig {
@Autowired
lateinit var accountRepository: AccountRepository
@Bean
fun transferService(): TransferService {
return TransferServiceImpl(accountRepository)
}
}
@Configuration
class RepositoryConfig(private val dataSource: DataSource) {
@Bean
fun accountRepository(): AccountRepository {
return JdbcAccountRepository(dataSource)
}
}
@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {
@Bean
fun dataSource(): DataSource {
// возвращаем новый DataSource
}
}
fun main() {
val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
// все связывается по конфигурационным классам...
val transferService = ctx.getBean<TransferService>()
transferService.transfer(100.00, "A123", "C456")
}
@Configuration
, поддерживается только начиная со Spring Framework 4.3. Обратите внимание, что нет необходимости указывать @Autowired
, если целевой бин определяет только один конструктор.В предыдущем сценарии использование аннотации @Autowired
работает отлично и обеспечивает желаемую модульность, но определение того, где именно объявляются определения автоматически обнаруженных и связанных бинов, остается несколько неоднозначным. Например, если разработчик смотрит на ServiceConfig
, как узнать, где именно объявлен бин @Autowired AccountRepository
? В коде это не указано явно, и это может быть вполне нормальным явлением. Помните, что Spring Tools for Eclipse предоставляет инструментарий, который может отображать графы, показывающие, каким образом все связано, и это может быть достаточно. Кроме того, ваша интегрированная среда разработки Java может легко осуществлять поиск всех объявлений и использований типа AccountRepository
и быстро отобразить вам расположение методов, помеченных аннотацией @Bean
, которые возвращают этот тип.
В случаях, если такая двусмысленность неприемлема и вам нужна прямая навигация из вашей IDE от одного класса, аннотированного @Configuration
, к другому, рассмотрите возможность автоматического обнаружения и связывания самих классов конфигурации. В следующем примере показано, как это сделать:
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
// выполняем навигацию "через" конфигурационный класс к методу, аннотированному @Bean!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
@Configuration
class ServiceConfig {
@Autowired
private lateinit var repositoryConfig: RepositoryConfig
@Bean
fun transferService(): TransferService {
// выполняем навигацию "через" конфигурационный класс к методу, аннотированному @Bean!
return TransferServiceImpl(repositoryConfig.accountRepository())
}
}
В предыдущей ситуации определение AccountRepository
является полностью явным. Однако ServiceConfig
теперь тесно связан с RepositoryConfig
. Таков компромисс. Эта тесная связь может быть несколько ослаблена за счет использования классов с аннотацией @Configuration
, основанных на интерфейсах или абстрактных классах. Рассмотрим следующий пример:
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
@Configuration
public interface RepositoryConfig {
@Bean
AccountRepository accountRepository();
}
@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(...);
}
}
@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // импортируем конкретную конфигурацию!
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// возвращаем DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
import org.springframework.beans.factory.getBean
@Configuration
class ServiceConfig {
@Autowired
private lateinit var repositoryConfig: RepositoryConfig
@Bean
fun transferService(): TransferService {
return TransferServiceImpl(repositoryConfig.accountRepository())
}
}
@Configuration
interface RepositoryConfig {
@Bean
fun accountRepository(): AccountRepository
}
@Configuration
class DefaultRepositoryConfig : RepositoryConfig {
@Bean
fun accountRepository(): AccountRepository {
return JdbcAccountRepository(...)
}
}
@Configuration
@Import(ServiceConfig::class, DefaultRepositoryConfig::class) // импортируем конкретную конфигурацию!
class SystemTestConfig {
@Bean
fun dataSource(): DataSource {
// возвращаем DataSource
}
}
fun main() {
val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
val transferService = ctx.getBean<TransferService>()
transferService.transfer(100.00, "A123", "C456")
}
Теперь ServiceConfig
слабо связан с конкретным DefaultRepositoryConfig
, а встроенные инструменты IDE по-прежнему полезны: Вы можете легко получить иерархию типов реализаций RepositoryConfig
. Таким образом, навигация по классам, помеченным аннотацией @Configuration
, и их зависимостям ничем не отличается от обычного процесса навигации по коду, основанному на интерфейсе.
@Lazy
(для создания при первом доступе, а не при запуске) или как @DependsOn
других определенных бинов (чтобы другие определенные бины создавались перед текущим бином, помимо тех действий, которые подразумеваются прямыми зависимостями последнего).Условное включение классов @Configuration
или методов @Bean
Часто бывает полезно условно активировать или дезактивировать весь класс, аннотированный @Configuration
, или даже отдельные методы, аннотированные @Bean
, основываясь на некотором произвольном состоянии системы. Одним из распространенных примеров этого подхода является использование аннотации @Profile
для активации бинов только в том случае, если определенный профиль был включен в Environment
Spring.
Аннотация @Profile
фактически реализуема с помощью гораздо более гибкой аннотации под названием @Conditional
. Аннотация @Conditional
указывает на конкретные реализации org.springframework.context.annotation.Condition
, с которыми следует ознакомиться до регистрации @Bean
.
Реализации интерфейса Condition
обеспечивают matches(….)
метод, который возвращает true
или false
. Например, в следующем листинге приведена фактическая реализация Condition
, используемая для аннотации @Profile
:
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// Считываем атрибуты аннотации @Profile
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
// Считываем атрибуты аннотации @Profile
val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name)
if (attrs != null) {
for (value in attrs["value"]!!) {
if (context.environment.acceptsProfiles(Profiles.of(*value as Array<String>))) {
return true
}
}
return false
}
return true
}
Более подробную информацию см. в javadoc по аннотации @Conditional
.
Объединение Java и XML-конфигурации
Поддержка класса, помеченного аннотацией @Configuration
в Spring, не стремится быть 100% полной заменой Spring XML. Некоторые средства, такие как пространства имен в Spring XML, остаются идеальным способом конфигурирования контейнера. В случаях, если XML является удобным или необходимым инструментом, вам дает выбор: либо создавать экземпляр контейнера "XML-ориентированным" способом, используя, например, ClassPathXmlApplicationContext
, либо создать его экземпляр "Java-ориентированным" способом, используя AnnotationConfigApplicationContext
и аннотацию @ImportResource
для импорта XML по мере необходимости.
XML-ориентированное использование классов c аннотацией @Configuration
Более предпочтительным вариантом может быть начальная загрузка контейнера Spring из XML и включение в него классов, аннотированных @Configuration
, в индивидуальном порядке. Например, в случае существующей обширной кодовой базы, использующей Spring XML, проще создавать классы с аннотацией @Configuration
по мере необходимости и осуществлять их включение из существующих XML-файлов. Позже в этом разделе мы рассмотрим варианты использования классов, помеченных аннотацией @Configuration
, в подобном "XML-ориентированном" случае.
Помните, что классы с аннотацией @Configuration
в конечном итоге являются определениями бинов в контейнере. В этой серии примеров мы создадим класс, аннотированный @Configuration
, с именем AppConfig
и включим его в файл system-test-config.xml
в качестве определения <bean/>
. Поскольку <context:annotation-config/>
был включен, контейнер распознает аннотацию @Configuration
и надлежащим образом обрабатывает методы, помеченные аннотацией @Bean
и объявленные в AppConfig
.
В следующем примере показан обычный класс конфигурации на языке Java:
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
@Configuration
class AppConfig {
@Autowired
private lateinit var dataSource: DataSource
@Bean
fun accountRepository(): AccountRepository {
return JdbcAccountRepository(dataSource)
}
@Bean
fun transferService() = TransferService(accountRepository())
}
В следующем примере показана часть образца файла system-test-config.xml
:
<beans>
<!-- активируем обработку аннотаций, таких как @Autowired и @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
В следующем примере показан возможный файл jdbc.properties
:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
fun main() {
val ctx = ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml")
val transferService = ctx.getBean<TransferService>()
// ...
}
system-test-config.xml
класс <bean/> AppConfig
не объявляет элемент id
. Хотя это был бы приемлемый исход, но он излишний, учитывая, что ни один другой бин ни в какой момент не ссылается на него, и он вряд ли будет явно получен из контейнера по имени. Аналогичным образом бин DataSource
автоматически обнаруживается и связывается только по типу, поэтому явный id
бина не является строго обязательным.Поскольку @Configuration
мета-аннотируется с помощью аннотации @Component
, классы, помеченные аннотацией @Configuration
, автоматически становятся кандидатами на сканирование компонентов. Используя тот же сценарий, что описан в предыдущем примере, мы можем переопределить system-test-config.xml
, чтобы воспользоваться преимуществами сканирования компонентов. Обратите внимание, что в этом случае нам не нужно явно объявлять <context:annotation-config/>
, потому что <context:component-scan/>
обеспечивает ту же функциональность.
В следующем примере показан измененный файл system-test-config.xml
:
<beans>
<!-- определяет и регистрирует AppConfig как определение бина -->
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
Использование XML с аннотацией@ImportResource
, ориентированное на класс, аннотированный @Configuration
В приложениях, где классы, аннотированные @Configuration
, являются основным механизмом для конфигурирования контейнера, все же, вероятно, приходится использовать XML хотя бы в некоторой степени. В таких сценариях можно использовать аннотацию @ImportResource
и определить только тот объем XML, который необходим. Таким образом получаем "Java-ориентированный" подход к конфигурированию контейнера, а использование XML сводится к минимуму. Следующий пример (который предусматривает конфигурационный класс, XML-файл, определяющий бин, файл свойств, а также main
класс) демонстрирует, как использовать аннотацию @ImportResource
для получения "Java-ориентированной" конфигурации, в которой XML используется по мере необходимости:
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
class AppConfig {
@Value("\${jdbc.url}")
private lateinit var url: String
@Value("\${jdbc.username}")
private lateinit var username: String
@Value("\${jdbc.password}")
private lateinit var password: String
@Bean
fun dataSource(): DataSource {
return DriverManagerDataSource(url, username, password)
}
}
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
import org.springframework.beans.factory.getBean
fun main() {
val ctx = AnnotationConfigApplicationContext(AppConfig::class.java)
val transferService = ctx.getBean<TransferService>()
// ...
}