Руководство по Spring. Автосвязывание бинов.
Ранее мы уже изучили, как объявлять и связывать бины с помощью таких тегов, как и в XML-файле.
Но Spring Framework позволяет нам осуществлять автоматическое связывание бинов (autowiring). Это позволяет нам существенно сократить объём кода в конфигурационном XML-файле, что особенно помогает при написании больших приложений.
Существует несколько режимов автоматического связывания в Spring:
(пусто)
Этот режим установлен по умолчанию и означает отсутствие автоматического связывания. Т.е. мы должны использовать прямую ссылку на бин для связывания. Такой способ мы уже рассматривали в статье внедрение зависимостей.
Используется автоматической связывание по имени бина. Контейнер Spring ищет в XML файле бин с указанным именем. И если находит бин с таким же именем – производит автоматическое связывание.
Автоматическое связывание по типу. В этом случае контейнер Spring ищет совпадение по типу. В случае, если он находит – происходит автоматическое связывание. Если в XML-файле определены несколько бинов с таким типом, то мы получаем исключение (exception).
Здесь всё происходит так же, как и при использовании режима ‘byType’, с тем отличием, что поиск идёт по аргументам конструктора. Если в XML-файле находится несколько таких бинов – мы получаем ошибку (error).
autodetect
В этом режиме сначала происходит связывание в режиме ‘constructor’, а затем (если автосвязывание не произошло) в режиме ‘byType’.
Для более ясного понимания темы, настоятельно рекомендую ознакомиться с примерами приложений, указанными по ссылкам.
В этой статье мы ознакомились с основами автоматического связывания в Spring Framework.
Узнайте, как привязать бины Spring с использованием XML и классов Java
Для подключения компонентов Spring используются два подхода: конфигурация XML и класс Java. Привязка может быть выполнена вручную путем настройки XML- файлов или с помощью классов Java. Также можно позволить Spring выполнить привязку на основе нескольких спецификаций.
Основная цель этой статьи – показать, как компоненты Spring могут быть привязаны с помощью двух упомянутых выше подходов.
Для чего используется фреймворк Spring?
Spring Framework — это одна из популярных сред разработки приложений, которая имеет следующие преимущества:
- Использование только нужных модулей.
Spring предлагает множество различных пакетов и классов. А также позволяет использовать только нужные из них. - Возможность применения других технологий .
Spring использует другие технологии: фреймворки логов, ORM, таймеры Quartz, JEE и JDK и другие.
- Компактность
Контейнер Spring легче по сравнению с контейнером EJB. Он развертывать приложения на основе Spring на маломощных компьютерах. - Гибкая система управления транзакциями
Интерфейс управления транзакциями Spring может использоваться обработки как глобальных транзакций (Java Transaction API), так и локальных транзакций (в одной базе данных).
Что такое привязка бинов?
Когда запускается программное приложение, совместно работают сразу несколько объектов (бинов). Также они могут использоваться независимо от других объектов.
Чтобы заставить бины работать вместе, их связывают через введение зависимостей в Spring (привязки). Когда запускается приложение на основе Spring, контекст приложения загружает определения компонентов или объектов из файла конфигурации и связывает их вместе.
Существует два подхода к привязке bean-компонентов в среде Spring:
- Привязка через XML.
- Привязка через классы Java.
Что такое автоматическая привязка?
Существует два способа привязки bean-компонентов в среде Spring:
- Вручную, объявив bean-компоненты с помощью Dependency Injection (DI)
- Позволить контейнеру Spring самостоятельно связать необходимые компоненты.
Последний способ известен как автоматическая привязка. При этом контейнеру Spring нужно указать, как он должен их связывать:
- byName: контейнер Spring ищет бины, для которых в файле конфигурации XML для атрибута auto wire установлено значение byName.
- byType: контейнер ищет бины, для которых в файле конфигурации XML для атрибута auto wire установлено значение byType.
- constructor: выполняется поиск бинов, для которых для атрибута auto wire установлено значение constructor.
- autodetect: способ автоматического подключения — через конструктор. Если он не работает, автоматическое подключение выполняется через параметр byType.
Что такое привязка через Java и XML?
Подключение bean-компонентов Spring может выполняться через классы Java и с помощью конфигурации XML. В этом разделе мы рассмотрим оба варианта:
XML: Подключение выполняется в файле конфигурации XML. Он содержит определения bean-компонентов. Когда запускается приложение, бины вызываются с помощью Dependency Injection (DI). В файле конфигурации необходимо объявить элемент и использовать элементы , .
Классы Java: Необходимо создать класс Spring Configuration и аннотировать класс @Configuration. Класс @Configuration сообщает Spring, что bean-компоненты, которые должны быть подключены и настроены в этом классе.
Настройка среды
Настройка среды для разработки приложений Spring, включает в себя всего три основных шага.
- Настройка Java Development Kit
Загрузите JDK с официального сайта Oracle, а затем установите и настройте его. После этого необходимо задать переменные среды PATH и JAVA_HOME. - Настройка Eclipse IDE
Eclipse можно скачать с официального сайта . После завершения загрузки распакуйте двоичные файлы, а затем установите PATH. - Настройка библиотек Spring
Spring можно скачать здесь . Важно правильно установить CLASSPATH.
Пример приложения
Мы разработаем два приложения. В первом бины будут подключаться с использованием аннотаций, а во втором приложении – с помощью XML-файла конфигурации.
Подключение через аннотации — у нас будет три класса Java, как показано ниже.
Листинг 1: Класс бина
package com.dashboardapp.beanwire; public class BeanWire < private String message; public void setMessage(String message)< this.message = message; >public void getMessage() < System.out.println("Message is : " + message); >>
В следующем классе для информирования контейнера Spring о том, что будет загружен beanWire, используются две аннотации. Аннотация @Configuration сообщает контейнеру Spring, что этот класс является источником определений бина.
Аннотация @Bean информирует среду Spring, что метод вернет объект, который должен быть зарегистрирован как компонент в контексте приложения.
Листинг 2: Класс конфигурации Bean с аннотациями
package com.dashboardapp.beanwire; import org.springframework.context.annotation.*; @Configuration public class BeanWireConfig < @Bean public BeanWire beanwire()< return new BeanWire(); >>
Ниже приведен последний класс для запуска приложения.
Листинг 3: Основной класс для запуска приложения
package com.dashboardapp.beanwire; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; public class BeanWireMain < public static void main(String[] args) < ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanWireConfig.class); BeanWire beanwire = ctx.getBean(BeanWire.class); beanwire.setMessage("Hello Bean. You are wired by annotation. "); beanwire.getMessage(); >>
Когда эта Java-программа будет выполнена, она выдаст следующий результат.
Теперь рассмотрим, как привязывать бины через XML-файл.
Привязка через XML-файл: У нас будет два Java-файла и один XML-файл конфигурации.
Java-класс бина практически аналогичен предыдущему.
Листинг 1: Файл бина
package com.dashboardapp.beanwire; public class BeanWireXML < private String message; public void setMessage(String message)< this.message = message; >public void getMessage() < System.out.println("Message is : " + message); >>
Это XML-файл конфигурации.
Листинг 2: Конфигурационный XML-файл.

