mybatis(一)SqlSessionFactory初始化


目录
  • Main方法
  • 创建SqlSessionFactory
    • propertiesElement:解析properties配置
    • settingsAsProperties:解析settings配置,并将其转换为Properties对象
    • settingsElement:settings中的信息设置到Configuration对象中
    • typeAliasesElement:解析typeAliases配置
    • pluginElement:解析plugins配置
    • environmentsElement:解析environments配置
    • mapperElement:解析mappers配置
      • 解析cache
      • 解析ResultMap
      • 解析sql节点
      • 解析sql语句节点
        • 通过applyIncludes方法解析节点
        • 通过createSqlSource方法创建SqlSource
        • 构建MappedStatement对象
    • Mapper接口绑定

Main方法

代码版本:3.4.2

    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //入口
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            Employee employeeMapper = sqlSession.getMapper(Employee.class);
            List all = employeeMapper.getAll();
            for (Employee item : all)
                System.out.println(item);
        } finally {
            sqlSession.close();
        }
    }
  1. 创建一个SqlSessionFactory。
  2. 通过SqlSessionFactory获取一个SqlSession
  3. 从SqlSession获取需要的mapper

创建SqlSessionFactory

//    SqlSessionFactoryBuilder
    public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        // 创建配置文件解析器
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        //解析配置文件,生成Configuration对象
        //入口
        Configuration configuration = parser.parse();
        //通过Configuration对象生成SqlSessionFactory,
        //就是创建一个SqlSessionFactory,然后设置其Configuration属性
        SqlSessionFactory var5 = this.build(configuration);
        return var5;
    }
  1. 创建配置文件解析器
  2. 通过parse方法解析配置文件,将解析结果封装到Configuration
  3. 通过Configuration创建SqlSessionFactory

接下来看【通过parse方法解析配置文件,将解析结果封装到Configuration】

//XMLConfigBuilder
public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            //入口
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }
//    XMLConfigBuilder
 private void parseConfiguration(XNode root) {
        try {
            private void parseConfiguration(XNode root) {
                try {
                    // 解析 properties 配置
                    propertiesElement(root.evalNode("properties"));

                    // 解析 settings 配置,并将其转换为 Properties 对象
                    Properties settings = settingsAsProperties(root.evalNode("settings"));

                    loadCustomVfs(settings);

                    // settings 中的信息设置到 Configuration 对象中
                    settingsElement(settings);

                    // 解析 typeAliases 配置
                    typeAliasesElement(root.evalNode("typeAliases"));

                    // 解析 plugins 配置
                    pluginElement(root.evalNode("plugins"));

                    objectFactoryElement(root.evalNode("objectFactory"));

                    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

                    reflectorFactoryElement(root.evalNode("reflectorFactory"));

                    // 解析 environments 配置
                    environmentsElement(root.evalNode("environments"));

                    databaseIdProviderElement(root.evalNode("databaseIdProvider"));

                    typeHandlerElement(root.evalNode("typeHandlers"));

                    // 解析 mappers 配置
                    mapperElement(root.evalNode("mappers"));
                } catch (Exception e) {
                    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
                }
            }
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

定义了一堆标签解析入口:

  1. propertiesElement:解析properties配置
  2. settingsAsProperties:解析settings配置,并将其转换为Properties对象
  3. settingsElement:settings中的信息设置到Configuration对象中
  4. typeAliasesElement:解析typeAliases配置
  5. pluginElement:解析plugins配置
  6. environmentsElement:解析environments配置
  7. mapperElement:解析mappers配置

返回顶部

propertiesElement:解析properties配置


    
    

//XMLConfigBuilder
 private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
            // 解析 propertis 的子节点,并将这些节点内容转换为属性对象 Properties
            //入口
            Properties defaults = context.getChildrenAsProperties();
            // 获取 propertis 节点中的 resource 和 url 属性值
            String resource = context.getStringAttribute("resource");
            String url = context.getStringAttribute("url");

            // 两者都不为空,则抛出异常
            if (resource != null && url != null) {
                throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
            }
            if (resource != null) {
                // 从文件系统中加载并解析属性文件
                defaults.putAll(Resources.getResourceAsProperties(resource));
            } else if (url != null) {
                // 通过 url 加载并解析属性文件
                defaults.putAll(Resources.getUrlAsProperties(url));
            }
            Properties vars = configuration.getVariables();
            if (vars != null) {
                defaults.putAll(vars);
            }
            parser.setVariables(defaults);
            // 将属性值设置到 configuration 中
            configuration.setVariables(defaults);
        }
    }
    public Properties getChildrenAsProperties() {
        //创建一个Properties对象
        Properties properties = new Properties();
        // 获取并遍历子节点
        for (XNode child : getChildren()) {
            // 获取 property 节点的 name 和 value 属性
            String name = child.getStringAttribute("name");
            String value = child.getStringAttribute("value");
            if (name != null && value != null) {
                // 设置属性到属性对象中
                properties.setProperty(name, value);
            }
        }
        return properties;
    }
  1. 解析出property的name和value封装到Properties对象中,然后设置到configuration的属性里
  2. 不只是从property属性加载,还会从文件系统或者网络读取属性配置,这就会存在同名属性覆盖的问题,也就是从文件系统,或者网络上读取到的属性及属性值会覆盖掉 properties子节点中同名的属性和及值

