日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis源码分析之(二)根据配置文件创建SqlSessionFactory(Configuration的创建过程)

發布時間:2025/3/12 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis源码分析之(二)根据配置文件创建SqlSessionFactory(Configuration的创建过程) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

SqlSessionFactoryBuilder.build創建SqlSessionFactory(粗略走一步流程)

看完上篇文章后,你對mybatis應該有個大概的了解了,那么我們知道new SqlSessionFactoryBuilder().build是框架的入口,我們到SqlSessionFactoryBuilder類里看到里面其實都是build函數的。

全都是build重載函數。上面幾個重載其實最終都是調用了build(Reader reader, String environment, Properties properties)這個方法。下面的幾個則是調用了build(InputStream inputStream, String environment, Properties properties) 。

最終都會調用最后一個build(Configuration config)方法,把創建好的Configuration賦給DefaultSqlSessionFactory里面的Configuration字段,所以這里的邏輯其實非常簡單,就是解析傳入配置文件的Reader或者inputStream,然后生成Configuration,賦值給新建出來的DefaultSqlSessionFactor中的configuration字段,然后返回DefaultSqlSessionFactor。

public class SqlSessionFactoryBuilder {public SqlSessionFactory build(Reader reader) {return build(reader, null, null);}public SqlSessionFactory build(Reader reader, String environment) {return build(reader, environment, null);}public SqlSessionFactory build(Reader reader, Properties properties) {return build(reader, null, properties);}public SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);return build(parser.parse());} 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.}}}public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment) {return build(inputStream, environment, null);}public SqlSessionFactory build(InputStream inputStream, Properties properties) {return build(inputStream, null, properties);}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);}}

下面,我們來完整的走一個流程,就以上一篇的demo為例

//第一步public static SqlSession getSqlsession(){//獲取mybatis,config的xml文件的輸入流InputStream config = MybatisUtil.class.getClassLoader().getResourceAsStream("mybatis.cfg.xml");//使用SqlSessionFactory build(InputStream inputStream);來獲取SqlSessionFactorySqlSessionFactory build = new SqlSessionFactoryBuilder().build(config);return build.openSession();}//第二步調用SqlSessionFactoryBuilder中的build(InputStream inputStream)方法public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}//第三部調用SqlSessionFactoryBuilder中的build(InputStream inputStream, String environment, Properties properties)方法public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {//把輸入流解析成可以用做分析的document,以及準備其他解析需要的東西XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);//parser.parse()生成出Configuration類,最后調用build(Configuration config)方法來生成DefaultSqlSessionFactory,并返回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.}}}//第四步調用SqlSessionFactoryBuilder中的 build(Configuration config) 方法public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

這是最上面的一層,邏輯還是非常簡單的,一句話就能概括,就是從給定的配置文件中解析出Configuration,然后生成DefaultSqlSessionFactory。(如果要指定環境,和特定的屬性的話用另外2個build 的重載方法)

我們先進入最后一個build看一下,new DefaultSqlSessionFactory(config);究竟做了什么。

public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {//非常簡單,就是生成了一個DefaultSqlSessionFactory 類,然后把里面的configuration字段,賦值為傳入的configuration。this.configuration = configuration;}//后面的函數就不列出來了}

build是如何通過xml文件來生成Configuration的(比較詳細的分析流程)

所以build的重點在解析xml成Configuration 類這個地方。
也就是下面這兩句話

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);parser.parse();

ok,第一句話是生成了一個XMLConfigBuilder類,我們來看看XMLConfigBuilder到底是什么樣的。只列出字段和關鍵方法

public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private XPathParser parser;private String environment;private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();//上層調用到這個方法,然后他調用到了下面的重載方法public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {//這里只有new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())需要看一下this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}//其實就是給關鍵的幾個字段賦值,這里的第一句調用了父類的構造函數,傳入了一個新的Configuration,也就是說到這步的時候,我們用來返回的Configuration對象已經有了,但是Configuration的值還沒有構造好。private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props);this.parsed = false;this.environment = environment;this.parser = parser;}}public abstract class BaseBuilder {protected final Configuration configuration;protected final TypeAliasRegistry typeAliasRegistry;protected final TypeHandlerRegistry typeHandlerRegistry;}

