mapper接口代理对象创建原理

springboot整合mybatis


    org.mybatis.spring.boot
    mybatis-spring-boot-starter
    1.3.2

mapper接口代理对象的创建

写在前面:Mapper接口对象时通过org.mybatis.spring.mapper.MapperFactoryBean#getObject方法创建的。

org.mybatis.spring.mapper.MapperFactoryBean#getObject
​
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

自动配置类 (MybatisAutoConfiguration)

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
​
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
      // 通过SqlSessionFactoryBean对象的getObject创建SqlSessionFactory
      SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    ........
      return factory.getObject();
    }
    
  // 创建 SqlSessionTemplate对象 
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }
}

org.mybatis.spring.SqlSessionFactoryBean#getObject

class : org.mybatis.spring.SqlSessionFactoryBean
​
@Override
public SqlSessionFactory getObject() throws Exception {  
  if (this.sqlSessionFactory == null) {
     // 会创建 configLocation对象作为sqlSessionFactory的属性
    afterPropertiesSet();
  }
  return this.sqlSessionFactory;
}
​
@Override
public void afterPropertiesSet() throws Exception {
    // 创建sqlSessionFactory
    this.sqlSessionFactory = buildSqlSessionFactory();
}
​
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
  final Configuration targetConfiguration;
   ..........
  return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
class: org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)
​
public SqlSessionFactory build(Configuration config) {
   // SqlSessionFactory实质是 DefaultSqlSessionFactory
  return new DefaultSqlSessionFactory(config);
}

小结:自动配置类中,创建两大对象(DefaultSqlSessionFactory和SqlSessionTemplate),交给spring容器管理。

SqlSessionFactory(DefaultSqlSessionFactory)创建过程:解析mapper.xml,创建configuration对象,交给SqlSessionFactory对象。configuration是DefaultSqlSessionFactory对象的属性。

 

注解@MapperScan

@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
    ...... 
}
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {}

说明:MapperScan注解中,是通过import注解导入MapperScannerRegistrar.class。而MapperScannerRegistrar(Mapper扫描注册器)实现了ImportBeanDefinitionRegistrar接口。所以spring启动时,会调用该接口的registerBeanDefinitions方法给spring容器注册bean definitions。也就是个beanDefinitonMap中加入beanDefinition

 

MapperScannerRegistrar#registerBeanDefinitions分析

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
​
  private ResourceLoader resourceLoader;
​
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
​
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    ......
    // 扫描
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }
}
​

org.mybatis.spring.mapper.ClassPathMapperScanner#doScan

// 扫描类 ---> 创建BeanDefinitions ---> 注册到beanDefinionMap中 ---> 修改BeanDefinitions

class: org.mybatis.spring.mapper.ClassPathMapperScanner extends ClassPathBeanDefinitionScanner
​
@Override
public Set doScan(String... basePackages) {
  // 通过指定的包路径,扫描mapper接口,创建BeanDefinition
  Set beanDefinitions = super.doScan(basePackages);
    .......
  processBeanDefinitions(beanDefinitions);
    .......
  return beanDefinitions;
}

org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions

   
private MapperFactoryBean mapperFactoryBean = new MapperFactoryBean();   
    
// 处理BeanDefinitions 
// 入参的beanDefinitions是从mapper接口产生的
private void processBeanDefinitions(Set beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
        definition = (GenericBeanDefinition) holder.getBeanDefinition();
        // 是为了创建mapperFactoryBean对象时,给mapperInterface赋值
        definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); 
        // 设置beanClass = mapperFactoryBean.class
        definition.setBeanClass(this.mapperFactoryBean.getClass());
        ....... 
        // 按类型注入,作用是在创建MapperFactoryBean对象时,通过set方法按类型实现装配
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  } 
  

小结:

1. 在MapperScannerRegistrar#registerBeanDefinitions中,通过ClassPathMapperScanner的doScan方法,扫描指定包下面的mapper接口,创建beanDefition加入到beanDefinitionMap中,(key是接口名首字母小写)。再通过processBeanDefinitions 将beandefinition的 BeanClass 改成 MapperFactoryBean.class。