返回顶部

settingsAsProperties:解析settings配置,并将其转换为Properties对象


    
    
    

//    XMLConfigBuilder
    private Properties settingsAsProperties(XNode context) {
        if (context == null) {
            return new Properties();
        }
        // 获取 settings 子节点中的内容,解析成Properties,getChildrenAsProperties 方法前面已分析过
        Properties props = context.getChildrenAsProperties();

        // 创建 Configuration 类的“元信息”对象
        MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
        for (Object key : props.keySet()) {
            // 检测 Configuration 中是否存在相关属性,不存在则抛出异常
            if (!metaConfig.hasSetter(String.valueOf(key))) {
                throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
            }
        }
        return props;
    }
  1. 遍历所有的setting,取出name、value封装到Properties
  2. 所有setting配置在Configuration类中都要有一个属性与其对应,如果没有则抛出异常

返回顶部

settingsElement:settings中的信息设置到Configuration对象中

//    XMLConfigBuilder
    private void settingsElement(Properties props) throws Exception {
        // 设置 autoMappingBehavior 属性,默认值为 PARTIAL
        configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
        configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
        // 设置 cacheEnabled 属性,默认值为 true
        configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));

        // 解析默认的枚举处理器
        Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));
        // 设置默认枚举处理器
        configuration.setDefaultEnumTypeHandler(typeHandler);
        configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
        configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    }
  1. 将解析settings标签获取到的properties中的属性在取出来,直接设置到configuration里。

返回顶部

typeAliasesElement:解析typeAliases配置

在 MyBatis 中,可以为我们自己写的有些类定义一个别名。这样在使用的时候,我们只需要输入别名即可,无需再把全限定的类名写出来。

有两种方式进行别名配置。第一种是仅配置包名,让MyBatis去扫描包中的类型,并根据类型得到相应的别名


    

第二种方式是通过手动的方式,明确为某个类型配置别名。


    
    //alias不是必须的,没配置的话就是user

