mybatis接口实例化过程解析

我们平时在使用mybatis时一般只需要定义一个接口,然后编写相应的mapper.xml文件,然后即可在spring中使用@Autowired进行注入即可使用。那么mybatis是如何根据接口来创建bean的呢?
   一般我们在使用mybatis时,都会使用
   MapperScan(basePackages = {"com.xxx.rootApp.mapper"})

这样的注解来告诉程序去哪个包下面找我们定义的接口。程序会通过MapperScannerConfigurer类的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法来解析上面的注解,我们就从该方法开始去探寻mybaitis的接口实例化过程。 MapperScannerConfigurer的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法代码如下:

@Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
          
   
       ......
      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
      ......
      scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, 
                   ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      ......
      }

ClassPathMapperScanner继承至ClassPathBeanDefinitionScanner,我们进入到ClassPathBeanDefinitionScanner的scan(String… basePackages)方法:

......
   doScan(basePackages);
   ......

注意,这里调用了doScan方法是ClassPathMapperScanner类中的,并不是父类ClassPathBeanDefinitionScanner的,我们进入ClassPathMapperScanner的doScan方法,在该方法中首先再调用父类的Set beanDefinitions = super.doScan(basePackages)来获取beanDefinitions ,我们暂且不管其中的实现,先重点看processBeanDefinitions(beanDefinitions)这一行,我们看到,在processBeanDefinitions方法中有这样一行:definition.setBeanClass(this.mapperFactoryBean.getClass());然后再其上面还有一行非常显眼的注释://the mapper interface is the original class of the bean,but, the actual class of the bean is MapperFactoryBean。通过注释我们可以得知,我们实际使用的bean实际上是通过mapperFactoryBean来创建的。 MapperFactoryBean看名称就知道是一个FactoryBean,这种工厂bean模式在spring中非常常用,通过FactoryBean实现的bean是用FactoryBean的getObject()方法来注入的bean。现在我们来看MapperFactoryBean的getObject()方法: public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }

继续跟代码,可以看到getSqlSession()获取的是SqlSessionTemplate的实例,然后我们进入SqlSessionTemplate的getMapper方法: return getConfiguration().getMapper(type, this); 继续跟代码,进入到Configuration方法的getMapper方法:return mapperRegistry.getMapper(type, sqlSession); 然后进入到MapperRegistry的getMapper方法,该方法中有这么一行mapperProxyFactory.newInstance(sqlSession); 我们跟进去,发现最终调用的是MapperProxyFactory的newInstance(MapperProxy mapperProxy)方法: Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy) 该方法最终返回的就是我们的bean的实例 其中,最后一个参数mapperProxy是MapperProxy的实例,该类实现了InvocationHandler接口,根据java的Proxy类的说明我们知道,当我们调用代理类的方法时,实际调用的就是InvocationHandler的invoke方法,MapperProxy的invoke方法如下: final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); 至此,bean的实例化基本完成。最终我们调用接口的方法时,实际执行的就是mapperMethod.execute(sqlSession, args)。 至于mybatis的时如何加载和解析配置文件并缓存各种configure和mapperMethod,就需要再单独写文章分析了

经验分享 程序员 微信小程序 职场和发展