营销型网站建设公司哪家建设,音乐网站建设视频教程,时事热点新闻,公益网站建设方案group : com.baomidou version:3.5.2.2-SNAPSHOT baomidou官网可以从快速开始了解到#xff0c;除了配置数据源#xff0c;最重要的就是MapperScan 注解#xff0c;在 Spring Boot 启动类中添加 MapperScan 注解#xff0c;扫描 Mapper 文件夹。
MapperScan
按照惯例除了配置数据源最重要的就是MapperScan 注解在 Spring Boot 启动类中添加 MapperScan 注解扫描 Mapper 文件夹。
MapperScan
按照惯例先看注释。在用java config的方式的时候使用MapperScan注解来注册Mybatis mapper接口。然后他给了一个配置的示例。 ConfigurationMapperScan(org.mybatis.spring.sample.mapper)public class AppConfig {Beanpublic DataSource dataSource() {return new EmbeddedDatabaseBuilder().addScript(schema.sql).build();}Beanpublic DataSourceTransactionManager transactionManager() {return new DataSourceTransactionManager(dataSource());}Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean sessionFactory new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource());return sessionFactory.getObject();}}然后我们看下里面具体有些什么属性大概了解一下。需要注意的是这个注解中Import了MapperScannerRegistrar.class。
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.TYPE)
Documented
Import(MapperScannerRegistrar.class)
Repeatable(MapperScans.class)
public interface MapperScan {String[] value() default {};String[] basePackages() default {};Class?[] basePackageClasses() default {};Class? extends BeanNameGenerator nameGenerator() default BeanNameGenerator.class;Class? extends Annotation annotationClass() default Annotation.class;Class? markerInterface() default Class.class;String sqlSessionTemplateRef() default ;String sqlSessionFactoryRef() default ;Class? extends MapperFactoryBean factoryBean() default MapperFactoryBean.class;String lazyInitialization() default ;String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT; value(): 指定要扫描的包路径用于指定需要扫描的包。可以指定多个包路径。basePackages() 属性的别名。 允许更简洁的注释声明例如MapperScan(“org.my.pkg”) 而不是 MapperScan(basePackages “org.my.pkg”)}。 basePackages(): 同样是指定要扫描的包路径与 value() 属性作用相同可以指定多个包路径。Mybatis接口的扫描包路径。 basePackageClasses(): 指定要扫描的类可以通过指定类来确定要扫描的包路径。可以指定多个类。basePackages的类型安全替代方法用于指定要扫描components注解的包。将扫描每个指定类的包。 nameGenerator(): 指定自定义的 Bean 名称生成器用于生成扫描到的 Mapper 接口对应的 Bean 的名称。 annotationClass(): 指定自定义的注解类型用于标识被扫描的 Mapper 接口。默认为 Annotation.class即不指定注解。 markerInterface(): 指定一个标记接口用于限定被扫描的 Mapper 接口必须继承该标记接口。 sqlSessionTemplateRef(): 指定一个注入的 SqlSessionTemplate Bean 的名称用于指定要使用的 SqlSessionTemplate。 sqlSessionFactoryRef(): 指定一个注入的 SqlSessionFactory Bean 的名称用于指定要使用的 SqlSessionFactory。 factoryBean(): 指定一个自定义的 MapperFactoryBean 类型用于创建 Mapper 接口对应的 Bean 实例。 lazyInitialization(): 指定是否启用延迟初始化。默认为空字符串表示不启用延迟初始化。 defaultScope(): 指定扫描到的 Mapper 接口对应的 Bean 的默认作用域。默认为 AbstractBeanDefinition.SCOPE_DEFAULT。
Import(MapperScannerRegistrar.class)
引入了MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar。也就是说MapperScannerRegistrar的registerBeanDefinitions方法将会被执行。
spring专题有描述过ImportBeanDefinitionRegistrar的时机在invokeBeanFactoryPostProcessors阶段有兴趣的朋友们也可以关注下。下面就是MapperScannerRegistrar的核心代码。
Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AnnotationAttributes mapperScanAttrs AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));if (mapperScanAttrs ! null) {registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));}}void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {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));}ListString 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-nativebuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}此段源码其实非常简单就是读取注解信息然后注册了一个MapperScannerConfigurer类的BeanDefinition。
MapperScannerConfigurer
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor、InitializingBean、ApplicationContextAware、BeanNameAware。比较重要的是BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法将在invokeBeanFactoryPostProcessors中的后续被执行。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders();}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);}scanner.registerFilters();scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}源码分析
this.processPropertyPlaceHolders这个属性是在创建的时候设置的默认就是true
BeanDefinitionBuilder builder BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);builder.addPropertyValue(processPropertyPlaceHolders, true);所以processPropertyPlaceHolders()方法默认的时候一定会执行的。
首先看注释BeanDefinitionRegistries在应用启动早期被调用且早于BeanFactoryPostProcessors。这就意味着PropertyResourceConfigurer将不会被加载因为其实现了BeanFactoryPostProcessor所以这个类的属性将会加载失败为了避免这类情况所以在此处找到所有PropertyResourceConfigurers的定义信息且运行他们的postProcessBeanFactory方法并更新他们的值。
/** BeanDefinitionRegistries are called early in application startup, before BeanFactoryPostProcessors. This means that* PropertyResourceConfigurers will not have been loaded and any property substitution of this class properties will* fail. To avoid this, find any PropertyResourceConfigurers defined in the context and run them on this class bean* definition. Then update the values.*/private void processPropertyPlaceHolders() {MapString, PropertyResourceConfigurer prcs applicationContext.getBeansOfType(PropertyResourceConfigurer.class,false, false);if (!prcs.isEmpty() applicationContext instanceof ConfigurableApplicationContext) {BeanDefinition mapperScannerBean ((ConfigurableApplicationContext) applicationContext).getBeanFactory().getBeanDefinition(beanName);// PropertyResourceConfigurer does not expose any methods to explicitly perform// property placeholder substitution. Instead, create a BeanFactory that just// contains this mapper scanner and post process the factory.DefaultListableBeanFactory factory new DefaultListableBeanFactory();factory.registerBeanDefinition(beanName, mapperScannerBean);for (PropertyResourceConfigurer prc : prcs.values()) {prc.postProcessBeanFactory(factory);}PropertyValues values mapperScannerBean.getPropertyValues();this.basePackage getPropertyValue(basePackage, values);this.sqlSessionFactoryBeanName getPropertyValue(sqlSessionFactoryBeanName, values);this.sqlSessionTemplateBeanName getPropertyValue(sqlSessionTemplateBeanName, values);this.lazyInitialization getPropertyValue(lazyInitialization, values);this.defaultScope getPropertyValue(defaultScope, values);}this.basePackage Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);this.sqlSessionFactoryBeanName Optional.ofNullable(this.sqlSessionFactoryBeanName).map(getEnvironment()::resolvePlaceholders).orElse(null);this.sqlSessionTemplateBeanName Optional.ofNullable(this.sqlSessionTemplateBeanName).map(getEnvironment()::resolvePlaceholders).orElse(null);this.lazyInitialization Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders).orElse(null);this.defaultScope Optional.ofNullable(this.defaultScope).map(getEnvironment()::resolvePlaceholders).orElse(null);}这个方法的作用是确保在应用启动时处理所有的属性占位符使得配置的值能够正确的诸如到当前类的属性中。这对于需要在应用程序启动时读取配置信息并进行相应处理的情况非常有用。
在方法内部创建了ClassPathMapperScanner对象。并设置了属性值注册了过滤器(包含过滤器和排除过滤器)执行了scan方法。
package-info结尾的类名将会被排除加载。默认所有的类都会被加载。
ClassPathMapperScanner
ClassPathMapperScanner类继承了ClassPathBeanDefinitionScanner类。
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));所以此处将会调用父类ClassPathBeanDefinitionScanner的Scan方法。
public int scan(String... basePackages) {int beanCountAtScanStart this.registry.getBeanDefinitionCount();doScan(basePackages);// Register annotation config processors, if necessary.if (this.includeAnnotationConfig) {AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);}ClassPathBeanDefinitionScanner的scan方法会执行doScan方法也就是子类的实现。
ClassPathMapperScanner#doScan
Override
public SetBeanDefinitionHolder doScan(String... basePackages) {SetBeanDefinitionHolder beanDefinitions super.doScan(basePackages);if (beanDefinitions.isEmpty()) {LOGGER.warn(() - No MyBatis mapper was found in Arrays.toString(basePackages) package. Please check your configuration.);} else {processBeanDefinitions(beanDefinitions);}return beanDefinitions;
}然后执行父类的doscan方法返回的是BeanDefinitionHolder集合也就是把包路径中的类扫描注册到容器中。
如果扫描为空就提示一下如果返回有值的话就进入processBeanDefinitions方法。这个就比较重要了。
private void processBeanDefinitions(SetBeanDefinitionHolder beanDefinitions) {AbstractBeanDefinition definition;BeanDefinitionRegistry registry getRegistry();for (BeanDefinitionHolder holder : beanDefinitions) {definition (AbstractBeanDefinition) holder.getBeanDefinition();boolean scopedProxy false;if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {definition (AbstractBeanDefinition) Optional.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() - new IllegalStateException(The target bean definition of scoped proxy bean not found. Root bean definition[ holder ]));scopedProxy true;}String beanClassName definition.getBeanClassName();LOGGER.debug(() - Creating MapperFactoryBean with name holder.getBeanName() and beanClassName mapperInterface);// the mapper interface is the original class of the bean// but, the actual class of the bean is MapperFactoryBeandefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59try {// for spring-nativedefinition.getPropertyValues().add(mapperInterface, Resources.classForName(beanClassName));} catch (ClassNotFoundException ignore) {// ignore}definition.setBeanClass(this.mapperFactoryBeanClass);definition.getPropertyValues().add(addToConfig, this.addToConfig);// Attribute for MockitoPostProcessor// https://github.com/mybatis/spring-boot-starter/issues/475definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName);boolean explicitFactoryUsed false;if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {definition.getPropertyValues().add(sqlSessionFactory,new RuntimeBeanReference(this.sqlSessionFactoryBeanName));explicitFactoryUsed true;} else if (this.sqlSessionFactory ! null) {definition.getPropertyValues().add(sqlSessionFactory, this.sqlSessionFactory);explicitFactoryUsed true;}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;}if (!explicitFactoryUsed) {LOGGER.debug(() - Enabling autowire by type for MapperFactoryBean with name holder.getBeanName() .);definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}definition.setLazyInit(lazyInitialization);if (scopedProxy) {continue;}if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) defaultScope ! null) {definition.setScope(defaultScope);}if (!definition.isSingleton()) {BeanDefinitionHolder proxyHolder ScopedProxyUtils.createScopedProxy(holder, registry, true);if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {registry.removeBeanDefinition(proxyHolder.getBeanName());}registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());}}
}代码非常长哈里面也就一些issue的记录注释有兴趣的可以看一看拓展一下。
其实里面做的事情就是BeanDefinition的属性设置需要注意的是扫描的这些Mapper类的BeanDefinition的beanClass都是MapperFactoryBean.class。因为我们使用的Mapper类都是接口。
写在最后
MapperScan注解实际上做的事情就是扫描Mapper类到容器中。引入的类也是为了加载和处理Mapper类。但是除了MapperScan之外有个一个SPI机制引入的类MybatisPlusAutoConfiguration将在下一章进行分析。