一:简介这篇开始是根据Mybatis源码来对Mybatis进行更深入的学习、当然、精力有限、还做不到学习的面面俱到。Mybatis初始化过程可以用一句话概括:就是将Mybatis的配置信息加载到一个类中、供后面Mybatis进行各种操作时使用、这个类叫:Configuration——见名知意。当然这个类的功能并不仅限与存放配置文件信息。 二:整体流程下面是一段正常情况下从加载配置到执行sql语句的代码: String mybatisConfigPath = "config/mybatis/mybatis.xml";InputStream inputStream = Resources.getResourceAsStream(mybatisConfigPath);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = sqlSessionFactory.openSession();int count = (Integer)sqlSession.selectOne("org.alien.mybatis.samples.mapper.AuthorMapper.getAllAuthorsCount");System.out.println(count); 初始化过程在上面代码中就是获取SqlSessionFactory的过程。 参照流程图、初始化大致步骤:
三:详细过程3.1 加载配置文件这一步很简单、从代码层面上来看就是将配置文件以流的形式读取到程序中、并将其作为参数传递给SqlSessionFactoryBuilder以供后面创建SqlSessionFactory。其提供了许多重载的方法供我们选择: 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. }} } public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config); } 3.2解析配置文件解析配置文件的入口是在SqlSessionFactoryBuilder中的: 流程图: 3.2.1 整理流程:
3.2.2 代码流程
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try { //创建解析文件并装配Configuration的类 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); //这里分开写、清楚一点。解析配置文件、装配Configuration并返回 Configuration configuration = parser.parse(); //根据Configuration创建SqlSessionFactory并返回 return build(configuration);} 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. }} } 先看 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); }
2、XMLMapperEntityResolver的创建: 3、XPathParser的创建: public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {//填充XPathParser 部分私有属性commonConstructor(validation, variables, entityResolver);//根据InputStream来创建Document对象用于后面操作配置文件。this.document = createDocument(new InputSource(inputStream)); }
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {this.validation = validation;this.entityResolver = entityResolver;this.variables = variables;XPathFactory factory = XPathFactory.newInstance();this.xpath = factory.newXPath(); }
private Document createDocument(InputSource inputSource) {// important: this must only be called AFTER common constructortry { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() {public void error(SAXParseException exception) throws SAXException { throw exception;}public void fatalError(SAXParseException exception) throws SAXException { throw exception;}public void warning(SAXParseException exception) throws SAXException {} }); return builder.parse(inputSource);} catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e);} }
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource("SQL Mapper Configuration");//解析文件代码只能执行一次、当解析之后此值将变为truethis.parsed = false;this.environment = environment;//前面实例化好的XPathParserthis.parser = parser; }
4、当XMLConfigBuilder实例化好之后、接下来就是解析配置文件、装配Configuration。 public Configuration parse() {if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;//对于parser.evalNode(String node)如何执行的、这里不关注。只需要知道parser.evalNode(String node)是干嘛的就行。 parseConfiguration(parser.evalNode("/configuration"));return configuration; }
private void parseConfiguration(XNode root) {try { propertiesElement(root.evalNode("properties")); //issue #117 read properties first typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers"));} catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);} }
5、这里只看其中一个最简单的过程——将Mybatis配置文件中的 private void settingsElement(XNode context) throws Exception {if (context != null) { Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class MetaClass metaConfig = MetaClass.forClass(Configuration.class); for (Object key : props.keySet()) {if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");} } configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true)); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setLogImpl(resolveClass(props.getProperty("logImpl"))); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));} } 6、解析装配完成之后、返回Configuration public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config); }
补充:多提一句、网上有的说SqlSessionFactory的创建用到了创建者模式、觉得并不是那么恰当、建造者模式的核心是有一个调度员来根据不同的场景来调度不同的创建者创建具体对象。而这里并没有。个人觉得只是方法的一系列的重载、来方便使用者根据不同的场景或者喜好来创建SqlSessionFactory。 |
|