Mybatis源码分析——Mapper接口和XML文件里的SQL是如何建立关系的?

前言

这是 mybatis 比较常问到的面试题,我自己在以前的面试过程中被问到过,因此自己印象很深刻。

另外,估计不少同学应该也注意到了,DAO 接口的全路径名和 XML 文件中的 SQL 的 namespace + id 是一样的。其实,这也是建立关联的根本原因。

正文

当一个项目中使用了 Spring 和 Mybatis 时,通常会有以下配置。当然现在很多项目应该都是 SpringBoot 了,可能没有以下配置,但是究其底层原理都是类似的,无非是将扫描 bean 等一些工作通过注解来实现。



    
    
    

 


    
    
    
    
    
    

 


    
    
    
    

通常我们还会有 DAO 类和 对用的 mapper 文件,如下。

package com.yibo.open.mapper;
 
import com.yibo.open.po.UserPO;
 
public interface UserPOMapper {
    UserPO queryByPrimaryKey(Integer id);
}



    
        
        
    
 
    

1、解析 MapperScannerConfigurer

MapperScannerConfigurer 是一个 BeanDefinitionRegistryPostProcessor,会在 Spring 构建 IoC容器的早期被调用重写的 postProcessBeanDefinitionRegistry 方法。

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }

        // 新建一个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);
        // 注册Filter,因为上面构造函数我们没有使用默认的Filter,
        // 有两种Filter,includeFilters:要扫描的;excludeFilters:要排除的
        scanner.registerFilters();
        
        // 扫描basePackage,basePackage可通过",; \t\n"来填写多个,
        // ClassPathMapperScanner重写了doScan方法
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
}

注册 Filter

作用:什么类型的Mapper将会留下来。

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

    public void registerFilters() {
        boolean acceptAllInterfaces = true;
        // 1.如果指定了注解,则将注解添加到includeFilters
        if (this.annotationClass != null) {
            this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        // 2.如果指定了标记接口,则将标记接口添加到includeFilters,
        // 但这边重写了matchClassName方法,并返回了false,
        // 相当于忽略了标记接口上的匹配项,所以该参数目前相当于没有任何作用
        if (this.markerInterface != null) {
            this.addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
                protected boolean matchClassName(String className) {
                    return false;
                }
            });
            acceptAllInterfaces = false;
        }
        
        // 3.如果没有指定annotationClass和markerInterface,则
        // 添加默认的includeFilters,直接返回true,接受所有类
        if (acceptAllInterfaces) {
            this.addIncludeFilter(new TypeFilter() {
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    return true;
                }
            });
        }
        
        // 4.添加默认的excludeFilters,排除以package-info结尾的类
        this.addExcludeFilter(new TypeFilter() {
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                String className = metadataReader.getClassMetadata().getClassName();
                return className.endsWith("package-info") ? true : metadataReader.getAnnotationMetadata().hasAnnotation("tk.mybatis.mapper.annotation.RegisterMapper");
            }
        });
    }
}

通常我们都不会指定 annotationClass 和 markerInterface,也就是会添加默认的 Filter,相当于会接受除了 package-info 结尾的所有类。因此,basePackage 包下的类不需要使用 @Component 注解或 XML 中配置 bean 定义,也会被添加到 IoC 容器中。

扫描 basePackage

这边会走到 ClassPathBeanDefinitionScanner(ClassPathMapperScanner 的父类),然后在执行 “doScan(basePackages)” 时回到 ClassPathMapperScanner 重写的方法doScan

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

    private final BeanDefinitionRegistry registry;

    public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        
        //调用同类方法进行扫描,并将basePackages下的class都封装成BeanDefinitionHolder,并注册进Spring容器的BeanDefinition
        doScan(basePackages);

        // Register annotation config processors, if necessary.
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    }
    
    protected Set doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set beanDefinitions = new LinkedHashSet<>();
        //遍历basePackages进行扫描
        for (String basePackage : basePackages) {
            //找出匹配的类
            Set candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                if (checkCandidate(beanName, candidate)) {
                    //封装成BeanDefinitionHolder 对象
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder =
                            AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    //将BeanDefinition对象注入spring的BeanDefinitionMap中,后续getBean时,就是从BeanDefinitionMap获取对应的BeanDefinition对象,取出其属性进行实例化Bean
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }   
}