接下來我們來看看XPathParser類

public class XPathParser {private Document document;private boolean validation;private EntityResolver entityResolver;private Properties variables;private XPath xpath;public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {//為類的其他字段賦值commonConstructor(validation, variables, entityResolver);//根據輸入流創建一個Document ,并賦值給 document字段(之后的解析就靠這里面的信息了)this.document = createDocument(new InputSource(inputStream));}}

xml解析成Document的具體過程LZ就不帶大家深入研究了,有興趣的朋友可以自己跟下去看看。

接下來我們回到SqlSessionFactoryBuilder類。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {//現在我們已經拿到parser了XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);//接下來要去看parse()是怎么創建出Configuration的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 Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;//解析靠這步,首先拿到configuration節點parseConfiguration(parser.evalNode("/configuration"));return configuration;}public XNode evalNode(String expression) {return evalNode(document, expression);}public XNode evalNode(Object root, String expression) {//拿到configuration節點Node node = (Node) evaluate(expression, root, XPathConstants.NODE);if (node == null) {return null;}//返回封裝成XNode類return new XNode(this, node, variables);} public class XNode {private Node node;private String name;private String body;private Properties attributes;private Properties variables;private XPathParser xpathParser;public XNode(XPathParser xpathParser, Node node, Properties variables) {this.xpathParser = xpathParser;this.node = node;this.name = node.getNodeName();this.variables = variables;this.attributes = parseAttributes(node);this.body = parseBody(node);} }

XPathParser類的實際值

封裝好的Xnode的實際值

//接下來就要解析并給configuration賦值拉。因為configuration在新建XMLConfigBuilder的時候就已經創建好了,解析的過程其實就是賦值的過程(我們在xml文件里配置的各個節點都在下面這個函數里解析) private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));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);}}

這是xml文件中要的標簽,和上面代碼一對比,你就會清楚的明白,上面每一個方法其實就是把每一個標簽轉換成configuration。

private void propertiesElement(XNode context) throws Exception {if (context != null) {//從子屬性直接獲取屬性的鍵值對Properties defaults = context.getChildrenAsProperties();//從外部引入屬性文件(其實就是獲取resource屬性的值)String resource = context.getStringAttribute("resource");//也可以從url下獲取屬性文件(引入網絡路徑或者磁盤路徑下的資源)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.");}//從resource 或者 url的屬性文件中加載鍵值對,若和上面的defaults,鍵相同則替換defaults中的if (resource != null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {defaults.putAll(Resources.getUrlAsProperties(url));}Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}//設置parser的Variablesparser.setVariables(defaults);//設置configuration的Variablesconfiguration.setVariables(defaults);}}

其他的解析其實和properties的解析差不多。根據配置文件節點的信息設置configuration。

因為mybatis非常重要的一個點就是在mapper上,所以樓主之后會有一篇專門寫mapper是什么解析和使用的。所以這里大家可以簡單過一下mapper的解析過程。

private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {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");if (resource != null && url == null && mapperClass == null) { //根據resource解析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 解析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.");}}}}}

套路其實差不多,就不細細的深入看了。但是mybatis的mapper的實現方法還是要細細討論的,讓我們在之后的文章中在細致的學習以下。

小結

看了加載過程的源碼,我們在這至少了解到了mybatis是如何解析xml文件的,還有properties,假如我們即在xml文件中直接配置的屬性和又設置了resource(或者url)的話,resource(或者url)中的屬性會覆蓋xml文件中直接配的(key相同的情況下才會覆蓋,否則是累加的情況),若詳細看過mapper的創建過程的話,還會知道在xml中配的mapper的語句會被注解中配置的語句覆蓋。為什么會有這些特性?因為源碼的讀取步驟以及策略如此,所以要善于利用

總結

以上是生活随笔為你收集整理的Mybatis源码分析之(二)根据配置文件创建SqlSessionFactory(Configuration的创建过程)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。