//    XMLConfigBuilder
private void typeAliasesElement(XNode parent) {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            // 第一种方式:自动扫描
            if ("package".equals(child.getName())) {
                String typeAliasPackage = child.getStringAttribute("name");
                //入口3
                configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);

            } else {//第二种方式:配置别名
                // 获取 alias 和 type 属性值,alias 不是必填项,可为空
                String alias = child.getStringAttribute("alias");
                String type = child.getStringAttribute("type");
                try {
                    // 加载 type 对应的类型
                    Class<?> clazz = Resources.classForName(type);

                    // 注册别名到类型的映射
                    if (alias == null) {
                        //入口1
                        typeAliasRegistry.registerAlias(clazz);
                    } else {
                        //入口2
                        typeAliasRegistry.registerAlias(alias, clazz);
                    }
                } catch (ClassNotFoundException e) {
                    throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
                }
            }
        }
    }
}
    public void registerAliases(String packageName) {
        registerAliases(packageName, Object.class);
    }
    public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil> resolverUtil = new ResolverUtil>();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set>> typeSet = resolverUtil.getClasses();
        //查找指定包下的所有类,遍历查找到的类型集合,为每个类型注册别名
        for (Class<?> type : typeSet) {
            // 忽略匿名类,接口,内部类
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                // 为类型注册别名
                registerAlias(type);
            }
        }
    }
//TypeAliasRegistry
private final Map> TYPE_ALIASES = new HashMap>();
    public void registerAlias(Class<?> type) {
        // 获取全路径类名的简称
        String alias = type.getSimpleName();
        Alias aliasAnnotation = type.getAnnotation(Alias.class);
        if (aliasAnnotation != null) {
            // 从注解中取出别名
            alias = aliasAnnotation.value();
        }
        // 调用重载方法注册别名和类型映射
        registerAlias(alias, type);
    }
    public void registerAlias(String alias, Class<?> value) {
        if (alias == null) {
            throw new TypeException("The parameter alias cannot be null");
        }
        // 将别名转成小写
        String key = alias.toLowerCase(Locale.ENGLISH);
        /*
         * 如果 TYPE_ALIASES 中存在了某个类型映射,这里判断当前类型与映射中的类型是否一致,
         * 不一致则抛出异常,不允许一个别名对应两种类型
         */
        if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
            throw new TypeException(
                    "The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
        }
        // 缓存别名到类型映射
        TYPE_ALIASES.put(key, value);
    }
  1. 判断是否包含package属性,如果包含,查找包下所有的类,过滤掉接口、内部类等,遍历挨个调用registerAlias
  2. 如果不包含,判断是否指定了alias属性,如果没指定,则从类获取alias注解,如果获取到注解就按照注解设置的别名,如果没获取到注解,则按照类的名称,最后调用registerAlias
  3. 如果指定了alias,直接按照指定到alias调用registerAlias
  4. 最后看registerAlias:
    • 将别名转换成小写
    • 检查别名不能重复,就是一个别名不能对应两种类型
    • 将别名存入到TypeAliasRegistry中。

返回顶部

pluginElement:解析plugins配置

插件是 MyBatis 提供的一个拓展机制,通过插件机制我们可在 SQL 执行过程中的某些点上做一些自定义操作。比如分页插件,在SQL执行之前动态拼接语句


    
        
    

//    XMLConfigBuilder
    private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                String interceptor = child.getStringAttribute("interceptor");
                // 获取配置信息
                Properties properties = child.getChildrenAsProperties();
                // 解析拦截器的类型,并创建拦截器
                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                // 设置属性
                interceptorInstance.setProperties(properties);
                // 添加拦截器到 Configuration 中
                configuration.addInterceptor(interceptorInstance);
            }
        }
    }
//    Configuration
    public void addInterceptor(Interceptor interceptor) {
        this.interceptorChain.addInterceptor(interceptor);
    }
  1. 插件其实就是拦截器,对执行的sql进行拦截。
  2. 就是解析出plugin,创建拦截器,然后放入到interceptorChain中。interceptorChain类内部维护了一个拦截器集合

返回顶部

environmentsElement:解析environments配置


    
        
        
            
            
            
            
        
    