我们重点看下doScan方法,获取basePackages下的所有Class,并将其生成BeanDefinition,注入spring的BeanDefinitionMap中,也就是Class的描述类,在调用getBean方法时,获取BeanDefinition进行实例化。此时,所有的Mapper接口已经被生成了BeanDefinition。

小结,解析 MapperScannerConfigurer 主要是做了几件事:

  • 1、新建扫描器 ClassPathMapperScanner。
  • 2、使用 ClassPathMapperScanner 扫描注册 basePackage 包下的所有 bean。
  • 3、将 basePackage 包下的所有 bean 进行一些特殊处理:beanClass 设置为 MapperFactoryBean、bean 的真正接口类作为构造函数参数传入 MapperFactoryBean、为 MapperFactoryBean 添加 sqlSessionFactory 和 sqlSessionTemplate属性。

2、解析 SqlSessionFactoryBean

对于 SqlSessionFactoryBean 来说,实现了2个接口,InitializingBean 和 FactoryBean,看过Spring 文章的同学应该对这2个接口不会陌生,简单来说:

  • 1、FactoryBean可以自己定义创建实例对象的方法,只需要实现它的 getObject() 方法。
  • 2、InitializingBean则是会在 bean 初始化阶段被调用。

SqlSessionFactoryBean 重写这两个接口的部分方法代码如下,核心代码就一个方法—— buildSqlSessionFactory()

public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {

    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
             // 如果之前没有构建,则这边也会调用afterPropertiesSet进行构建操作
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }
    
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
        // 构建sqlSessionFactory
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }
}

buildSqlSessionFactory()

主要做了几件事:

  • 1、对我们配置的参数进行相应解析。
  • 2、使用配置的参数构建一个 Configuration。
  • 3、使用 Configuration 新建一个 DefaultSqlSessionFactory。

这边的核心内容是对于 mapperLocations 的解析,如下代码。

public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {

    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
        
        //省略前面代码......

        configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
        // mapper处理(最重要)
        if (!ObjectUtils.isEmpty(this.mapperLocations)) {
            Resource[] var29 = this.mapperLocations;
            var27 = var29.length;

            for(var5 = 0; var5 < var27; ++var5) {
                Resource mapperLocation = var29[var5];
                if (mapperLocation != null) {
                    try {
                        // 新建XMLMapperBuilder
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());
                        // 解析mapper文件
                        xmlMapperBuilder.parse();
                    } catch (Exception var20) {
                        throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var20);
                    } finally {
                        ErrorContext.instance().reset();
                    }

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                    }
                }
            }
        } else if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
        }
        // 使用targetConfiguration构建DefaultSqlSessionFactory
        return this.sqlSessionFactoryBuilder.build(configuration);
    }
}

解析mapper文件

public class XMLMapperBuilder extends BaseBuilder {

    private final XPathParser parser;

    private final String resource;

    public void parse() {
        // 如果resource没被加载过才进行加载
        if (!configuration.isResourceLoaded(resource)) {
            // 解析mapper文件
            configurationElement(parser.evalNode("/mapper"));
            // 将resource添加到已加载列表
            configuration.addLoadedResource(resource);
            // 绑定namespace的mapper
            bindMapperForNamespace();
        }

        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
    }
}

解析mapper文件

public class XMLMapperBuilder extends BaseBuilder {

