MyBatis如何加载配置文件

InputStream xml = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(xml);

1. 全局配置文件的加载

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {
        if (null != cl) {
            // try to find the resource as passed
            InputStream returnValue = cl.getResourceAsStream(resource);
            // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
            if (null == returnValue) {
                returnValue = cl.getResourceAsStream("/" + resource);
            }
            if (null != returnValue) {
                return returnValue;
            }
        }
    }
    return null;
}

2.解析配置文件

接下来顺着源码点进去

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

继续往里边走来到了 XMLConfigBuilder.java

public class XMLConfigBuilder extends BaseBuilder {
    // ......
}

这里面用到的第一个底层核心组件,是 XMLConfigBuilder ,直译为基于 xml 的配置建造器(建造器模式的体现)。而这个 XMLConfigBuilder ,首先继承了 BaseBuilder,

BaseBuilder 顾名思义,它是一个基础的构造器,它的初始化需要传入 MyBatis 的全局配置对象 Configuration :

public abstract class BaseBuilder {
    protected final Configuration configuration;
    protected final TypeAliasRegistry typeAliasRegistry;
    protected final TypeHandlerRegistry typeHandlerRegistry;

    public BaseBuilder(Configuration configuration) {
        this.configuration = configuration;
        this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
    }

看一看里边的方法,大多数都是一些解析、获取之类的方法,看上去更像是提供基础的工具类方法支撑,那这样来看,核心的处理逻辑并不在 BaseBuilder 中,我们回

XMLConfigBuilder中

里边有一个核心parse方法

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

 进入到 parseConfiguration(XNode root)方法,该方法会解析mybatis-config.xml文件中的各种配置项。

private void parseConfiguration(XNode root) {
    try {
        // 解析配置定义
        propertiesElement(root.evalNode("properties"));
        // 加载配置项
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        // 处理类型别名
        typeAliasesElement(root.evalNode("typeAliases"));
        // 注册插件
        pluginElement(root.evalNode("plugins"));
        // 注册一些Factory
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        // 应用配置项
        settingsElement(settings);
        // 数据源环境配置
        environmentsElement(root.evalNode("environments"));
        // 数据库厂商标识解析
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        // 注册类型处理器TypeHandler
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 解析mapper.xml、注解Mapper定义
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

我们挑几个比较常用的配置、来看看源码

2.1处理别名类

typeAliasesElement(root.evalNode("typeAliases"));

配置和注册类型的别名,这里面它支持一个一个类的声明,也可以直接包扫描处理,注意包扫描的时候,是会波及子包的(全扫描)

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            // 处理package的包扫描指定别名
            if ("package".equals(child.getName())) {
                String typeAliasPackage = child.getStringAttribute("name");
                // 全扫描的别名注册
                configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
            } else {
                // 处理typeAlias标签的逐个解析
                String alias = child.getStringAttribute("alias");
                String type = child.getStringAttribute("type");
                try {
                    Class<?> clazz = Resources.classForName(type);
                    if (alias == null) {
                        typeAliasRegistry.registerAlias(clazz);
                    } else {
                        typeAliasRegistry.registerAlias(alias, clazz);
                    }
                } // catch ......
            }
        }
    }
}

2.2注册插件


 pluginElement(root.evalNode("plugins"));

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).getDeclaredConstructor().newInstance();
            // 拦截器的属性赋值
            interceptorInstance.setProperties(properties);
            configuration.addInterceptor(interceptorInstance);
        }
    }
}

整体下来,它就是简单的把拦截器创建出来,注册进全局 Configuration 中。当然我们要意识到的一件事:拦截器是 MyBatis 自行创建的,如果我们要用 Spring 整合 MyBatis ,并且想让 Spring 管理 MyBatis 的拦截器,似乎不太现实

2.3数据源环境配置


environmentsElement(root.evalNode("environments"));

mybatis-config.xml 中 environments标签配置

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (environment == null) {
            // 从default中取出默认的数据库环境配置标识
            environment = context.getStringAttribute("default");
        }
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            // 只会构造默认的数据库环境配置
            if (isSpecifiedEnvironment(id)) {
                // 解析transactionManager标签,生成TransactionFactory
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                // 解析dataSource标签,生成DataSource
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                DataSource dataSource = dsFactory.getDataSource();
                // 简单的建造器,构造出Environment对象
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}


2.4数据库厂商标识解析


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

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (environment == null) {
            // 从default中取出默认的数据库环境配置标识
            environment = context.getStringAttribute("default");
        }
        for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            // 只会构造默认的数据库环境配置
            if (isSpecifiedEnvironment(id)) {
                // 解析transactionManager标签,生成TransactionFactory
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
                // 解析dataSource标签,生成DataSource
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
                DataSource dataSource = dsFactory.getDataSource();
                // 简单的建造器,构造出Environment对象
                Environment.Builder environmentBuilder = new Environment.Builder(id)
                    .transactionFactory(txFactory)
                    .dataSource(dataSource);
                configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}


  2.5注册类型处理器TypeHandler


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

    <typeHandlers>
        <typeHandler handler="com.linkedbear.mybatis.handler.DepartmentTypeHandler"
                     javaType="com.linkedbear.mybatis.entity.Department" jdbcType="VARCHAR"/>
    </typeHandlers>

这里面的逻辑也是比较简单,要么包扫描,要么逐个注册,但最终都是注册到了 typeHandlerRegistry