//    XMLConfigBuilder
    private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
            if (environment == null) {
                // 获取 default 属性
                environment = context.getStringAttribute("default");
            }
            for (XNode child : context.getChildren()) {
                // 获取 id 属性
                String id = child.getStringAttribute("id");
                /*
                 * 检测当前 environment 节点的 id 与其父节点 environments 的属性 default
                 * 内容是否一致,一致则返回 true,否则返回 false
                 * 将其default属性值与子元素environment的id属性值相等的子元素设置为当前使用的Environment对象
                 */
                if (isSpecifiedEnvironment(id)) {
                    // 将environment中的transactionManager标签转换为TransactionFactory对象
                    //入口1
                    TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                    // 将environment中的dataSource标签转换为DataSourceFactory对象
                    //入口2
                    DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                    // 创建 DataSource 对象
                    DataSource dataSource = dsFactory.getDataSource();
                    Environment.Builder environmentBuilder = new Environment.Builder(id)
                            .transactionFactory(txFactory)
                            .dataSource(dataSource);
                    // 构建 Environment 对象,并设置到 configuration 中
                    configuration.setEnvironment(environmentBuilder.build());
                }
            }
        }
    }
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
        if (context != null) {
            String type = context.getStringAttribute("type");
            Properties props = context.getChildrenAsProperties();
            //通过别名获取Class,并实例化
            TransactionFactory factory = (TransactionFactory)this.resolveClass(type).newInstance();
            factory.setProperties(props);
            return factory;
        } else {
            throw new BuilderException("Environment declaration requires a TransactionFactory.");
        }
    }
    private DataSourceFactory dataSourceElement(XNode context) throws Exception {
        if (context != null) {
            String type = context.getStringAttribute("type");
            //通过别名获取Class,并实例化
            Properties props = context.getChildrenAsProperties();
            DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
            factory.setProperties(props);
            return factory;
        } else {
            throw new BuilderException("Environment declaration requires a DataSourceFactory.");
        }
    }
  1. environment的id属性和environments的default属性要一致,用来处理多环境
  2. 通过transactionManager标签和dataSource标签,反射实例化TransactionFactory和DataSourceFactory
  3. 创建DataSource
  4. 将DataSource 和TransactionFactory都设置到environment中,并且将environment添加到Configuration
  5. environment是用来存储事务和数据源的。总结一下这步就是通过反射,按照配置实例化事务工厂和数据源

返回顶部

mapperElement:解析mappers配置

常用的配置有三种情况:
1、接口信息进行配置:这种方式必须保证接口名(例如UserMapper)和xml名(UserMapper.xml)相同,还必须在同一个包中。因为是通过获取mapper中的class属性,拼接上.xml来读取UserMapper.xml,如果xml文件名不同或者不在同一个包中是无法读取到xml的。


    
    
    

2、相对路径进行配置:这种方式不用保证同接口同包同名。但是要保证xml中的namespase和对应的接口名相同。


    
    
    

3、接口所在包进行配置:这种方式和第一种方式要求一致,保证接口名(例如UserMapper)和xml名(UserMapper.xml)相同,还必须在同一个包中。


    

//    XMLConfigBuilder
    private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                //包扫描的形式
                if ("package".equals(child.getName())) {
                    // 获取  节点中的 name 属性
                    String mapperPackage = child.getStringAttribute("name");
                    // 从指定包中查找 所有的 mapper 接口,并根据 mapper 接口解析映射配置
                    configuration.addMappers(mapperPackage);
                } else {
                    String resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");

                    //相对路径的方式
                    if (resource != null && url == null && mapperClass == null) {
                        ErrorContext.instance().resource(resource);
                        InputStream inputStream = Resources.getResourceAsStream(resource);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                        // 解析映射文件
                        mapperParser.parse();
                        // 绝对路径方式,略过
                    } else if (resource == null && url != null && mapperClass == null) {
                        ErrorContext.instance().resource(url);
                        InputStream inputStream = Resources.getUrlAsStream(url);
                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url == null && mapperClass != null) {
                        // 通过 mapperClass 解析映射配置
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        configuration.addMapper(mapperInterface);
                    } else {
                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                    }
                }
            }
        }
    }
