MybatisSqlSessionFactoryBuilder源碼的示例 分析,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。
創(chuàng)新互聯(lián)主營(yíng)索縣網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,手機(jī)APP定制開(kāi)發(fā),索縣h5微信小程序定制開(kāi)發(fā)搭建,索縣網(wǎng)站營(yíng)銷推廣歡迎索縣等地區(qū)企業(yè)咨詢
public static void main(String[] args) { try { // 基本mybatis環(huán)境 // 1.定義mybatis_config文件地址 String resources = "mybatis_config.xml"; // 2.獲取InputStreamReaderIo流 Reader reader = Resources.getResourceAsReader(resources); // 3.獲取SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // 4.獲取Session SqlSession sqlSession = sqlSessionFactory.openSession(); // 5.操作Mapper接口 UserMapper mapper = sqlSession.getMapper(UserMapper.class); UserEntity user = mapper.getUser(2); System.out.println(user.getName()); } catch (Exception e) { e.printStackTrace(); } }
// 2.獲取InputStreamReaderIo流
Reader reader = Resources.getResourceAsReader(resources);
public static Reader getResourceAsReader(String resource) throws IOException { InputStreamReader reader; if (charset == null) { reader = new InputStreamReader(getResourceAsStream(resource)); } else { reader = new InputStreamReader(getResourceAsStream(resource), charset); } return reader; }
通過(guò)上述代碼可知:使用了門(mén)面模式:定義了Resource類,把復(fù)雜過(guò)程封裝起來(lái),方便用戶使用,返回reader為InputStreamReader,指的是讀取的mybatis_config.xml文件,斷點(diǎn)調(diào)試結(jié)果如下:
// 3.獲取SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
進(jìn)入SqlSessionFactoryBuilder()構(gòu)造函數(shù)如下:
public SqlSessionFactoryBuilder() { }
可知,無(wú)參構(gòu)造函數(shù)沒(méi)用做任何事情,再進(jìn)入build(reader)源碼,reader參數(shù)為InputStream流
public SqlSessionFactory build(Reader reader) { return this.build((Reader)reader, (String)null, (Properties)null); }
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException var13) { ; } } return var5; }
我們來(lái)分析下XMLConfigBuilder這個(gè)類是干嘛的,進(jìn)入XMLConfigBuilder構(gòu)造函數(shù)如下:
public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); }
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); this.localReflectorFactory = new DefaultReflectorFactory(); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
進(jìn)入super()代碼如下:
public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); }
通過(guò)上述代碼可知:this.parsed = false;后面有用,這里先提下。返回原先執(zhí)行處:var5 = this.build(parser.parse());
var5 = this.build(parser.parse());
進(jìn)入parser.parse()這個(gè)方法,代碼如下:
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; } }
由前面設(shè)置了this.parsed = false,可知this.parsed為false,就進(jìn)入else分支,讀者這個(gè)時(shí)候就有疑問(wèn)了,為啥要設(shè)置this.parsed = false呢?
我們通過(guò)else分支可知,又設(shè)置了this.parsed = true;說(shuō)明再下一次再次進(jìn)入parse方法的時(shí)候,this.parsed=true會(huì)直接拋出異常。
這里我們可以總結(jié)下:
為什么XMLConfigBuilder只能被使用一次呢?
答:因?yàn)槲覀兊腃onfiguration是一個(gè)全局的,所以只能被解析一次。
多次解析的話,會(huì)拋出:Each XMLConfigBuilder can only be used once.異常,防止用戶私自調(diào)用parse()方法再去重復(fù)解析,因?yàn)榕渲梦募侨值?,不能多次解析?/strong>
進(jìn)入else分支的下面這個(gè)代碼中:
this.parseConfiguration(this.parser.evalNode("/configuration"));
private void parseConfiguration(XNode root) { try { this.propertiesElement(root.evalNode("properties")); this.typeAliasesElement(root.evalNode("typeAliases")); this.pluginElement(root.evalNode("plugins")); this.objectFactoryElement(root.evalNode("objectFactory")); this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectionFactoryElement(root.evalNode("reflectionFactory")); this.settingsElement(root.evalNode("settings")); this.environmentsElement(root.evalNode("environments")); this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); this.typeHandlerElement(root.evalNode("typeHandlers")); this.mapperElement(root.evalNode("mappers")); } catch (Exception var3) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); } }
我們先看看mybatis_config.xml配置文件的內(nèi)容:
<configuration> <!-- 環(huán)境配置 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!-- 數(shù)據(jù)庫(kù)連接相關(guān)配置 ,這里動(dòng)態(tài)獲取config.properties文件中的內(nèi)容--> <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="root"/> </dataSource> </environment> </environments> <!-- mapping文件路徑配置 --> <mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers> </configuration>
我們先進(jìn)入下面這行代碼:因?yàn)檫@個(gè)environments在我們配置文件中配置了,我們先分析它:
this.environmentsElement(root.evalNode("environments"))
private void environmentsElement(XNode context) throws Exception { if (context != null) { if (this.environment == null) { this.environment = context.getStringAttribute("default"); } Iterator i$ = context.getChildren().iterator(); while(i$.hasNext()) { XNode child = (XNode)i$.next(); String id = child.getStringAttribute("id"); if (this.isSpecifiedEnvironment(id)) { TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource); this.configuration.setEnvironment(environmentBuilder.build()); } } } }
通過(guò)斷點(diǎn)調(diào)試environmentsElement()代碼結(jié)果如下:
我們看下這段代碼:
this.configuration.setEnvironment(environmentBuilder.build());
public void setEnvironment(Environment environment) { this.environment = environment; }
到這里我們就明白了:這里將解析的XML結(jié)點(diǎn)封裝成Environment對(duì)象,再把Environment對(duì)象設(shè)置給Configuration對(duì)象中。也就是解析XML,再把XML轉(zhuǎn)為Configuration實(shí)體類
到這里我們?cè)賮?lái)分析:mappers結(jié)點(diǎn)在配置文件中配置了,我們也來(lái)分析下,下面是mapper.xml配置文件的內(nèi)容,看下是如何轉(zhuǎn)化為實(shí)體對(duì)象保存起來(lái)的:
<mapper namespace="com.mayikt.mapper.UserMapper"> <!-- 在select標(biāo)簽中編寫(xiě)查詢的SQL語(yǔ)句, 設(shè)置select標(biāo)簽的id屬性為getUser,id屬性值必須是唯一的,不能夠重復(fù) 使用parameterType屬性指明查詢時(shí)使用的參數(shù)類型,resultType屬性指明查詢返回的結(jié)果集類型 resultType="com.mayikt.entity.User"就表示將查詢結(jié)果封裝成一個(gè)User類的對(duì)象返回 User類就是users表所對(duì)應(yīng)的實(shí)體類 --> <!-- 根據(jù)id查詢得到一個(gè)user對(duì)象 --> <select id="getUser" parameterType="int" resultType="com.mayikt.entity.UserEntity"> select * from user where id=#{id} </select> </mapper>
this.mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception { if (parent != null) { Iterator i$ = parent.getChildren().iterator(); while(true) { while(i$.hasNext()) { XNode child = (XNode)i$.next(); String resource; if ("package".equals(child.getName())) { //注解方式配置掃包package resource = child.getStringAttribute("name"); this.configuration.addMappers(resource); } else { //resource 方式 resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); XMLMapperBuilder mapperParser; InputStream inputStream; if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); inputStream = Resources.getResourceAsStream(resource); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); inputStream = Resources.getUrlAsStream(url); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments()); mapperParser.parse(); } else { if (resource != null || url != null || mapperClass == null) { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } Class<?> mapperInterface = Resources.classForName(mapperClass); this.configuration.addMapper(mapperInterface); } } } return; } } }
通過(guò)上述代碼可知,配置方式有兩種:一種是注解形式掃包,第二種是resource方式
我們是resource方式的配置,所以進(jìn)入else分支:
由上面斷點(diǎn)分析可知,這里會(huì)讀取mapper.xml配置文件的內(nèi)容,轉(zhuǎn)化為inputStream流,再解析mapper.xml配置文件
XMLMapperBuilder類的作用:解析mapper配置文件得到Configuration對(duì)象,我們看下XMLMapperBuilder怎么去解析mapper配置文件
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments); } private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { super(configuration); this.builderAssistant = new MapperBuilderAssistant(configuration, resource); this.parser = parser; this.sqlFragments = sqlFragments; this.resource = resource; }
最終進(jìn)入:
mapperParser.parse()
public void parse() { if (!this.configuration.isResourceLoaded(this.resource)) { this.configurationElement(this.parser.evalNode("/mapper")); this.configuration.addLoadedResource(this.resource); this.bindMapperForNamespace(); }
進(jìn)入addLoadedResource()方法:
public void addLoadedResource(String resource) { this.loadedResources.add(resource); }
protected final Set<String> loadedResources;
public Configuration() { this.loadedResources = new HashSet(); }
通過(guò)上述代碼可知:loadedResources存放的都是mybatis映射的文件路徑地址【mapper.xml】, 使用HashSet集合存放
存放進(jìn)去之后,斷點(diǎn)如下:
我們進(jìn)入下面這個(gè)方法:
this.bindMapperForNamespace();
private void bindMapperForNamespace() { String namespace = this.builderAssistant.getCurrentNamespace(); //拿到mapper.xml里面配置的namespace,這里是com.mayikt.mapper.UserMapper if (namespace != null) { Class boundType = null; try { boundType = Resources.classForName(namespace); //通過(guò)Java反射機(jī)制幫我去查找,這里得到interface com.mayikt.mapper.UserMapper } catch (ClassNotFoundException var4) { ; } if (boundType != null && !this.configuration.hasMapper(boundType)) {//判斷mapper.xml配置文件是否注冊(cè)過(guò) this.configuration.addLoadedResource("namespace:" + namespace); this.configuration.addMapper(boundType); } } }
先看看addMapper方法:
this.configuration.addMapper(boundType);
public <T> void addMapper(Class<T> type) { this.mapperRegistry.addMapper(type); }
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { //判斷是否是接口類型 if (this.hasMapper(type)) { //再次判斷是否注冊(cè)過(guò),如果注冊(cè)過(guò),則拋出異常 throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { this.knownMappers.put(type, new MapperProxyFactory(type)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { this.knownMappers.remove(type); } } }
this.knownMappers.put(type, new MapperProxyFactory(type));
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
由上述代碼可知:mapperRegistry作用是:存放dao層mapper接口,debug結(jié)果如下:
最后,我們來(lái)看看loadedResources里面的東西:存放的是userMapper的配置文件
再看看mapperRegistery里面的東西:存放的是mapper接口
最后,我們回到開(kāi)始的parse()方法,上述代碼執(zhí)行完this.parseConfiguration(this.parser.evalNode("/configuration"))方法之后,返回configuration對(duì)象
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; } }
到這里,我們就結(jié)束了源碼分析,下面總結(jié)下大體流程:
獲取本地InputStreamReader對(duì)象(mybatis配置文件)
調(diào)用SqlSessionFactoryBuilder
###再使用XMLConfigBuilder解析mybatis配置文件,裝配到Configuration中。
將配置文件中的Mapper添加到Configuration mapperRegistry實(shí)現(xiàn)注冊(cè)。
備注:mapperRegistry存放當(dāng)前所有的mapper接口。
loadedResources里面的東西:存放的是userMapper的配置文件
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。
分享題目:MybatisSqlSessionFactoryBuilder源碼的示例分析
鏈接URL:http://jinyejixie.com/article0/jjgoio.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、標(biāo)簽優(yōu)化、網(wǎng)站內(nèi)鏈、動(dòng)態(tài)網(wǎng)站、微信公眾號(hào)、做網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
營(yíng)銷型網(wǎng)站建設(shè)知識(shí)