    private void configurationElement(XNode context) {
        try {
             // 1.获取namespace属性
            String namespace = context.getStringAttribute("namespace");
            if (namespace == null || namespace.equals("")) {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }
            // 2.设置currentNamespace属性
            builderAssistant.setCurrentNamespace(namespace);
            // 3.解析parameterMap、resultMap、sql等节点
            cacheRefElement(context.evalNode("cache-ref"));
            cacheElement(context.evalNode("cache"));
            parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            resultMapElements(context.evalNodes("/mapper/resultMap"));
            sqlElement(context.evalNodes("/mapper/sql"));
            // 4.解析增删改查节点,封装成Statement
            buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
    }
    
    private void buildStatementFromContext(List list) {
        if (configuration.getDatabaseId() != null) {
            buildStatementFromContext(list, configuration.getDatabaseId());
        }
        // 解析增删改查节点,封装成Statement
        buildStatementFromContext(list, null);
    }
    
    private void buildStatementFromContext(List list, String requiredDatabaseId) {
        for (XNode context : list) {
            // 1.构建XMLStatementBuilder
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                // 2.解析节点
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }
}

这边会一直执行到 statementParser.parseStatementNode();

这边每个 XNode 都相当于如下的一个 SQL,下面封装的每个 MappedStatement 可以理解就是每个 SQL。


statementParser.parseStatementNode()
public class XMLMapperBuilder extends BaseBuilder {

    public void parseStatementNode() {
        // 1 获得 id 属性,编号。
        String id = context.getStringAttribute("id");
        
        // 2 获得 databaseId , 判断 databaseId 是否匹配
        String databaseId = context.getStringAttribute("databaseId");

        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
          return;
        }
        
        // 3 获得各种属性
        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        String parameterType = context.getStringAttribute("parameterType");
        Class parameterTypeClass = resolveClass(parameterType);
        String resultMap = context.getStringAttribute("resultMap");
        String resultType = context.getStringAttribute("resultType");
        String lang = context.getStringAttribute("lang");
        
        // 4 获得 lang 对应的 LanguageDriver 对象
        LanguageDriver langDriver = getLanguageDriver(lang);
        
        // 5 获得 resultType 对应的类
        Class resultTypeClass = resolveClass(resultType);
        
        // 6 获得 resultSet 对应的枚举值
        String resultSetType = context.getStringAttribute("resultSetType");
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        
        // 7 获得 statementType 对应的枚举值
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
        
        // 8 获得 SQL 对应的 SqlCommandType 枚举值
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        
        // 9 获得各种属性
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

        // Include Fragments before parsing
        // 10 创建 XMLIncludeTransformer 对象,并替换  标签相关的内容
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());

        // Parse selectKey after includes and remove them.
        // 11 解析  标签
        processSelectKeyNodes(id, parameterTypeClass, langDriver);

        // Parse the SQL (pre:  and  were parsed and removed)
        // 12 创建 SqlSource
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        // 13 获得 KeyGenerator 对象
        String resultSets = context.getStringAttribute("resultSets");
        String keyProperty = context.getStringAttribute("keyProperty");
        String keyColumn = context.getStringAttribute("keyColumn");
        KeyGenerator keyGenerator;
        // 13.1 优先,从 configuration 中获得 KeyGenerator 对象。如果存在,意味着是  标签配置的
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
          keyGenerator = configuration.getKeyGenerator(keyStatementId);
          
        // 13.2 其次,根据标签属性的情况,判断是否使用对应的 Jdbc3KeyGenerator 或者 NoKeyGenerator 对象  
        } else {
          keyGenerator = context.getBooleanAttribute("useGeneratedKeys", // 优先,基于 useGeneratedKeys 属性判断
              configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) // 其次,基于全局的 useGeneratedKe
              ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
        
        // 创建 MappedStatement 对象
        // 将解析出来的所有参数添加到 mappedStatements 缓存
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered, 
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}

public class MapperBuilderAssistant extends BaseBuilder {

  public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class parameterType,
      String resultMap,
      Class resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }

    // 1.将id填充上namespace,例如:queryByPrimaryKey变成
    // com.yibo.open.mapper.UserPOMapper.queryByPrimaryKey
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    // 2.使用参数构建MappedStatement.Builder
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);
    // 3.使用MappedStatement.Builder构建MappedStatement
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    // 4.将MappedStatement 添加到缓存
    MappedStatement statement = statementBuilder.build();
    configuration.addMappedStatement(statement);
    return statement;
  }
}

public class Configuration {

    protected final Map mappedStatements = new StrictMap("Mapped Statements collection");

    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }
}

该方法会将节点的属性解析后封装成 MappedStatement,放到 mappedStatements 缓存中,key 为 id,例如:com.yibo.open.mapper.UserPOMapper.queryByPrimaryKey,value 为 MappedStatement。

bindMapperForNamespace()

绑定namespace的mapper

public class XMLMapperBuilder extends BaseBuilder {

