Неверный оператор привязки — относительно распространенное исключение в Mybaits:
org.apache.ibatis.binding.bindingexception:
invalid bound statement (not found):
com.dhy.testMapper.query
Независимо от конкретной причины этого исключения, окончательный анализ показывает, что интерфейс преобразователя и соответствующий XML-файл не были успешно связаны.
Существует множество причин, по которым интерфейс преобразователя и XML не могут быть успешно связаны, но большинство причин относительно легко найти. В этом разделе я хотел бы поделиться причиной, которую не так-то легко найти:
Предположим, у нас есть два источника данных, один — DataSourceA, а другой — DataSourceB, тогда мы можем настроить его вот так в среде Mybaits-Spring:
@Bean
public SqlSessionFactory sqlSessionFactoryOfDataSourceA(@Qualifier("datasourceA") DataSource dataSourceA) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSourceA);
sessionFactoryBean.setConfigLocation(configLocation);
sessionFactoryBean.setTypeAliasesPackage("com.dhy.mapper");
Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:dhy/mapper/*.xml");
sessionFactoryBean.setMapperLocations(resources);
return sessionFactoryBean.getObject();
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurerOfDataSourceA() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactoryOfDataSourceA");
mapperScannerConfigurer.setBasePackage("com.dhy.mapper");
return mapperScannerConfigurer;
}
@Bean
public SqlSessionFactory sqlSessionFactoryOfDataSourceB(@Qualifier("datasourceB") DataSource dataSourceB) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSourceB);
sessionFactoryBean.setConfigLocation(configLocation);
sessionFactoryBean.setTypeAliasesPackage("com.dhy.mapper.b");
Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath:dhy/mapper/*.xml");
sessionFactoryBean.setMapperLocations(resources);
return sessionFactoryBean.getObject();
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurerOfDataSourceB() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactoryOfDataSourceB");
mapperScannerConfigurer.setBasePackage("com.dhy.mapper.b");
return mapperScannerConfigurer;
}
Благодаря приведенной выше информации о конфигурации мы можем наблюдать, что диапазоны сканирования MapperScannerA и MapperScannerB перекрываются, то есть MapperScannerA также будет сканировать интерфейс картографа MapperScannerB:
Фактически, по умолчанию mybaits-spring автоматически вводит MapperScanner в IOC. Этот сканер Mapper отличается от двух сканеров, которые мы настроили выше. По умолчанию он запускается на основе собственного диапазона сканирования пакета SpringBoot, рекурсивно сканирует и получает. все Интерфейс, отмеченный аннотацией @Mapper:
Два представленных выше сканера рекурсивно получают все классы по указанному пути к пакету, независимо от того, добавлена ли аннотация @Mapper.
/**
Auto-Configuration for Mybatis.
Contributes a SqlSessionFactory and a SqlSessionTemplate.
If org.mybatis.spring.annotation.MapperScan is used,
or a configuration file is specified as a property,
those will be considered,
otherwise this auto-configuration will attempt to register mappers
based on the interface definitions in or under the root auto-configuration package.
*/
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
...
//Если мы не предоставляем SqlSessionFactory, он будет автоматически внедрен по умолчанию
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
...
}
//Если мы не предоставляем SqlSessionTemplate , один из них будет автоматически введен по умолчанию
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
...
}
/**
MapperScan ultimately ends up creating instances of MapperFactoryBean.
If MapperScan is used then this auto-configuration is not needed.
If it is _not_ used, however, then this will bring in a bean registrar and automatically register components based on the same component-scanning path as Spring Boot itself.
*/
@org.springframework.context.annotation.Configuration
@Import({ AutoConfiguredMapperScannerRegistrar.class })
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration {
@PostConstruct
public void afterPropertiesSet() {
logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
}
MybatisAutoConfiguration будет работать, когда у нас нет Конфигурация sqlSessionFactory и sqlSessionTemplate.,Автоматически помогите нам внедрить,И его внутренний статический класс MapperScannerRegistrarNotFoundConfiguration.,Если мы не используем аннотации MapperScan для указания пути сканирования пакета вручную, мы поможем нам импортировать класс AutoConfiguredMapperScannerRegistrar, который отвечает за сканирование на основе собственного диапазона сканирования пакета SpringBoot.
/**
This will just scan the same base package as Spring Boot does.
If you want more power, you can explicitly use org.mybatis.spring.annotation.MapperScan but this will get typed mappers working correctly, out-of-the-box, similar to using Spring Data JPA repositories.
*/
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
logger.debug("Searching for mappers annotated with @Mapper");
//сканер картографа, предоставленный mybaits
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
//Получаем путь сканирования пакета текущего проекта SpringBoot
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
...
//Настраиваем фильтр на основе аннотации
scanner.setAnnotationClass(Mapper.class);
//зарегистрироваться Процесс сканирования пакет Фильтровать ожидаемый результат, установленный пользователем
//После того, как мы настроили аннотацию выше, она будет инкапсулирована как AnnotationTypeFilter и зарегистрирована в сканере.
scanner.registerFilters();
//Сканирование пакетов
scanner.doScan(StringUtils.toStringArray(packages));
}...
}
...
}
Вызовите метод сканирования класса ClassPathMapperScanner, предоставленный mybaits, для завершения обработки пакета пакета:
ClassPathMapperScanner наследует ClassPathBeanDefinitionScanner. Сканирование пакетов и возможность фильтрации по фильтру во время процесса сканирования уже предусмотрены Spring.
/**
* Calls the parent search that will search and register all the candidates. Then the registered objects are post
* processed to set them as MapperFactoryBeans
*/
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//Вызов метода doScan ClassPathBeanDefinitionScanner для завершения сканирования пакета и фильтрации фильтра.
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
//mybaits отвечает за обработку определения компонента
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
Для AutoConfiguredMapperScannerRegistrar набор результатов, полученный в результате вышеуказанной фильтрации, представляет собой все классы, помеченные @Mapper, в указанном наборе путей к пакету.
После того, как Mybaits завершит фильтрацию с помощью сканера пакетов Spring, следующим шагом будет дальнейшая обработка квалифицированного BeanDefintion:
При сканировании пакетов получаются интерфейсы сопоставителей, но в конечном итоге mybaits необходимо сгенерировать прокси-объекты для этих интерфейсов, и тип объекта Bean, окончательно введенный пользователем, также должен быть прокси-объектом. Таким образом, тип преобразователя, который фактически внедряется в контейнер с помощью mybaits, — это FactoryBean, то есть его метод getObject отвечает за создание прокси-объекта для текущего интерфейса преобразователя и последующий его возврат.
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
...
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
//Добавляем параметры конструктора для MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//Добавляем сопоставление отношений внедрения атрибутов
definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
//Устанавливаем фактический тип текущего компонента MapperFactoryBeanClass
definition.setBeanClass(this.mapperFactoryBeanClass);
//Добавляем сопоставление отношений внедрения атрибутов
definition.getPropertyValues().add("addToConfig", this.addToConfig);
...
//Установили ли мы свойство sqlSessionFactoryBeanName для ClassPathMapperScanner
// Установка этого атрибута указывает, что интерфейс картографа, сканируемый текущим сканером, в конечном итоге будет передан этому sqlSessionFactory для управления.
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
//Если установлено, добавляем сопоставление атрибутов
definition.getPropertyValues().add("sqlSessionFactory",
//RuntimeBeanReference будет заменен фактическим экземпляром sqlSessionFactoryBean на этапе внедрения свойств компонента.
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
//Или мы напрямую указываем экземпляр объекта sqlSessionFactory, тогда мы можем напрямую заменить RuntimeBeanReference фактическим экземпляром bean-компонента
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
//Если указан sqlSessionTemplate, это также возможно.
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
//Если ни sqlSessionTemplate, ни sqlSessionFactory не указаны, то этот параметр поддерживает автоматическое внедрение.
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
//Устанавливаем, нужно ли отложенной загрузке текущего bean-компонента
definition.setLazyInit(lazyInitialization);
...
//Настройки области
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
...
}
}
МетодprocessBeanDefinitions в основном определяет соответствующие отношения сопоставления внедрения зависимостей для текущего MapperFactoryBean, что эквивалентно ручной спецификации propertyValues.
В дополнение к спецификации кодирования вручную мы также можем указать отношения отображения внедрения зависимостей следующими способами:
Еще один момент, на который всем необходимо обратить внимание, это то, что если мы не укажем beanName или экземпляр bean-компонента sqlSessionFactory или sqlSessionTemplate для текущего ClassPathMapperScanner, то сканер включит автоматическое внедрение в соответствии с типом для текущего bean-компонента:
Что такое автоматический впрыск?
Автоматическое внедрение означает, что если мы не укажем вручную отношение отображения внедрения зависимостей с помощью вышеуказанного метода, Spring автоматически попытается выполнить внедрение зависимостей для нашего текущего объекта bean-компонента:
Логика вышеописанного процесса отражена в методе populateBean класса AbstractAutowireCapableBeanFactory, который является этапом внедрения атрибутов компонента:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
...
//Получаем существующую коллекцию PropertyValues в beanDefintion ---в коллекции могут быть разработки картографирование зависимостей
//Например: Мы передаем текущий bean-компонент через xmlConfiguration или вручную обрабатываем коллекцию PropertyValues, как указано выше.
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
//Получаем режим автоматического внедрения текущего компонента — по умолчанию он не включен
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
//Если для текущего компонента включен режим автоматического внедрения, определите, следует ли внедрять по имени или по типу.
//Например: Если режим автоматического внедрения включен и автоматическое внедрение выполняется в соответствии с типом, Spring попытается автоматически внедрить каждое свойство текущего компонента.
//Если определенные атрибуты могут быть автоматически успешно внедрены в соответствии с типом, то создаем соответствующую реализацию зависимостей Отображение отношений---propertyValue
//Затем добавляем propertyValue в существующую коллекцию PropertyValues текущего bean-компонента
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
//Постпроцессор обрабатывает логику внедрения сопоставления зависимостей, указанную в режиме аннотации, и опускает ее.
...
//Наконец, используем BeanWrapper для взаимодействия с модулем преобразования типов Spring
//По реализации, сохраненной в коллекции PropertyValues отображение взаимосвязей зависимостей, завершение внедрения на основе метода установки зависимостей
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
Как упоминалось выше, без указания sqlSessionTemplate или sqlSessionFactory для ClassPathMapperScanner ClassPathMapperScanner включит режим автоматического внедрения в соответствии с типом для каждого сканируемого MapperFactoryBean:
Конечно, предпосылкой возникновения этой проблемы является то, что автоматически внедренный ClassPathMapperScanner сканирует интерфейс картографа, отмеченный аннотацией @Mapper.
Есть две идеи решения этой проблемы:
Информацию о реализации аннотации @MapperScan см. в дополнительном примечании 2.
Конечно, когда эта проблема возникает при нормальных обстоятельствах, вы можете пометить один из bean-компонентов аннотацией @Primary, чтобы сообщить Spring, что при появлении нескольких потенциальных bean-компонентов приоритет должен отдаваться bean-компоненту, отмеченному аннотацией @Primary.
В сценарии конфигурации с несколькими источниками данных мы можем одновременно настроить несколько ClassPathMapperScanner и SqlSessionFactory, которые отвечают за сканирование интерфейсов сопоставителя по разным путям пакетов.
Интерфейсы сопоставителя в разных путях пакетов принадлежат разным источникам данных, поэтому они управляются разными SqlSessionFactory:
Как показано на рисунке выше, сканер A и сканер B сканируют разные пути пакетов соответственно, но путь, сканируемый сканером A, перекрывает путь сканера B, и поскольку сканер A имеет приоритет над сканером B, это приведет к тому, что устройство B не сможет сканировать. интерфейс сопоставления соответствующего пакета. Почему это?
Это связано с тем, что метод doScan класса ClassPathBeanDefinitionScanner регистрирует bean-компоненты, сканированные каждым сканером. Если bean-компонент, сканируемый текущим сканером, уже существует в контейнере, текущий сканер пропустит его обработку:
/**
Check the given candidate's bean name, determining whether the corresponding bean definition needs to be registered or conflicts with an existing definition.
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
//Определения двух bean-компонентов одинаковы, то есть они совместимы друг с другом
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
//Если два bean-компонента имеют одинаковое имя, но определения bean-компонента несовместимы, будет выброшен аномальный объект, сообщающий об ошибке, что beanName сопоставлен с двумя разными bean-компонентами.
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}
Есть две идеи для решения проблемы, с которой мы столкнулись выше:
Отметка аннотации @Order в методе @Bean влияет на порядок загрузки компонента. Не заблуждайтесь!
Идея 2 состоит в том, чтобы предотвратить перекрытие пути, сканируемого сканером A, с путем, сканируемого сканером B:
Также обратите внимание на то, чтобы отключить автоматически внедряемый сканер по умолчанию или не отмечать аннотацию @Mapper в интерфейсе картографа.
MapperScannerConfigurer — это, по сути, постпроцессор фабричного компонента Bean, используемый для добавления некоторых дополнительных определений BeanDefinitions в контейнер после завершения инициализации подготовки BeanFactory.
Его основной метод — postProcessBeanDefinitionRegistry:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
//В соответствии со значением, которое мы установили для MapperScannerConfigurer, затем установите его в ClassPathMapperScanner середина
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
//зарегистрироватьсяFilter,Затем запустите сканирование пакет, путь сканирования пакета — это тот, который мы установили в методе @Bean середина
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
Основная роль аннотации MapperScan — импорт MapperScannerRegistrar в контейнер. MapperScannerRegistrar наследует интерфейс ImportBeanDefinitionRegistrar и отвечает за регистрацию дополнительных beanDefinitions в контейнере.
Тип компонента, зарегистрированный MapperScannerRegistrar в контейнере, — это не что иное, как MapperScannerConfigurer, представленный выше. Атрибуты в аннотации MapperScan соответствуют атрибутам в классе конфигурации MapperScannerConfigurer один к одному:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//Получаем все атрибуты аннотации середина
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//Регистрация MapperScannerConfigurer
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// Тип bean-компонента, который необходимо зарегистрировать, — MapperScannerConfigurer.
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
//Если вы указали сканирование и фильтрацию пакетов по аннотации, то настроим по умолчанию, будет собираться весь интерфейс под указанным пакетом.
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
//Указан ли путь сканирования пакета в аннотациисередина
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList())); basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
//Если путь сканирования пакета не указан, выбирается путь сканирования пакета по умолчанию.
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
String defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// for spring-native
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index;
}
private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) {
return ClassUtils.getPackageName(importingClassMetadata.getClassName());
}