beanDefinitionMap
key = "mapper接口名称,首字母小写"  
value=beanDefinition(beanClass=MapperFactoryBean.class)

 

mapper代理对象创建(MapperFactoryBean#getObject)

1. spring遍历beanDefinionMap时,会创建MapperFactoryBean对象。并加入到单例池中。

2. 一般我们都会将mapper对象注入到Service中。那么当service对象在创建时,发现依赖mapper,则会从ioc容器中获取Mapper对象。因为单例池中存放了maper接口和MapperFactoryBean对象的映射关系。所以很一定会获取到MapperFactory对象(MapperFactoryBean实现了FactoryBean接口),再调用其getObject方法,创建mapper接口对象。mapper代理对象实质是通过MapperFactoryBean#getObject方法创建的

 

public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {
org.mybatis.spring.mapper.MapperFactoryBean#getObject
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}
org.mybatis.spring.support.SqlSessionDaoSupport#getSqlSession
public SqlSession getSqlSession() {
  return this.sqlSessionTemplate;
}
​
// 装配SqlSessionTemplate
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
  this.sqlSessionTemplate = sqlSessionTemplate;
}

说明: MapperFactoryBean继承了SqlSessionDaoSupport类,且MapperFactoryBean的definition的autowireMode属性为AbstractBeanDefinition.AUTOWIRE_BY_TYPE。所以在创建MapperFactoryBean对象时,已经通过set方式装配了sqlSessionTemplate对象。且sqlSessionTemplate持有了DefaultSqlSessionFactory对象。

 

org.mybatis.spring.mapper.MapperFactoryBean#getObject
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}
​
org.mybatis.spring.SqlSessionTemplate#getMapper
@Override
public  T getMapper(Class type) {
    return getConfiguration().getMapper(type, this);
}
​
org.apache.ibatis.session.Configuration#getMapper
//MapperRegistry mapperRegistry映射器是configuration的属性
public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}
​
org.apache.ibatis.binding.MapperRegistry#getMapper
@SuppressWarnings("unchecked")
public  T getMapper(Class type, SqlSession sqlSession) {
    // knownMappers保存了接口类型和mapperProxyFactory接口的关系
    // 创建Configuration对象的阶段 通过XMLMapperBuilder解析将关系保存到knownMappers
    // knownMappers("接口class对象", new MapperProxyFactory()<接口class对象>)
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    ......
        // 通过该方法创建 Mapper接口代理对象
        return mapperProxyFactory.newInstance(sqlSession);
    ......
}
​
​
  class: org.apache.ibatis.binding.MapperProxyFactory
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
​
  class: org.apache.ibatis.binding.MapperProxyFactory
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy mapperProxy) {
     // 通过jdk代理创建对象, 入参接口的classLoad, 接口的calss对象, MapperProxy(实现了InvocationHandler)
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

小结:

  1. 获取MapperProxyFactory,从MapperRegistry mapperRegistry映射器的knownMappers属性中获取。

  1. 通过mapperProxyFactory.newInstance(sqlSession)创建代理对象。(使用的是jdk代理,mapperProxy实现了invocationHandler接口)

  2. mapepr接口的方法执行,实质调用的是mapperProxy#invoke方法。

 

总结:

  1. 通过自动配置类MybatisAutoConfiguration,创建DefaultSqlSessionFactory和sqlSessionTemplate对象。(创建过程中解析了mapper.xml)

  2. 解析xml中,给Map, MapperProxyFactory> knownMappers 赋值。

  3. 扫描maper接口,注册beanDefinitions。进而创建MapperFactoryBean对象。

  4. 通过MapperFactoryBean#getObject方法,在knownMappers 找到MapperProxyFactory工厂对象创建maper代理对象。

 

你可能感兴趣的:(java,spring,boot,mybatis)