Mybatis源码解读(五)--配置加载(建造者模式)

    技术2022-07-10  140

    Mybatis启动流程就是组装Configuration对象的过程,在这其中主要就是初始化环境变量和初始化Mapper.xml的映射。

    Mybatis采用了建造者模式来加载配置。

    建造者模式

    建造者模式(Builder Pattern):使用多个简单的对象一步步构建成一个复杂的对象,这种设计模式属于创建型模式,提供了一种创建对象的方式。

    Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。 ConcreteBuilder:实现Builder接口,针对不同的业务逻辑,具体化复杂对象的各部分创建。在建造过程完成后,提供产品的实例。 Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。 Product:要创建的复杂对象。

     

    Mybatis建造者

    核心类

    Configuration:Mybatis启动初始化的核心就是将XML配置文件的信息加载到Configuration对象中。Configuration是单例的,生命周期是应用级的。

    protected Environment environment; /* 是否启用行内嵌套语句**/ protected boolean safeRowBoundsEnabled; protected boolean safeResultHandlerEnabled = true; /* 是否启用数据组A_column自动映射到Java类中的驼峰命名的属性**/ protected boolean mapUnderscoreToCamelCase; /*当对象使用延迟加载时 属性的加载取决于能被引用到的那些延迟属性,否则,按需加载(需要的是时候才去加载)**/ protected boolean aggressiveLazyLoading; /*是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true **/ protected boolean multipleResultSetsEnabled = true; /*-允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false**/ protected boolean useGeneratedKeys; /* 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。**/ protected boolean useColumnLabel = true; /*配置全局性的cache开关,默认为true**/ protected boolean cacheEnabled = true; protected boolean callSettersOnNulls; protected boolean useActualParamName = true; protected boolean returnInstanceForEmptyRow; /* 日志打印所有的前缀 **/ protected String logPrefix; /* 指定 MyBatis 所用日志的具体实现,未指定时将自动查找**/ protected Class <? extends Log> logImpl; protected Class <? extends VFS> vfsImpl; /* 设置本地缓存范围,session:就会有数据的共享,statement:语句范围,这样不会有数据的共享**/ protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION; /* 设置但JDBC类型为空时,某些驱动程序 要指定值**/ protected JdbcType jdbcTypeForNull = JdbcType.OTHER; /* 设置触发延迟加载的方法**/ protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString")); /* 设置驱动等待数据响应超时数**/ protected Integer defaultStatementTimeout; /* 设置驱动返回结果数的大小**/ protected Integer defaultFetchSize; /* 执行类型,有simple、resue及batch**/ protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; /*指定 MyBatis 应如何自动映射列到字段或属性*/ protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL; protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE; protected Properties variables = new Properties(); protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); /*MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO*/ protected ObjectFactory objectFactory = new DefaultObjectFactory(); protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory(); /*延迟加载的全局开关*/ protected boolean lazyLoadingEnabled = false; /*指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具*/ protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL protected String databaseId; /** * Configuration factory class. * Used to create Configuration for loading deserialized unread properties. * * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a> */ protected Class<?> configurationFactory; /*插件集合*/ protected final InterceptorChain interceptorChain = new InterceptorChain(); /*TypeHandler注册中心*/ protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(); /*TypeAlias注册中心*/ protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry(); //------------------------------------------------------------- /*mapper接口的动态代理注册中心*/ protected final MapperRegistry mapperRegistry = new MapperRegistry(this); /*mapper文件中增删改查操作的注册中心*/ protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection"); /*mapper文件中配置cache节点的 二级缓存*/ protected final Map<String, Cache> caches = new StrictMap<>("Caches collection"); /*mapper文件中配置的所有resultMap对象 key为命名空间+ID*/ protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection"); protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection"); /*mapper文件中配置KeyGenerator的insert和update节点,key为命名空间+ID*/ protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection"); /*加载到的所有*mapper.xml文件*/ protected final Set<String> loadedResources = new HashSet<>(); /*mapper文件中配置的sql元素,key为命名空间+ID*/ protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers"); protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>(); protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>(); protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>(); protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

    MapperRegistry:mapper接口动态代理工厂类的注册中心。Mybatis中,通过mapperProxy实现InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象。

    ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素。

    MappedStatement:用于储存mapper.xml文件中的select、insert、update、delete节点,同时还包含这些节点的其他重要属性。 SqlSource:mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行。  

    启动流程

    SqlSessionFactoryBuilder读取配置文件

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { //读取配置文件 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse());//解析配置文件得到configuration对象,并返回SqlSessionFactory } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }

    XMLConfigBuilder解析config.xml

    public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { //解析<properties>节点 propertiesElement(root.evalNode("properties")); //解析<settings>节点 Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); //解析<typeAliases>节点 typeAliasesElement(root.evalNode("typeAliases")); //解析<plugins>节点 pluginElement(root.evalNode("plugins")); //解析<objectFactory>节点 objectFactoryElement(root.evalNode("objectFactory")); //解析<objectWrapperFactory>节点 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //解析<reflectorFactory>节点 reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings);//将settings填充到configuration // read it after objectFactory and objectWrapperFactory issue #631 //解析<environments>节点 environmentsElement(root.evalNode("environments")); //解析<databaseIdProvider>节点 databaseIdProviderElement(root.evalNode("databaseIdProvider")); //解析<typeHandlers>节点 typeHandlerElement(root.evalNode("typeHandlers")); //解析<mappers>节点 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }

    XMLMapperBuilder解析mapper.xml

    public void parse() { //判断是否已经加载该配置文件 if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper"));//处理mapper节点 configuration.addLoadedResource(resource);//将mapper文件添加到configuration.loadedResources中 bindMapperForNamespace();//注册mapper接口 } //处理解析失败的ResultMap节点 parsePendingResultMaps(); //处理解析失败的CacheRef节点 parsePendingCacheRefs(); //处理解析失败的Sql语句节点 parsePendingStatements(); } public XNode getSqlFragment(String refid) { return sqlFragments.get(refid); } private void configurationElement(XNode context) { try { //获取mapper节点的namespace属性 String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } //设置builderAssistant的namespace属性 builderAssistant.setCurrentNamespace(namespace); //解析cache-ref节点 cacheRefElement(context.evalNode("cache-ref")); //重点分析 :解析cache节点----------------1------------------- cacheElement(context.evalNode("cache")); //解析parameterMap节点(已废弃) parameterMapElement(context.evalNodes("/mapper/parameterMap")); //重点分析 :解析resultMap节点(基于数据结果去理解)----------------2------------------- resultMapElements(context.evalNodes("/mapper/resultMap")); //解析sql节点 sqlElement(context.evalNodes("/mapper/sql")); //重点分析 :解析select、insert、update、delete节点 ----------------3------------------- 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); } } //解析select、insert、update、delete节点 private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); } //处理所有的sql语句节点并注册至configuration对象 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { //创建XMLStatementBuilder 专门用于解析sql语句节点 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { //解析sql语句节点 statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }

    XMLStatementBuilder解析sql节点

    public void parseStatementNode() { //获取sql节点的id String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } /*获取sql节点的各种属性*/ 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); Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); //根据sql节点的名称获取SqlCommandType(INSERT, UPDATE, DELETE, SELECT) String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); 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 //在解析sql语句之前先解析<include>节点 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. //在解析sql语句之前,处理<selectKey>子节点,并在xml节点中删除 processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) //解析sql语句是解析mapper.xml的核心,实例化sqlSource,使用sqlSource封装sql语句 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets");//获取resultSets属性 String keyProperty = context.getStringAttribute("keyProperty");//获取主键信息keyProperty String keyColumn = context.getStringAttribute("keyColumn");///获取主键信息keyColumn //根据<selectKey>获取对应的SelectKeyGenerator的id KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); //获取keyGenerator对象,如果是insert类型的sql语句,会使用KeyGenerator接口获取数据库生产的id; if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } //通过builderAssistant实例化MappedStatement,并注册至configuration对象 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); } private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) { List<XNode> selectKeyNodes = context.evalNodes("selectKey"); if (configuration.getDatabaseId() != null) { parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId()); } parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null); removeSelectKeyNodes(selectKeyNodes); } private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) { for (XNode nodeToHandle : list) { String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX; String databaseId = nodeToHandle.getStringAttribute("databaseId"); if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) { parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId); } } } private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) { String resultType = nodeToHandle.getStringAttribute("resultType"); Class<?> resultTypeClass = resolveClass(resultType); StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString())); String keyProperty = nodeToHandle.getStringAttribute("keyProperty"); String keyColumn = nodeToHandle.getStringAttribute("keyColumn"); boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER")); //defaults boolean useCache = false; boolean resultOrdered = false; KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE; Integer fetchSize = null; Integer timeout = null; boolean flushCache = false; String parameterMap = null; String resultMap = null; ResultSetType resultSetTypeEnum = null; SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass); SqlCommandType sqlCommandType = SqlCommandType.SELECT; builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null); id = builderAssistant.applyCurrentNamespace(id, false); MappedStatement keyStatement = configuration.getMappedStatement(id, false); configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore)); } private void removeSelectKeyNodes(List<XNode> selectKeyNodes) { for (XNode nodeToHandle : selectKeyNodes) { nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode()); } } private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { if (requiredDatabaseId != null) { if (!requiredDatabaseId.equals(databaseId)) { return false; } } else { if (databaseId != null) { return false; } // skip this statement if there is a previous one with a not null databaseId id = builderAssistant.applyCurrentNamespace(id, false); if (this.configuration.hasStatement(id, false)) { MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2 if (previous.getDatabaseId() != null) { return false; } } } return true; }

    MapperBuilderAssistant辅助XMLMapperBuilder解析mapper.xml文件,完善属性信息,并注册到configuration对象

    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"); } 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)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); return statement; }

     

    Processed: 0.016, SQL: 9