    private final MapperBuilderAssistant builderAssistant;

    private void bindMapperForNamespace() {
        String namespace = builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class boundType = null;
            try {
                // 1.解析namespace对应的绑定类型
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException e) {
                //ignore, bound type is not required
            }
            if (boundType != null) {
                if (!configuration.hasMapper(boundType)) {
                    // Spring may not know the real resource name so we set a flag
                    // to prevent loading again this resource from the mapper interface
                    // look at MapperAnnotationBuilder#loadXmlResource
                    
                    // 2.boundType不为空,并且configuration还没有添加boundType,
                    // 则将namespace添加到已加载列表,将boundType添加到knownMappers缓存
                    configuration.addLoadedResource("namespace:" + namespace);
                    configuration.addMapper(boundType);
                }
            }
        }
    }
}

public class Configuration {

    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

    public  void addMapper(Class type) {
        mapperRegistry.addMapper(type);
    }
}

public class MapperRegistry {

    private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();

    public  void addMapper(Class type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // 将type和以该type为参数构建的MapperProxyFactory作为键值对,
                // 放到knownMappers缓存中去
                knownMappers.put(type, new MapperProxyFactory(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }
}

主要是将刚刚解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中,key 为 namespace 对应的 class,value 为 MapperProxyFactory。

小结,解析 SqlSessionFactoryBean 主要做了几件事:

  • 1、解析处理所有属性参数构建 Configuration ,使用 Configuration 新建 DefaultSqlSessionFactory;

  • 2、解析 mapperLocations 属性的 mapper 文件,将 mapper 文件中的每个 SQL 封装成 MappedStatement,放到 mappedStatements 缓存中,key 为 id,例如:com.yibo.open.mapper.UserPOMapper.queryByPrimaryKey,value 为 MappedStatement。

  • 3、将解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中,key 为 namespace 对应的 class,value 为 MapperProxyFactory。

3、解析Mapper 文件

Mapper 文件,也就是 basePackage 指定的包下的文件,也就是上文的 interface UserPOMapper 。

上文 doScan 中说过,basePackage 包下所有 bean 定义的 beanClass 会被设置成 MapperFactoryBean.class,而 MapperFactoryBean 也是 FactoryBean,因此直接看 MapperFactoryBean 的 getObject 方法。

public class MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean {

    public T getObject() throws Exception {
        // 1.从父类中拿到sqlSessionTemplate,这边的sqlSessionTemplate也是doScan中添加的属性
        // 2.通过mapperInterface获取mapper
        return this.getSqlSession().getMapper(this.mapperInterface);
    }
}

public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;

    @Override
    public  T getMapper(Class type) {
        return configuration.getMapper(type, this);
    }
}


public class Configuration {

    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

    public  T getMapper(Class type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
}

public class MapperRegistry {

    private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();


    public  T getMapper(Class type, SqlSession sqlSession) {
        // 1.从knownMappers缓存中获取
        final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            // 2.新建实例
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }
}

public class MapperProxyFactory {

    private final Class mapperInterface;
    private final Map methodCache = new ConcurrentHashMap();

    public T newInstance(SqlSession sqlSession) {
        // 1.构造一个MapperProxy
        final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
        // 2.使用MapperProxy来构建实例对象
        return newInstance(mapperProxy);
    }
    
    protected T newInstance(MapperProxy mapperProxy) {
        // 使用JDK动态代理来代理要创建的实例对象,InvocationHandler为mapperProxy,
        // 因此当我们真正调用时,会走到mapperProxy的invoke方法
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }
}

这边代码用到的 sqlSessionTemplate、mapperInterface 等都是之前添加的属性。

小结,解析 DAO 文件 主要做了几件事:

  • 1、通过 mapperInterface 从 knownMappers 缓存中获取到 MapperProxyFactory 对象。

  • 2、通过 JDK 动态代理创建 MapperProxyFactory 实例对象,InvocationHandler 为 MapperProxy。

4、Mapper 接口被调用

当 Mapper 中的接口被调用时,会走到 MapperProxy 的 invoke 方法。

public class MapperProxy implements InvocationHandler, Serializable {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // Object的方法执行
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                //JDK8的默认方法执行
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 获取MapperMethod
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 真正的处理在这里
        return mapperMethod.execute(sqlSession, args);
    }
    
