从MapperScannerConfigurer看MyBatis自动扫描Mapper的机制

首先我们来看MapperScannerConfigurer的继承和实现关系

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware 

这里我们关注BeanDefinitionRegistryPostProcessor这个接口:它继承了BeanFactoryPostProcessor,用来在BeanFactoryPostProcessor初始化之前再往容器中注册一些额外的BeanDefinition。

看到这里你应该有点想法了吧?对,它就是扫描了我们给的basePackage下的类之后,然后生成BeanDefinition再添加到容器中的。

下面还是从源码入手,我们先看BeanDefinitionRegistryPostProcessor初始化时调用的方法:

  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    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.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

这里我们先看ClassPathMapperScanner这个类,可以从名字上(Scanner)看出来,它就是去Scan的核心类。并且除了最后一行,其余的代码都是在配置这个Scanner。

这里根据名字我们可以猜想,最后一行scanner.scan(...)就是在扫描Mapper并将它添加到BeanDefinition中去。为了验证我们的想法,跟进去看看:

	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;
	}

发现进的并不是ClassPathMapperScanner这个类,而是ClassPathBeanDefinitionScanner这个类,并且ClassPathBeanDefinitionScanner在spring的包下。那么再回过头来看看ClassPathBeanDefinitionScanner的定义:

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner

恩,确实是继承了org.springframework.context.annotation.ClassPathBeanDefinitionScanner,并且我们可以看到它覆盖了好几个方法:

doScan(...),熟吗?不熟的请往上看到scan方法中。监测的逻辑实际上都在doScan(...)中,接下来我们就关注这个Override的方法:

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    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 {
      for (BeanDefinitionHolder holder : beanDefinitions) {
        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

        if (logger.isDebugEnabled()) {
          logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
        }

        // the mapper interface is the original class of the bean
        // but, the actual class of the bean is MapperFactoryBean
        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
        definition.setBeanClass(MapperFactoryBean.class);

        definition.getPropertyValues().add("addToConfig", this.addToConfig);

        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) {
          if (logger.isDebugEnabled()) {
            logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
          }
          definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
      }
    }

    return beanDefinitions;
  }

第一步就能看到,调用了父类的doScan(...)方法,父类的doScan(..)这里就不放出来了,它是Spring自动扫描机制的核心,也就是我们注册:

<context:component-scan base-package="com.test "/>

这个东西会用到的,它可以帮我们自动注册@Controller、@Service等组件。

本质上,也是通过扫描这些类,并将它们加入到BeanDefinition中,所以上面通过父类的doScan(...)方法其实是将我们设置的base-package下的类都封装成了BeanDefinition,并且添加到了一个Set集合中去了。也就是doScan(..)第一句得到的beanDefinitions。

但和父类略有不同的是,得到了所有的BeanDefinition之后,这并不真正要注入到容器中的类,还记得MyBatis手动把Mapper注入到Spring时是如何配置的吗:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

而上面通过自动扫描注册的Mapper的BeanDefinition是类似这样的:

<bean id="userMapper" class="com.test.a.mapper.UserMapper">
</bean>

关键是这个class,所以需要转换,并将属性注入进去,mapperInterface和sqlSessionFactory都是必须的。

这样就完成了Mapper的自动扫描,不知道这样讲是否思路清晰~

时间: 2024-10-15 20:42:27

从MapperScannerConfigurer看MyBatis自动扫描Mapper的机制的相关文章

org.mybatis.spring.mapper.MapperScannerConfigurer 类作用

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="annotationClass" value="org.springframework.stereotype.Repository"/>        <property name="basePackage" value=&

org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner$1

不能加载或找不到 org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner$1 经查证,是mybatis-spring-xxx.jar 这个版本不匹配,可以试试 mybatis-spring-1.1.1.jar mybatis-spring-1.2.1.jar

org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#39;org.mybatis.spring.mapper.MapperScannerConfigurer#0&#39; defined in class path resource

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.mybatis.spring.mapper.MapperScannerConfigurer#0' defined in class path resource [applicationContext.xml]: Initialization of bean failed; nested exc

Mybatis运用mapper代码实现和Spring的整合(一)

引言 先前做的java项目中一直使用的EclipseLink和Hibernate,两种ORM框架,现在用Mybatis实现,当然它们之间各有各的优点,同样也有缺点,有时候,一个项目中是可以存在两种框架一起使用的,在项目中,技术选型很重要.Mybatis是一个持久层的框架,是apache下的顶级项目,mybatis让程序将主要的精力放在sql上,通过mybatis提供的映射方式,自由灵活生成. Mybatis的主要是靠sql语句来进行实现对数据库持久化的,这就是mybatis的硬伤,当然,它也因为

Spring Boot MyBatis 通用Mapper插件集成

看本文之前,请确保你已经在SpringBoot中集成MyBatis,并能正常使用.如果没有,那么请先移步 http://blog.csdn.net/catoop/article/details/50553714 做了解后,再按本文步骤操作. 使用MyBatis在我们通过xml集中配置SQL,并通过创建接口Mapper文件来完成持久化DAO层(mybatis内部使用的是动态代理,所以我们不需要自己编写实现类). 然而在实际开发中,单表操作非常多,如果你也想像JPA.JDBC那样做一个所谓的Base

Mybatis通用Mapper

转:http://blog.csdn.net/isea533/article/details/41457529 极其方便的使用Mybatis单表的增删改查 项目地址:http://git.oschina.net/free/Mapper 优点? 不客气的说,使用这个通用Mapper甚至能改变你对Mybatis单表基础操作不方便的想法,使用它你能简单的使用单表的增删改查,包含动态的增删改查. 程序使用拦截器实现具体的执行Sql,完全使用原生的Mybatis进行操作. 你还在因为数据库表变动重新生成x

Mybatis通用Mapper(转)

转自:http://blog.csdn.net/isea533/article/details/41457529 极其方便的使用Mybatis单表的增删改查 项目地址:http://git.oschina.net/free/Mapper 优点? 不客气的说,使用这个通用Mapper甚至能改变你对Mybatis单表基础操作不方便的想法,使用它你能简单的使用单表的增删改查,包含动态的增删改查. 程序使用拦截器实现具体的执行Sql,完全使用原生的Mybatis进行操作. 你还在因为数据库表变动重新生成

mybatis的mapper为什么不用写实现类的源码分析

首先看下我们spring和mybatis扫描包的时候是怎样写: 有两种写法: 第一种写法: <mybatis-spring:scan base-package="com.gupaoedu.crud.dao"/>   第二种写法: <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property n

springboot mybatis 集成mapper与pagehelper

1.引入jar到pom.xml <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>tk.mybati