//Configuration
    public void addMappers(String packageName) {
        mapperRegistry.addMappers(packageName);
    }
    //MapperRegistry
    public void addMappers(String packageName) {
        this.addMappers(packageName, Object.class);
    }
    public void addMappers(String packageName, Class<?> superType) {
        ResolverUtil> resolverUtil = new ResolverUtil();
        resolverUtil.find(new IsA(superType), packageName);
        Set>> mapperSet = resolverUtil.getClasses();
        Iterator i$ = mapperSet.iterator();

        while(i$.hasNext()) {
            Class<?> mapperClass = (Class)i$.next();
            //找出包下的所有mapper,挨个调用这个方法
            //入口
            this.addMapper(mapperClass);
        }

    }
    public  void addMapper(Class type) {
        if (type.isInterface()) {//mapper是接口
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;

            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();//入口
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }
//MapperAnnotationBuilder
public void parse() {
        String resource = this.type.toString();
        if (!this.configuration.isResourceLoaded(resource)) {
            this.loadXmlResource();//入口
            this.configuration.addLoadedResource(resource);
            this.assistant.setCurrentNamespace(this.type.getName());
            this.parseCache();
            this.parseCacheRef();
            Method[] methods = this.type.getMethods();
            Method[] var3 = methods;
            int var4 = methods.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Method method = var3[var5];

                try {
                    if (!method.isBridge()) {
                        this.parseStatement(method);
                    }
                } catch (IncompleteElementException var8) {
                    this.configuration.addIncompleteMethod(new MethodResolver(this, method));
                }
            }
        }

        this.parsePendingMethods();
    }
private void loadXmlResource() {
        if (!this.configuration.isResourceLoaded("namespace:" + this.type.getName())) {
            String xmlResource = this.type.getName().replace('.', '/') + ".xml";
            InputStream inputStream = null;

            try {
                inputStream = Resources.getResourceAsStream(this.type.getClassLoader(), xmlResource);
            } catch (IOException var4) {
            }

            if (inputStream != null) {
                XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, this.assistant.getConfiguration(), xmlResource, this.configuration.getSqlFragments(), this.type.getName());
                xmlParser.parse();//入口
            }
        }

    }
