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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Mybatis 源码解析 -- 基于配置的源码解析(二)

發布時間:2023/12/31 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis 源码解析 -- 基于配置的源码解析(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么80%的碼農都做不了架構師?>>> ??

mapper解析

接著上篇的配置,本篇主要講解mappers標簽

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><environments default="dev"><environment id="dev"><transactionManager type="JDBC" /><dataSource type="com.xiaoye.clearworld.datasource.CustomDruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/zlgedu" /><property name="username" value="root" /><property name="password" value="root" /></dataSource></environment><environment id="uat"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/zlgedu" /><property name="username" value="root" /><property name="password" value="root" /></dataSource></environment></environments><mappers><mapper class="com.xiaoye.clearworld.persistence.WebsiteZlgeduCompanyMapper"/><!-- 更多………… --></mappers> </configuration>

該標簽的主要功能:標記Mybatis數據庫接口或Mybatis映射文件,配置方式有三:

<package name=""/> 數據庫接口所在父包路徑 <mapper class=""/> 數據庫接口類路徑 <mapper resource=""/> 數據庫映射文件路徑 <mapper url=""/> 數據庫映射文件資源路徑

源碼解析:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode root)

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"));// mappers 標簽解析mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}

進入到方法

private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) { // 解析 mappers 的子節點if ("package".equals(child.getName())) { // 配置接口掃描包 package 節點(掃描包)String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage); // 如果配置了package節點,mybatis默認會將該包下所有的接口類加載到mybatis中} else { // 單獨配置String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) { // resource xml 文件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) {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) {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.");}}}}}

得到mappers節點后,解析其子節點。

如果子節點中存在 package 節點,則解析方式如下:

  • 獲取包路徑 name屬性值
  • 解析:configuration.addMappers(mapperPackage); public void addMappers(String packageName) {mapperRegistry.addMappers(packageName);}/*** @since 3.2.2 也就是mybatis3.2.2版本才增加了這一功能*/public void addMappers(String packageName) {// 添加mappers,并指定要添加的類或子類的類型addMappers(packageName, Object.class);}/*** @since 3.2.2*/public void addMappers(String packageName, Class<?> superType) {// 工具類:用來判斷是否是指定類或其子類,并存儲結果ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();// 查找類resolverUtil.find(new ResolverUtil.IsA(superType), packageName);Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();for (Class<?> mapperClass : mapperSet) {addMapper(mapperClass);}}public ResolverUtil<T> find(Test test, String packageName) {// 獲取包路徑String path = getPackagePath(packageName);try {// 獲取包路徑下的所有文件路徑(格式:com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.class)List<String> children = VFS.getInstance().list(path);// 遍歷包路徑下的所有文件路徑for (String child : children) {// 過濾 class 文件if (child.endsWith(".class")) {// 判斷是否是指定類或指定類的子類addIfMatching(test, child);}}} catch (IOException ioe) {log.error("Could not read package: " + packageName, ioe);}return this;}// 所以包路徑的格式類似:com.xxx.xxx.xxxprotected String getPackagePath(String packageName) {return packageName == null ? null : packageName.replace('.', '/');}// 判斷是否是指定類或指定類的子類protected void addIfMatching(Test test, String fqn) {try {String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');ClassLoader loader = getClassLoader();if (log.isDebugEnabled()) {log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");}// 得到類的Class類型Class<?> type = loader.loadClass(externalName);// 判斷,符合存儲到set集合中if (test.matches(type)) {matches.add((Class<T>) type);}} catch (Throwable t) {log.warn("Could not examine class '" + fqn + "'" + " due to a " +t.getClass().getName() + " with message: " + t.getMessage());}}// 添加public <T> void addMapper(Class<T> type) {if (type.isInterface()) { // 首先是接口if (hasMapper(type)) { // 判斷是否已添加過throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// 緩存接口及其代理knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}

    分析上面的代碼:a、根據包名稱轉換得到包路徑;b、根據包路徑獲取該包下所有的文件;c、遍歷文件,取出class結尾的文件;d、判斷是否是指定類或指定類的子類;e:添加并解析

  • 具體的解析(非package節點的解析直接進入這):

    public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}

    分析:1、首先是接口;2、之前未添加過haddMapper;3、存儲:knownMappers.put(type, new MapperProxyFactory<T>(type));4、映射文件解析

  • 映射文件解析

    MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {String resource = type.getName().replace('.', '/') + ".java (best guess)";this.assistant = new MapperBuilderAssistant(configuration, resource);this.configuration = configuration;this.type = type;sqlAnnotationTypes.add(Select.class);sqlAnnotationTypes.add(Insert.class);sqlAnnotationTypes.add(Update.class);sqlAnnotationTypes.add(Delete.class);sqlProviderAnnotationTypes.add(SelectProvider.class);sqlProviderAnnotationTypes.add(InsertProvider.class);sqlProviderAnnotationTypes.add(UpdateProvider.class);sqlProviderAnnotationTypes.add(DeleteProvider.class);} public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) { // 判斷是否解析過loadXmlResource(); // 解析映射文件configuration.addLoadedResource(resource); // 添加已解析assistant.setCurrentNamespace(type.getName()); // 校驗映射文件命名空間parseCache(); // 緩存解析parseCacheRef(); // TODOMethod[] methods = type.getMethods(); // 獲取接口的所有方法for (Method method : methods) { // 遍歷try {// issue #237if (!method.isBridge()) { // parseStatement(method); // }} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods(); //}

    映射文件解析:loadXmlResource();

  • private void loadXmlResource() {// Spring may not know the real resource name so we check a flag// to prevent loading again a resource twice// this flag is set at XMLMapperBuilder#bindMapperForNamespaceif (!configuration.isResourceLoaded("namespace:" + type.getName())) {// 映射文件默認和數據庫接口文件在同一包下String xmlResource = type.getName().replace('.', '/') + ".xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);} catch (IOException e) {// ignore, resource is not required}if (inputStream != null) {XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());xmlParser.parse();}}} XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); xmlParser.parse();

    ?

  • 轉載于:https://my.oschina.net/asddsa/blog/1573374

    總結

    以上是生活随笔為你收集整理的Mybatis 源码解析 -- 基于配置的源码解析(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

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