mybatisSqlSessionFactory初始化

Posted yanhui007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatisSqlSessionFactory初始化相关的知识,希望对你有一定的参考价值。

代码版本: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<Employee> all = employeeMapper.getAll();
            for (Employee item : all)
                System.out.println(item);
        } finally {
            sqlSession.close();
        }
    }

上面这段代码先创建一个SqlSessionFactory,然后通过SqlSessionFactory获取一个SqlSession,然后在从SqlSession获取需要的mapper。

而我们在实际项目中都是通过spring来使用mybatis,如下配置:

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.letv.shop.web.dao" />
        <property name="sqlSessionFactoryBeanName" value="couponSessionFactory" />  
    </bean>
    <bean id="couponSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="mapperLocations" value="classpath:mybatis/*.xml"></property>   
    	<property name="typeAliasesPackage" value="com.letv.shop.domain"/>
    </bean>
@Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        for (BeanDefinitionHolder holder : beanDefinitions) {//获取所有路径下的mapper接口
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

            //设置Bean的真实类型MapperFactoryBean
            definition.setBeanClass(MapperFactoryBean.class);

        }
        //这个集合返回以后 Spring容器会将里面的所有内容注册到容器中
        return beanDefinitions;
    }

项目启动阶段会扫描配置路径下所有mapper接口,然后将其beanClass都设置为MapperFactoryBean,并且注册到spring容器中。
然后通过spring容器获取mapper的时候,获取的bean的beanClass是MapperFactoryBean,bean实例调用的是MapperFactoryBean的getObject方法:

    public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }

可以发现,不论哪种方式,最终都是获取一个SqlSession,从SqlSession中获取mapper。

接下来我们以第一种方法为入口开始分析。

    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<Employee> all = employeeMapper.getAll();
            for (Employee item : all)
                System.out.println(item);
        } finally {
            sqlSession.close();
        }
    }

首先看下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对象
        //入口1
        Configuration configuration = parser.parse();
        //通过Configuration对象生成SqlSessionFactory
        //入口2
        SqlSessionFactory var5 = this.build(configuration);
        return var5;
    }

其实就是将配置文件的内容解析出来封装到Configuration中。所以先来认识一下xml配置文件和Configuration类。

<?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>    
     <!-- 属性配置,读取properties中的配置文件 -->
    <properties resource="db.propertis">
       <property name="XXX" value="XXX"/>
    </properties>
    
    <!-- 类型别名 -->
    <typeAliases>
       <!-- 在用到User类型的时候,可以直接使用别名,不需要输入User类的全部路径 -->
       <typeAlias type="com.luck.codehelp.entity.User" alias="user"/>
    </typeAliases>

    <!-- 类型处理器 -->
    <typeHandlers>
        <!-- 类型处理器的作用是完成JDBC类型和java类型的转换,mybatis默认已经由了很多类型处理器,正常无需自定义-->
    </typeHandlers>
    
    <!-- 对象工厂 -->
    <!-- mybatis创建结果对象的新实例时,会通过对象工厂来完成,mybatis有默认的对象工厂,正常无需配置 -->
    <objectFactory type=""></objectFactory>
    
    <!-- 插件 -->
    <plugins>
        <!-- 可以自定义拦截器通过plugin标签加入 -->
       <plugin interceptor="com.lucky.interceptor.MyPlugin"></plugin>
    </plugins>
    
    <!-- 全局配置参数 -->
    <settings>   
        <setting name="cacheEnabled" value="false" />   
        <setting name="useGeneratedKeys" value="true" /><!-- 是否自动生成主键 -->
        <setting name="defaultExecutorType" value="REUSE" />   
        <setting name="lazyLoadingEnabled" value="true"/><!-- 延迟加载标识 -->
        <setting name="aggressiveLazyLoading" value="true"/><!--有延迟加载属性的对象是否延迟加载 -->
        <setting name="multipleResultSetsEnabled" value="true"/><!-- 是否允许单个语句返回多个结果集 -->
        <setting name="useColumnLabel" value="true"/><!-- 使用列标签而不是列名 -->
        <setting name="autoMappingBehavior" value="PARTIAL"/><!-- 指定mybatis如何自动映射列到字段属性;NONE:自动映射;PARTIAL:只会映射结果没有嵌套的结果;FULL:可以映射任何复杂的结果 -->
        <setting name="defaultExecutorType" value="SIMPLE"/><!-- 默认执行器类型 -->
        <setting name="defaultFetchSize" value=""/>
        <setting name="defaultStatementTimeout" value="5"/><!-- 驱动等待数据库相应的超时时间 ,单位是秒-->
        <setting name="safeRowBoundsEnabled" value="false"/><!-- 是否允许使用嵌套语句RowBounds -->
        <setting name="safeResultHandlerEnabled" value="true"/>
        <setting name="mapUnderscoreToCamelCase" value="false"/><!-- 下划线列名是否自动映射到驼峰属性:如user_id映射到userId -->
        <setting name="localCacheScope" value="SESSION"/><!-- 本地缓存(session是会话级别) -->
        <setting name="jdbcTypeForNull" value="OTHER"/><!-- 数据为空值时,没有特定的JDBC类型的参数的JDBC类型 -->
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/><!-- 指定触发延迟加载的对象的方法 -->
        <setting name="callSettersOnNulls" value="false"/><!--如果setter方法或map的put方法,如果检索到的值为null时,数据是否有用  -->
        <setting name="logPrefix" value="XXXX"/><!-- mybatis日志文件前缀字符串 -->
        <setting name="logImpl" value="SLF4J"/><!-- mybatis日志的实现类 -->
        <setting name="proxyFactory" value="CGLIB"/><!-- mybatis代理工具 -->
    </settings>  

    <!-- 环境配置集合 -->
    <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?useUnicode=true&amp;characterEncoding=UTF-8" />    
                <property name="username" value="root" />    
                <property name="password" value="root" />    
            </dataSource>    
        </environment>    
    </environments>    
    
    <!-- mapper文件映射配置 -->
    <mappers>    
        <mapper resource="com/luck/codehelp/mapper/UserMapper.xml"/>    
    </mappers>    
</configuration>

Configuration属性:

 public class Configuration {
        protected Environment environment;//运行环境

        protected boolean safeRowBoundsEnabled = false;
        protected boolean safeResultHandlerEnabled = true;
        protected boolean mapUnderscoreToCamelCase = false;
        protected boolean aggressiveLazyLoading = true; //true:有延迟加载属性的对象被调用时完全加载任意属性;false:每个属性按需要加载
        protected boolean multipleResultSetsEnabled = true;//是否允许多种结果集从一个单独的语句中返回
        protected boolean useGeneratedKeys = false;//是否支持自动生成主键
        protected boolean useColumnLabel = true;//是否使用列标签
        protected boolean cacheEnabled = true;//是否使用缓存标识
        protected boolean callSettersOnNulls = false;//
        protected boolean useActualParamName = true;

        protected String logPrefix;
        protected Class <? extends Log> logImpl;
        protected Class <? extends VFS> vfsImpl;
        protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
        protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
        protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
        protected Integer defaultStatementTimeout;
        protected Integer defaultFetchSize;
        protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
        protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;//指定mybatis如果自动映射列到字段和属性,PARTIAL会自动映射简单的没有嵌套的结果,FULL会自动映射任意复杂的结果
        protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

        protected Properties variables = new Properties();
        protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        protected ObjectFactory objectFactory = new DefaultObjectFactory();
        protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

        protected boolean lazyLoadingEnabled = false;//是否延时加载,false则表示所有关联对象即使加载,true表示延时加载
        protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

        protected String databaseId;

        protected Class<?> configurationFactory;

        protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
        protected final InterceptorChain interceptorChain = new InterceptorChain();
        protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
        protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
        protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

        protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
        protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
        protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
        protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
        protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

        protected final Set<String> loadedResources = new HashSet<String>(); //已经加载过的resource(mapper)
        protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

        protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
        protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
        protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
        protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

        protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

    }

Configuration构造器:注意,这里创建了一个用来存放mapper别名的typeAliasRegistry ,并且初始化好多mapper进去。

public Configuration() {
        this.safeRowBoundsEnabled = false;
        this.safeResultHandlerEnabled = true;
        this.mapUnderscoreToCamelCase = false;
        this.aggressiveLazyLoading = true;
        this.multipleResultSetsEnabled = true;
        this.useGeneratedKeys = false;
        this.useColumnLabel = true;
        this.cacheEnabled = true;
        this.callSettersOnNulls = false;
        this.localCacheScope = LocalCacheScope.SESSION;
        this.jdbcTypeForNull = JdbcType.OTHER;
        this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
        this.defaultExecutorType = ExecutorType.SIMPLE;
        this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
        this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
        this.variables = new Properties();
        this.reflectorFactory = new DefaultReflectorFactory();
        this.objectFactory = new DefaultObjectFactory();
        this.objectWrapperFactory = new DefaultObjectWrapperFactory();
        this.mapperRegistry = new MapperRegistry(this);
        this.lazyLoadingEnabled = false;
        this.proxyFactory = new JavassistProxyFactory();
        this.interceptorChain = new InterceptorChain();
        this.typeHandlerRegistry = new TypeHandlerRegistry();
        //这个属性就是用来存储mapper的
        this.typeAliasRegistry = new TypeAliasRegistry();
        this.languageRegistry = new LanguageDriverRegistry();
        this.mappedStatements = new Configuration.StrictMap("Mapped Statements collection");
        this.caches = new Configuration.StrictMap("Caches collection");
        this.resultMaps = new Configuration.StrictMap("Result Maps collection");
        this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");
        this.keyGenerators = new Configuration.StrictMap("Key Generators collection");
        this.loadedResources = new HashSet();
        this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");
        this.incompleteStatements = new LinkedList();
        this.incompleteCacheRefs = new LinkedList();
        this.incompleteResultMaps = new LinkedList();
        this.incompleteMethods = new LinkedList();
        this.cacheRefMap = new HashMap();
        //这里初始化了好多mapper
        this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
        this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
        this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
        this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
        this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
        this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
        this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
        this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
        this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
        this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
        this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
        this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
        this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
        this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
        this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
        this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
        this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
        this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
        this.languageRegistry.register(RawLanguageDriver.class);
    }

TypeAliasRegistry:内部是一个map

public class TypeAliasRegistry {
        //内部是一个map
        private final Map<String, Class<?>> TYPE_ALIASES = new HashMap();

        public TypeAliasRegistry() {
            this.registerAlias("string", String.class);
            this.registerAlias("byte", Byte.class);
            this.registerAlias("long", Long.class);
            this.registerAlias("short", Short.class);
            this.registerAlias("int", Integer.class);
            this.registerAlias("integer", Integer.class);
            this.registerAlias("double", Double.class);
            this.registerAlias("float", Float.class);
            this.registerAlias("boolean", Boolean.class);
            this.registerAlias("byte[]", Byte[].class);
            this.registerAlias("long[]", Long[].class);
            this.registerAlias("short[]", Short[].class);
            this.registerAlias("int[]", Integer[].class);
            this.registerAlias("integer[]", Integer[].class);
            this.registerAlias("double[]", Double[].class);
            this.registerAlias("float[]", Float[].class);
            this.registerAlias("boolean[]", Boolean[].class);
            this.registerAlias("_byte", Byte.TYPE);
            this.registerAlias("_long", Long.TYPE);
            this.registerAlias("_short", Short.TYPE);
            this.registerAlias("_int", Integer.TYPE);
            this.registerAlias("_integer", Integer.TYPE);
            this.registerAlias("_double", Double.TYPE);
            this.registerAlias("_float", Float.TYPE);
            this.registerAlias("_boolean", Boolean.TYPE);
            this.registerAlias("_byte[]", byte[].class);
            this.registerAlias("_long[]", long[].class);
            this.registerAlias("_short[]", short[].class);
            this.registerAlias("_int[]", int[].class);
            this.registerAlias("_integer[]", int[].class);
            this.registerAlias("_double[]", double[].class);
            this.registerAlias("_float[]", float[].class);
            this.registerAlias("_boolean[]", boolean[].class);
            this.registerAlias("date", Date.class);
            this.registerAlias("decimal", BigDecimal.class);
            this.registerAlias("bigdecimal", BigDecimal.class);
            this.registerAlias("biginteger", BigInteger.class);
            this.registerAlias("object", Object.class);
            this.registerAlias("date[]", Date[].class);
            this.registerAlias("decimal[]", BigDecimal[].class);
            this.registerAlias("bigdecimal[]", BigDecimal[].class);
            this.registerAlias("biginteger[]", BigInteger[].class);
            this.registerAlias("object[]", Object[].class);
            this.registerAlias("map", Map.class);
            this.registerAlias("hashmap", HashMap.class);
            this.registerAlias("list", List.class);
            this.registerAlias("arraylist", ArrayList.class);
            this.registerAlias("collection", Collection.class);
            this.registerAlias("iterator", Iterator.class);
            this.registerAlias("ResultSet", ResultSet.class);
        }
    }

接下来看看具体是如何解析xml的:

//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);
        }
    }

解析 properties 配置

<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</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;
    }

需要注意的是,propertiesElement 方法是先解析 properties 节点的子节点内容,后再从文件系统或者网络读取属性配置,并将所有的属性及属性值都放入到 defaults 属性对象中。这就会存在同名属性覆盖的问题,也就是从文件系统,或者网络上读取到的属性及属性值会覆盖掉 properties 子节点中同名的属性和及值。

解析 settings 配置

<settings>
    <setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="autoMappingBehavior" value="PARTIAL"/>
</settings>
//    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;
    }

设置 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));
    }

解析 typeAliases 配置

在 MyBatis 中,可以为我们自己写的有些类定义一个别名。这样在使用的时候,我们只需要输入别名即可,无需再把全限定的类名写出来。在 MyBatis 中,我们有两种方式进行别名配置。第一种是仅配置包名,让 MyBatis 去扫描包中的类型,并根据类型得到相应的别名

<typeAliases>
    <package name="com.mybatis.model"/>
</typeAliases>

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

<typeAliases>
    <typeAlias alias="employe" type="com.mybatis.model.Employe" />
    <typeAlias type="com.mybatis.model.User" />//alias不是必须的,没配置的话就是user
</typeAliases>
//    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);
                }
            }
        }
    }
}

注册的alias分三种情况:1、指定package 2、指定class 3、指定class和type

先看后两个:


//TypeAliasRegistry
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
    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);
    }

在看第一个:

    public void registerAliases(String packageName) {
        registerAliases(packageName, Object.class);
    }
    public void registerAliases(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
        //查找指定包下的所有类,遍历查找到的类型集合,为每个类型注册别名
        for (Class<?> type : typeSet) {
            // 忽略匿名类,接口,内部类
            if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
                // 为类型注册别名
                registerAlias(type);
            }
        }
    }

解析 plugins 配置

插件是 MyBatis 提供的一个拓展机制,通过插件机制我们可在 SQL 执行过程中的某些点上做一些自定义操作。比喻分页插件,在SQL执行之前动态拼接语句,我们后面会单独来讲插件机制,先来了解插件的配置。如下:

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
    </plugin>
</plugins>
//    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);
    }

就是解析出plugin,创建一个拦截器,然后放入到interceptorChain中,看一下interceptorChain长啥样。

public class InterceptorChain {
    //一个拦截器集合
    private final List<Interceptor> interceptors = new ArrayList();
}

解析 environments 配置

在 MyBatis 中,事务管理器和数据源是配置在 environments 中的。

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</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.");
        }
    }

中type有"JDBC"、"MANAGED"这两种配置,而我们前面Configuration中默认注册的别名中有对应的JdbcTransactionFactory.class、ManagedTransactionFactory.class这两个TransactionFactory

中type有"JNDI"、"POOLED"、"UNPOOLED"这三种配置,默认注册的别名中有对应的JndiDataSourceFactory.class、PooledDataSourceFactory.class、UnpooledDataSourceFactory.class这三个DataSourceFactory

而我们的environment配置中transactionManager type="JDBC"和dataSource type="POOLED",则生成的transactionManager为JdbcTransactionFactory,DataSourceFactory为PooledDataSourceFactory

解析 mappers 配置

mapperElement方法会将mapper标签内的元素转换成MapperProxyFactory产生的代理类,和与mapper.xml文件的绑定,这里先预留个位置,后面要详细讲解。

到现在所有标签解析完毕,并且都封装到Configuration中了,最后一步就是将Configuration在封装到SqlSessionFactory中。

    //只是将configuration设置为其属性
    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

以上是关于mybatisSqlSessionFactory初始化的主要内容,如果未能解决你的问题,请参考以下文章

mybatis 问题

云笔记开发思路

04- Mybatis SqlSessionFactory

Hadoop初体验

小升初数学数论知识归纳总结

家教初代人物