//XMLMapperBuilder
public void parse() {
    // 检测映射文件是否已经被解析过
    if (!configuration.isResourceLoaded(resource)) {
        //解析mapper的方法入口
        //入口
        configurationElement(parser.evalNode("/mapper"));
        // 添加资源路径到“已解析资源集合”中
        configuration.addLoadedResource(resource);
        // 通过命名空间绑定 Mapper 接口
        bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
}
  1. 按照配置的规则找出所有mapper xml文件(如果配置的是class,则通过名称拼接找到对应的xml)
  2. 通过configurationElement方法挨个对xml文件进行解析
  3. 将解析结果和相应的mapper接口进行绑定

先看一看xml大致是什么样。

<?xml version="1.0" encoding="UTF-8"?>


    
    
    
        
        
    
    
    
        employee
    
    
    
    
    

看一下【通过configurationElement方法挨个对xml文件进行解析】

//XMLMapperBuilder
private void configurationElement(XNode context) {
        try {
            // 获取 mapper 命名空间,如 mapper.EmployeeMapper
            String namespace = context.getStringAttribute("namespace");
            if (namespace == null || namespace.equals("")) {
                throw new BuilderException("Mapper's namespace cannot be empty");
            }

            // 设置命名空间到 builderAssistant 中
            builderAssistant.setCurrentNamespace(namespace);

            // 解析  节点
            cacheRefElement(context.evalNode("cache-ref"));

            // 解析  节点
            cacheElement(context.evalNode("cache"));

            // 已废弃配置,这里不做分析
            parameterMapElement(context.evalNodes("/mapper/parameterMap"));

            // 解析  节点
            resultMapElements(context.evalNodes("/mapper/resultMap"));

            // 解析  节点
            sqlElement(context.evalNodes("/mapper/sql"));

            // 解析 
    SELECT * FROM  WHERE id = #{id}

//XMLMapperBuilder
private void sqlElement(List list) throws Exception {
    if (configuration.getDatabaseId() != null) {
        // 调用 sqlElement 解析  节点
        sqlElement(list, configuration.getDatabaseId());
    }

    // 再次调用 sqlElement,不同的是,这次调用,该方法的第二个参数为 null
    sqlElement(list, null);
}

private void sqlElement(List list, String requiredDatabaseId) throws Exception {
    for (XNode context : list) {
        // 获取 id 和 databaseId 属性
        String databaseId = context.getStringAttribute("databaseId");
        String id = context.getStringAttribute("id");

        // id = currentNamespace + "." + id
        id = builderAssistant.applyCurrentNamespace(id, false);

        // 检测当前 databaseId 和 requiredDatabaseId 是否一致
        if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
            // 将  键值对缓存到XMLMapperBuilder对象的 sqlFragments 属性中,以供后面的sql语句使用
            sqlFragments.put(id, context);
        }
    }
}

返回顶部

解析sql语句节点

//XMLMapperBuilder
private void buildStatementFromContext(List list) {
    if (configuration.getDatabaseId() != null) {
        // 调用重载方法构建 Statement
        buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
}

private void buildStatementFromContext(List list, String requiredDatabaseId) {
    for (XNode context : list) {
        // 创建 XMLStatementBuilder 建造类
        final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        try {
            /*
             * 解析sql节点,将其封装到 Statement 对象中,并将解析结果存储到 configuration 的 mappedStatements 集合中
             */
            statementParser.parseStatementNode();
        } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        }
    }
}
//XMLStatementBuilder
public void parseStatementNode() {
    // 获取 id 和 databaseId 属性
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        return;
    }

    // 获取各种属性
    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");
    LanguageDriver langDriver = getLanguageDriver(lang);

    // 通过别名解析 resultType 对应的类型
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    
    // 解析 Statement 类型,默认为 PREPARED
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    
    // 解析 ResultSetType
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    // 获取节点的名称,比如 
        SELECT  * FROM   WHERE id = #{id}
    

public void applyIncludes(Node source) {
    Properties variablesContext = new Properties();
    Properties configurationVariables = configuration.getVariables();
    if (configurationVariables != null) {
        variablesContext.putAll(configurationVariables);
    }
    //入口
    applyIncludes(source, variablesContext, false);
}
//XMLIncludeTransformer
private void applyIncludes(Node source, final Properties variablesContext, boolean included) {

    // 第一个条件分支  
    //会进入这里
    if (source.getNodeName().equals("include")) {

        //获取  节点。
        Node toInclude = findSqlFragment(getStringAttribute(source, "refid"), variablesContext);

        Properties toIncludeContext = getVariablesContext(source, variablesContext);

        applyIncludes(toInclude, toIncludeContext, true);

        if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
            toInclude = source.getOwnerDocument().importNode(toInclude, true);
        }
        // 将 会进入这里
    } else if (source.getNodeType() == Node.ELEMENT_NODE) {
        if (included && !variablesContext.isEmpty()) {
            NamedNodeMap attributes = source.getAttributes();
            for (int i = 0; i < attributes.getLength(); i++) {
                Node attr = attributes.item(i);
                // 将 source 节点属性中的占位符 ${} 替换成具体的属性值
                attr.setNodeValue(PropertyParser.parse(attr.getNodeValue(), variablesContext));
            }
        }
        
        NodeList children = source.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            // 递归调用
            applyIncludes(children.item(i), variablesContext, included);
        }
        
    // 第三个条件分支
    //SELECT  * FROM和 WHERE id = 5会进入这里
    } else if (included && source.getNodeType() == Node.TEXT_NODE && !variablesContext.isEmpty()) {
        // 将文本(text)节点中的属性占位符 ${} 替换成具体的属性值
        source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
    }
}