    private final SqlSession sqlSession;
    private final Class mapperInterface;
    private final Map methodCache;    
    
    private MapperMethod cachedMapperMethod(Method method) {
        //从缓存中获取mapperMethod
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            //缓存中没有则构建一个并放入缓存中
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
}

mapperMethod.execute(sqlSession, args)

MapperMethod.execute()方法执行

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 根据命令类型执行来进行相应操作
    // 具体的增删改查操作,都有具体的执行
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
}

这边就比较简单,根据不同的操作类型执行相应的操作,最终将结果返回

这边的 command 是上文 new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())时创建的。

增删改查

public class DefaultSqlSession implements SqlSession {

    @Override
    public int insert(String statement, Object parameter) {
        return update(statement, parameter);
    }
    
    @Override
    public int update(String statement, Object parameter) {
        try {
            dirty = true;
            // 从mappedStatements缓存拿到对应的MappedStatement对象,执行更新操作
            MappedStatement ms = configuration.getMappedStatement(statement);
            return executor.update(ms, wrapCollection(parameter));
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
    
    @Override
    public int delete(String statement, Object parameter) {
        return update(statement, parameter);
    }
}

public class MapperMethod {

    private final SqlCommand command;
    private final MethodSignature method;
  
    //select,以executeForMany为例
    private  Object executeForMany(SqlSession sqlSession, Object[] args) {
        List result;
        // 1.参数转换成sql命令参数
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
            RowBounds rowBounds = method.extractRowBounds(args);
            result = sqlSession.selectList(command.getName(), param, rowBounds);
        } else {
            // 2.执行查询操作
            result = sqlSession.selectList(command.getName(), param);
        }
        // issue #510 Collections & arrays support
        // 3.处理返回结果
        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
            if (method.getReturnType().isArray()) {
                return convertToArray(result);
            } else {
                return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
            }
        }
        return result;
    }   
}

public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;

    private final Executor executor;

    @Override
    public  List selectList(String statement) {
        return this.selectList(statement, null);
    }

    @Override
    public  List selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
    }

    @Override
    public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            //从mappedStatements缓存中拿到对应的MappedStatement对象,执行查询操作
            MappedStatement ms = configuration.getMappedStatement(statement);
            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
}

可以看出,最终都是从 mappedStatements 缓存中拿到对应的 MappedStatement 对象,执行相应的操作。

这边的增删改查不是直接调用 SqlSession 中的方法,而是调用 SqlSessionTemplate 中的方法,继而通过 sqlSessionProxy 来调用 SqlSession 中的方法。SqlSessionTemplate 中的方法主要是通过 sqlSessionProxy 做了一层动态代理,基本没差别。

总结

整个流程主要是以下几个核心步骤:

  • 1、扫描注册 basePackage 包下的所有 bean,将 basePackage 包下的所有 bean 进行一些特殊处理:beanClass 设置为 MapperFactoryBean、bean 的真正接口类作为构造函数参数传入 MapperFactoryBean、为 MapperFactoryBean 添加 sqlSessionFactory 和 sqlSessionTemplate属性。

  • 2、解析 mapperLocations 属性的 mapper 文件,将 mapper 文件中的每个 SQL 封装成 MappedStatement,放到 mappedStatements 缓存中,key 为 id,例如:com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKey,value 为 MappedStatement。并且将解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中,key 为 namespace 对应的 class,value 为 MapperProxyFactory。

  • 3、创建 DAO 的 bean 时,通过 mapperInterface 从 knownMappers 缓存中获取到 MapperProxyFactory 对象,通过 JDK 动态代理创建 MapperProxyFactory 实例对象,InvocationHandler 为 MapperProxy。

  • 4、DAO 中的接口被调用时,通过动态代理,调用 MapperProxy 的 invoke 方法,最终通过 mapperInterface 从 mappedStatements 缓存中拿到对应的 MappedStatement,执行相应的操作。

参考:
https://zhuanlan.zhihu.com/p/140087414

你可能感兴趣的:(Mybatis源码分析——Mapper接口和XML文件里的SQL是如何建立关系的?)