private void typeHandlerElement(XNode parent) {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            // 包扫描
            if ("package".equals(child.getName())) {
                String typeHandlerPackage = child.getStringAttribute("name");
                typeHandlerRegistry.register(typeHandlerPackage);
            } else {
                // 逐个注册TypeHandler
                String javaTypeName = child.getStringAttribute("javaType");
                String jdbcTypeName = child.getStringAttribute("jdbcType");
                String handlerTypeName = child.getStringAttribute("handler");
                Class<?> javaTypeClass = resolveClass(javaTypeName);
                JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
                Class<?> typeHandlerClass = resolveClass(handlerTypeName);
                if (javaTypeClass != null) {
                    if (jdbcType == null) {
                        typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
                    } else {
                        typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
                    }
                } else {
                    typeHandlerRegistry.register(typeHandlerClass);
                }
            }
        }
    }
}


2.6解析mapper.xml、注解Mapper定义


mapperElement(root.evalNode("mappers"));

<mappers>
        <mapper resource="mapper/department.xml"/>
        <mapper class="com.linkedbear.mybatis.mapper.UserAnnotationMapper"/>
    </mappers>
private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            // 包扫描Mapper接口
            if ("package".equals(child.getName())) {
                String mapperPackage = child.getStringAttribute("name");
                configuration.addMappers(mapperPackage);
            } else {
                String resource = child.getStringAttribute("resource");
                String url = child.getStringAttribute("url");
                String mapperClass = child.getStringAttribute("class");
                // 处理resource加载的mapper.xml
                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) {
                    // 处理url加载的mapper.xml
                    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) {
                    // 注册单个Mapper接口
                    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.");
                }
            }
        }
    }
}

从mapperElement(XNode parent)方法可知获取到mapper标签之后,进入了if-else判断中,主要可以分为两大类,解析mapp.xml文件,解析接口。

  1. mapperParser.parse();
  2. configuration.addMapper(mapperInterface);


其中configuration.addMappers(mapperPackage);最后也会走到configuration.addMapper()方法中

public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

具体怎么解析mapper.xml 和mapp接口,这个就不展开了。这个过程有些复杂。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Mybatis是一款优秀的持久层框架,其核心配置文件是用于配置Mybatis的各种参数以及SQL语句映射关系的xml文件。以下是一个简单的Mybatis核心配置文件xml的示例: ```xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <typeAlias type="com.example.User" alias="User"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/example/UserMapper.xml"/> </mappers> </configuration> ``` 上述xml文件中主要包含以下三个部分: 1. **typeAliases**:用于为Java类型建立别名,可以简化后续的SQL语句映射操作。 2. **environments**:定义Mybatis使用的环境,包括事务管理器和数据源等。 3. **mappers**:定义映射文件,即SQL语句与Java方法的映射关系。在示例中,`UserMapper.xml`文件定义了SQL语句与Java接口的映射关系。 这是一个简单的Mybatis核心配置文件xml示例,实际应用中可能会更加复杂,需要根据具体情况进行配置。 ### 回答2: MyBatis核心配置文件是一个重要的配置文件,用于定义和配置MyBatis的全局属性和设置。 在MyBatis核心配置文件中,第一个标签是`<configuration>`,它是整个配置文件的根元素。在该标签中可以配置一些全局通用的设置,比如类型别名、插件等。可以使用`<typeAliases>`标签来定义别名,使得在Mapper映射文件中可以使用别名来代替类的全限定名。插件可以通过自定义拦截器来对SQL语句进行增强或自定义处理。 接下来是`<environments>`标签,用于配置MyBatis的数据库环境。在该标签中可以配置多个`<environment>`子标签,每个子标签代表一个数据库环境,包括数据库连接池、事务管理器等。可以通过`<transactionManager>`标签配置事务管理器,通过`<dataSource>`标签配置数据库连接池。 紧接着是`<mappers>`标签,用于配置Mapper映射器。可以使用`<mapper>`子标签来引入Mapper映射文件,可以配置多个`<mapper>`标签。在Mapper映射文件中定义了与数据库交互的SQL语句和对应的映射关系。 除了上述标签外,还有一些其他的全局配置,比如日志输出方式、延迟加载等。可以通过`<properties>`标签定义一些全局的配置属性,并通过`${}`引用这些属性。 总之,MyBatis核心配置文件MyBatis框架中的一个重要组成部分,通过配置该文件,可以定义和配置一些全局的属性和设置,使得MyBatis能够正常运行并与数据库交互。 ### 回答3: Mybatis的核心配置文件是一个XML文件,用于配置与数据库相关的信息和Mybatis框架的各种功能。 首先,核心配置文件需要指定数据库的连接信息,包括数据库的URL、驱动程序类名、用户名和密码等。这些信息使得Mybatis能够和数据库建立连接,并执行SQL语句。 其次,核心配置文件还包括映射器(Mapper)的注册信息。映射器是一个用于定义数据库操作的接口,通过将接口与数据库的SQL语句进行映射,实现了Java方法和数据库操作的关联。核心配置文件会定义多个映射器的路径,用于告诉Mybatis在哪里可以找到这些映射器的定义。 另外,核心配置文件还可以配置一些全局属性和插件。全局属性可以被映射器中定义的SQL语句引用,用于动态地生成SQL语句。插件可以为Mybatis提供额外的功能,比如自定义拦截器、日志记录等。 在核心配置文件中,还可以定义数据库连接池的配置、缓存的配置、事务管理器的配置等。这些配置项可以根据实际需求进行调整,以满足特定的性能要求和业务需求。 总之,Mybatis的核心配置文件是一个重要的配置文件,用于定义数据库连接信息、映射器的路径和其他配置项。通过对核心配置文件的配置,可以实现与数据库的连接、SQL语句的映射以及其他数据库相关的功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值