Это файл для получения компонента из XML-файла конфигурации и вызова методов.
Листинг 3: Основной Java-файл.
Package com.dashboardapp.beanwire; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class BeanWireXMLMain < public static void main(String[] args) < ApplicationContext context = new FileSystemXmlApplicationContext( "WebContent/WEB-INF/appConfig.xml"); BeanWireXML beanwireobj = (BeanWireXML)context.getBean("beanwire"); beanwireobj.setMessage("Hello Bean. You are wired by XML config. "); beanwireobj.getMessage(); >>
Запустите приложение, и оно выдаст следующий результат.

Заключение
Автоматическая привязка требует осторожности. Не все привязки могут выполняться с помощью автоматического подключения. Оно не подходит для сложных привязок, и это следует учитывать.
Вадим Дворников автор-переводчик статьи « Learn How to wire Spring Beans by using XML and Java classes »
Как обеспечить доступ к бинам для классов, не являющихся точкой входа в приложение (и бинами)?
Из ответов на вопрос Как убрать приведение типа из конструкции фреймворка Spring applicationContext.getBean ? я узнал, что для соблюдения инверсии контроля использовать applicationContext можно только в конфигурационных классах Spring, а также в точке входа, например:
public class EntryPoint < public static void main (String[] args) < ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationContext.class); FilesManager filesManager = applicationContext.getBean(FilesManager.class); PeopleGateway peopleGateway = applicationContext.getBean(PeopleGateway.class); filesManager.initializeUserFiles(); GUI_Service.startGUI(); >>
Каким же образом принято обеспечивать доступ к бинам из всех остальных классов? Например, в данном приложении GUI_Service будет вызывать у peopleGateway разные методы — getAllPeolple , getPersonByID и так далее. К контексту GUI_Service обращаться вроде как не должен. А в точке входа peopleGateway пока что и вовсе не нужен. Обычный способ — передать peopleGateway либо через конструктор GUI_Service , либо через какой-нибудь инициализирующий метод. Что даёт Spring? GUI_Service на данный момент пока не является бином.
Несколько баз данных для Spring Boot приложения
Иногда бывает невозможно объединить базы данных в одну. Давайте разберемся, как добавить подключение к двум разным провайдерам баз данных.
27 нояб. 2021 · 5 минуты на чтение
Существование Spring Boot настолько упростило создание приложений, что разработчикам для подключении к базе данных уже даже не обязательно знать о существовании EntityManager . Поэтому работа сразу с двумя БД может оказаться не простой задачей.
Так как для подключения сразу двух баз данных в одно SpringBoot приложение одной конфигурацией не обойтись, необходимо руками прописать все создаваемые бины. SpringBoot здесь не помощник, вспоминаем старый добрый Spring. Давайте разбираться.
Спонсор поста
Постановка задачи
По вымышленному ТЗ нам необходимо написать небольшое CRUD приложение для правительства РФ, которое анализирует судебные дела. Проблема заключается в том, что данные об адвокатах у нас лежат в одной базе, а данные о судьях в другой.
Наше приложение будет максимально простым:
- Две сущности. Два репозитория. Два REST контроллера;
- CRUD операции;
- Две базы данных: Postgres и H2;
Проект на GitHub: multiple-databases-spring-boot
Создание классов
Для начала создадим наши сущности это Judge и Lawyer , которые наследуют Person .
Базовая сущность Person выглядит следующим образом:
package dev.struchkov.example.multipledatabases.domain;import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.GenericGenerator; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import java.util.UUID;@Setter @Getter @MappedSuperclass public class Person
Сущность Lawyer выглядит аналогично сущности Judge , за исключением того, что они лежат в разных пакетах. Сущности для одной БД должны лежать в одном пакете, а сущности для второй БД в другом пакете.
Теперь создадим JPA репозитории, которые тоже положим в разные пакеты.
package dev.struchkov.example.multipledatabases.repository.dbone;
@Repository public interface JudgeRepository extends JpaRepository
И контроллеры для каждой сущности. Они могут лежать в одном пакете.
package dev.struchkov.example.multipledatabases.controller; import dev.struchkov.example.multipledatabases.domain.entity.dbtwo.Lawyer; import dev.struchkov.example.multipledatabases.repository.dbtwo.LawyerRepository; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.UUID; @RestController @RequiredArgsConstructor @RequestMapping("api/lawyer") public class LawyerController < private final LawyerRepository lawyerRepository; @PostMapping public ResponseEntitycreate(@RequestBody Lawyer lawyer) < return ResponseEntity.ok(lawyerRepository.save(lawyer)); >@GetMapping("") public ResponseEntity getById(@PathVariable UUID id) < return ResponseEntity.ok(lawyerRepository.findById(id).orElseThrow()); >@DeleteMapping("") public HttpStatus delete(@PathVariable UUID id) < lawyerRepository.deleteById(id); return HttpStatus.OK; >>
Конфигурации БД
Теперь дело за конфигурацией для баз данных. Сначала создаем конфигурацию для Judge .
package dev.struchkov.example.multipledatabases.config; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; @EnableJpaRepositories( entityManagerFactoryRef = DatabaseOneConfig.ENTITY_MANAGER_FACTORY, transactionManagerRef = DatabaseOneConfig.TRANSACTION_MANAGER, basePackages = DatabaseOneConfig.JPA_REPOSITORY_PACKAGE ) @Configuration public class DatabaseOneConfig < public static final String PROPERTY_PREFIX = "app.dbone.datasource"; public static final String JPA_REPOSITORY_PACKAGE = "dev.struchkov.example.multipledatabases.repository.dbone"; public static final String ENTITY_PACKAGE = "dev.struchkov.example.multipledatabases.domain.entity.dbone"; public static final String ENTITY_MANAGER_FACTORY = "oneEntityManagerFactory"; public static final String DATA_SOURCE = "oneDataSource"; public static final String DATABASE_PROPERTY = "oneDatabaseProperty"; public static final String TRANSACTION_MANAGER = "oneTransactionManager"; @Bean(DATABASE_PROPERTY) @ConfigurationProperties(prefix = PROPERTY_PREFIX) public DatabaseProperty appDatabaseProperty() < return new DatabaseProperty(); >@Bean(DATA_SOURCE) public DataSource appDataSource( @Qualifier(DATABASE_PROPERTY) DatabaseProperty databaseProperty ) < return DataSourceBuilder .create() .username(databaseProperty.getUsername()) .password(databaseProperty.getPassword()) .url(databaseProperty.getUrl()) .driverClassName(databaseProperty.getClassDriver()) .build(); >@Bean(ENTITY_MANAGER_FACTORY) public LocalContainerEntityManagerFactoryBean appEntityManager( @Qualifier(DATA_SOURCE) DataSource dataSource ) < final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); em.setPersistenceUnitName(ENTITY_MANAGER_FACTORY); em.setPackagesToScan(ENTITY_PACKAGE); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); final HashMapproperties = new HashMap<>(); properties.put("javax.persistence.validation.mode", "none"); properties.put("hibernate.hbm2ddl.auto", "update"); properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); em.setJpaPropertyMap(properties); return em; > @Bean(TRANSACTION_MANAGER) public PlatformTransactionManager sqlSessionTemplate( @Qualifier(ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean entityManager, @Qualifier(DATA_SOURCE) DataSource dataSource ) < final JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManager.getObject()); transactionManager.setDataSource(dataSource); return transactionManager; >>
Чтобы работать с двумя базами нужно создать для каждой БД свой EntityManagerFactory и TransactionManager . А также указать, какие JPA репозитории и Entity относятся к этой БД.
В наших конфигурациях будут отличаться только строки 12-18 это переменные настройки:
- PROPERTY_PREFIX – Префикс для конфигурации в файле application.property
- JPA_REPOSITORY_PACKAGE – Путь до пакета, где лежат JPA репозитории для данной БД
- ENTITY_PACKAGE – Путь до пакета, где лежат Entity
- ENTITY_MANAGER_FACTORY – Название бина EntityManager
- DATA_SOURCE – Название бина DataSource
- DATABASE_PROPERTY – Название бина DatabaseProperty
- TRANSACTION_MANAGER – Название бина TransactionManager
Названия для бинов нужны, чтобы спринг из двух вариантов выбирал правильный для конкретной БД.
DatabaseProperty
Первый бин DatabaseProperty отвечает за передачу данных из файла aplication.property для подключения к бд: пароль, логин, url и драйвер.
package dev.struchkov.example.multipledatabases.config;import lombok.Getter; import lombok.Setter;@Getter @Setter public class DatabaseProperty
Создадим необходимые записи в aplication.property . Не забываем про PROPERTY_PREFIX .
app: dbone: datasource: class-driver: org.h2.Driver username: sa password: password url: jdbc:h2:mem:dbh2
DataSource
Теперь, когда у нас есть данные для подключения к БД создаем DataSource . С помощью @Qualifier указываем какой именно бин DatabaseProperty нам нужен.
EntityManager
Далее нам нужен EntityManagerFactoryBean .
- В 44 строке мы указываем наш DataSource .
- В 45 строке устанавливаем название для EntityManager .
- В 46 строке мы передаем путь до пакета, в котором у нас лежат Entities.
- А строки 49-53 позволяют задать проперти для Hibernate.
TransactionManager
Ну и последним шагом создаем бин PlatformTransactionManager . Для работы ему нужны бины EntityManager и Datasource .
Вторая база данных
Для второй базы данных будут отличаться только параметры настройки.
package dev.struchkov.example.multipledatabases.config; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; @EnableJpaRepositories( entityManagerFactoryRef = DatabaseTwoConfig.ENTITY_MANAGER_FACTORY, transactionManagerRef = DatabaseTwoConfig.TRANSACTION_MANAGER, basePackages = DatabaseTwoConfig.JPA_REPOSITORY_PACKAGE ) @Configuration public class DatabaseTwoConfig < public static final String PROPERTY_PREFIX = "app.dbtwo.datasource"; public static final String JPA_REPOSITORY_PACKAGE = "dev.struchkov.example.multipledatabases.repository.dbtwo"; public static final String ENTITY_PACKAGE = "dev.struchkov.example.multipledatabases.domain.entity.dbtwo"; public static final String ENTITY_MANAGER_FACTORY = "twoEntityManagerFactory"; public static final String DATA_SOURCE = "twoDataSource"; public static final String DATABASE_PROPERTY = "twoDatabaseProperty"; public static final String TRANSACTION_MANAGER = "twoTransactionManager"; @Bean(DATABASE_PROPERTY) @ConfigurationProperties(prefix = PROPERTY_PREFIX) public DatabaseProperty appDatabaseProperty() < return new DatabaseProperty(); >@Bean(DATA_SOURCE) public DataSource appDataSource( @Qualifier(DATABASE_PROPERTY) DatabaseProperty databaseProperty ) < return DataSourceBuilder .create() .username(databaseProperty.getUsername()) .password(databaseProperty.getPassword()) .url(databaseProperty.getUrl()) .driverClassName(databaseProperty.getClassDriver()) .build(); >@Bean(ENTITY_MANAGER_FACTORY) public LocalContainerEntityManagerFactoryBean appEntityManager( @Qualifier(DATA_SOURCE) DataSource dataSource ) < LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); em.setPersistenceUnitName(ENTITY_MANAGER_FACTORY); em.setPackagesToScan(ENTITY_PACKAGE); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); HashMapproperties = new HashMap<>(); properties.put("javax.persistence.validation.mode", "none"); properties.put("hibernate.hbm2ddl.auto", "update"); em.setJpaPropertyMap(properties); return em; > @Bean(TRANSACTION_MANAGER) public PlatformTransactionManager sqlSessionTemplate( @Qualifier(ENTITY_MANAGER_FACTORY) LocalContainerEntityManagerFactoryBean entityManager, @Qualifier(DATA_SOURCE) DataSource dataSource ) >
Указываем для поиска другие пакеты и другие названия бинов, а также указываем другой префикс для передачи параметров подключения к БД.
Записываем эти параметры в application.yml :
app: dbone: datasource: class-driver: org.h2.Driver username: sa password: password url: jdbc:h2:mem:dbh2 dbtwo: datasource: class-driver: org.postgresql.Driver username: postgres password: 12345 url: jdbc:postgresql://localhost:5432/dbpostgres
Резюмирую
Чтобы создать соединение сразу с несколькими базами данных, необходимо самостоятельно прописать необходимые бины для работы.
Предыдущая запись