观察上面代码有三个分支,这段代码是在做什么?
这段代码是在解析

    
        user
    

    

这个语句。最终生成语句

select * from user where id=5

这里边把语句拆分成3个片段

//片段1, 对应上面代码分支二
,则id=java.mybaits.dao.UserMapper.findOne
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    // 创建建造器,设置各种属性
    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))
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .resultSetType(resultSetType).useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);//这里用到了前面解析节点时创建的Cache对象,设置到MappedStatement对象里面的cache属性中

    // 获取或创建 ParameterMap
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
        statementBuilder.parameterMap(statementParameterMap);
    }

    // 构建 MappedStatement
    MappedStatement statement = statementBuilder.build();
    // 添加 MappedStatement 到 configuration 的 mappedStatements 集合中
    // 通过UserMapper代理对象调用findOne方法时,就可以拼接UserMapper接口名java.mybaits.dao.UserMapper和findOne方法找到id=java.mybaits.dao.UserMapper的MappedStatement,然后执行对应的sql语句
    configuration.addMappedStatement(statement);
    return statement;
}
  1. 一条MappedStatement相当于一个sql语句
  2. MappedStatement相当于最终用来存放解析好的sql的集合。通过UserMapper代理对象调用findOne方法时,就可以拼接UserMapper接口名java.mybaits.dao.UserMapper和findOne方法找到id=java.mybaits.dao.UserMapper的MappedStatement,然后执行对应的sql语句
  3. 所有后续会用到的属性都封装到这个对象里了

总结:1. 首先是替换include和$占位符
2. 解析sql语句封装成一个一个sqlNode,sqlNode分动态和静态部分,同时像where、if等标签有专门的handler处理。
3. 将sqlNode封装到sqlSource中,sqlSource可以看出是sqlNode集合
4. 最后在将sqlSource封装到MappedStatement。

返回顶部

Mapper接口绑定

//XMLMapperBuilder
private void bindMapperForNamespace() {
    // 获取映射文件的命名空间
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
        Class<?> boundType = null;
        try {
            // 根据命名空间解析 mapper 类型
            boundType = Resources.classForName(namespace);
        } catch (ClassNotFoundException e) {
        }
        if (boundType != null) {
            // 检测当前 mapper 类是否被绑定过
            if (!configuration.hasMapper(boundType)) {
                configuration.addLoadedResource("namespace:" + namespace);
                // 绑定 mapper 类
                configuration.addMapper(boundType);
            }
        }
    }
}

// Configuration
public  void addMapper(Class type) {
    // 通过 MapperRegistry 绑定 mapper 类
    mapperRegistry.addMapper(type);
}

// MapperRegistry
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 和 MapperProxyFactory 进行绑定,MapperProxyFactory 可为 mapper 接口生成代理类
             */
            knownMappers.put(type, new MapperProxyFactory(type));
            
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            // 解析注解中的信息
            parser.parse();
            loadCompleted = true;
        } finally {
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}
public class MapperProxyFactory {
    //存放Mapper接口Class
    private final Class mapperInterface;
    private final Map methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class getMapperInterface() {
        return this.mapperInterface;
    }

    public Map getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy mapperProxy) {
        //生成mapperInterface的代理类
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}
  1. 通过命名空间和class找到mapper接口。然后为mapper接口创建一个代理类工厂,将mapper接口和代理类工厂放入到knownMappers。
  2. 那么后续通过mapper接口调用方法的时候就可以通过这个knownMappers找到代理类工厂,然后获取代理类。
  3. 代理类工厂内部是通过jdk动态代理生成代理类的

返回顶部