0
点赞
收藏
分享

微信扫一扫

Mybatis源码浅析

Mybatis xml解析流程

Xml解析的常见方式:DOM SAX Xpath ,Mybatis使用的时Xpath,因其足够简单。

对应代码:

 String resource="mybatis-config.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

执行流程:
1、 执行SqlSessionFactoryBuilder 中的build

//以下3个方法都是调用下面第8种方法
  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);
  }

  //第8种方法和第4种方法差不多,Reader换成了InputStream
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //创建一个XMLConfigBuilder对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //解析xml文件各个结点返回Configuration对象,传入build对象返回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.
      }
    }
  }

  //最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

2、执行XMLConfigBuilder的构造方法

 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    //构建XPathParser对象  进行xml解析
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }


  //上面6个构造函数最后都合流到这个函数,传入XPathParser
  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //首先调用父类初始化Configuration
    super(new Configuration());
    //错误上下文设置成SQL Mapper Configuration(XML文件配置),以便后面出错了报错用吧
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //将Properties全部设置到Configuration里面去
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

3、new Configuration() (属性都为默认值)

设置configuration的属性参数

设置XMLConfigBuilder的其他参数(document对象也在其中创建 即将xml 转化为Document对象)

此时XMLConfigBuilder构建完成

 public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    //调用createDocument() 构建document ,底层使用SAX解析  将进来的xml文件  转换为document
    this.document = createDocument(new InputSource(inputStream));
  }

4、调用parser.parse()获得Configuration 对象

//解析配置
  public Configuration parse() {  //只解析一次
    //如果已经解析过了,报错
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;  //未解析过  将标志位置为true
//  <?xml version="1.0" encoding="UTF-8" ?>
//  <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
//  "http://mybatis.org/dtd/mybatis-3-config.dtd">
//  <configuration>
//  <environments default="development">
//  <environment id="development">
//  <transactionManager type="JDBC"/>
//  <dataSource type="POOLED">
//  <property name="driver" value="${driver}"/>
//  <property name="url" value="${url}"/>
//  <property name="username" value="${username}"/>
//  <property name="password" value="${password}"/>
//  </dataSource>
//  </environment>
//  </environments>
//  <mappers>
//  <mapper resource="org/mybatis/example/BlogMapper.xml"/>
//  </mappers>
//  </configuration>

    //根节点是configuration   parser Xpath解析器
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  //解析配置
  private void parseConfiguration(XNode root) {
    try {
      //分步骤解析
      //issue #117 read properties first
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.插件
      pluginElement(root.evalNode("plugins"));
      //4.对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.对象包装工厂
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.设置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.环境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

5、解析对应结点下的各个结点内容,放进Configuration

//1.properties
  //<properties resource="org/mybatis/example/config.properties">
  //    <property name="username" value="dev_user"/>
  //    <property name="password" value="F2Fa3!33TYyg"/>
  //</properties>
  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:

      //1.在 properties 元素体内指定的属性首先被读取。
      //2.从类路径下资源或 properties 元素的 url 属性中加载的属性第二被读取,它会覆盖已经存在的完全一样的属性。
      //3.作为方法参数传递的属性最后被读取, 它也会覆盖任一已经存在的完全一样的属性,这些属性可能是从 properties 元素体内和资源/url 属性中加载的。
      //传入方式是调用构造函数时传入,public XMLConfigBuilder(Reader reader, String environment, Properties props)

      //1.XNode.getChildrenAsProperties函数方便得到孩子所有Properties
      Properties defaults = context.getChildrenAsProperties();
      //2.然后查找resource或者url,加入前面的Properties
      String resource = context.getStringAttribute("resource");
      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.");
      }
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //3.Variables也全部加入Properties
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

mapperElement节点解析

//10.映射器
//  10.1使用类路径
//  <mappers>
//    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
//    <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
//    <mapper resource="org/mybatis/builder/PostMapper.xml"/>
//  </mappers>
//
//  10.2使用绝对url路径
//  <mappers>
//    <mapper url="file:///var/mappers/AuthorMapper.xml"/>
//    <mapper url="file:///var/mappers/BlogMapper.xml"/>
//    <mapper url="file:///var/mappers/PostMapper.xml"/>
//  </mappers>
//
//  10.3使用java类名
//  <mappers>
//    <mapper class="org.mybatis.builder.AuthorMapper"/>
//    <mapper class="org.mybatis.builder.BlogMapper"/>
//    <mapper class="org.mybatis.builder.PostMapper"/>
//  </mappers>
//
//  10.4自动扫描包下所有映射器
//  <mappers>
//    <package name="org.mybatis.builder"/>
//  </mappers>
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //10.4自动扫描包下所有映射器
          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) {
            //10.1使用类路径
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //映射器比较复杂,调用XMLMapperBuilder
            //注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //10.2使用绝对url路径
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //映射器比较复杂,调用XMLMapperBuilder
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            //10.3使用java类名
            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.");
          }
        }
      }
    }
  }

//Configuration类下
public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }

  //查找包下所有类   MapperRegistry类下
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }

  //MapperRegistry类下
  public void addMappers(String packageName, Class<?> superType) {
    //查找包下所有是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);    //将set里面的xxxMapper使用动态代理创建出来
    }
  }

  //看一下如何添加一个映射   
  public <T> void addMapper(Class<T> type) {
    //mapper必须是接口!才会添加
    if (type.isInterface()) {
      if (hasMapper(type)) {
        //如果重复添加了,报错
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //创建mapper的动态代理,使用JDK的动态代理实现
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();  //解析成对应的mapper.xml 文件
        loadCompleted = true;
      } finally {
        //如果加载过程中出现异常需要再将这个mapper从mybatis中删除
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

6、调用build得到会话工厂DefaultSqlSessionFactory

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //创建一个XMLConfigBuilder对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //解析xml文件各个结点返回Configuration对象,传入build对象返回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.
      }
    }
  }

  //最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

流程图:



SqlSession创建流程

SqlSessionFactoryBuilder

    使用**建造者模式** 

SqlSessionFactory

使用工厂模式,单例模式

SqlSession


Mybatis核心对象

Mybatis的核心对象及其作用

存储类对象

概念:在java中(jvm)对Mybatis相关的配置信息进行封装

Configrution

mybatis-config.xml ----------------->Configuration

public class Configuration {

  //环境
  protected Environment environment;

  //---------以下都是<settings>节点-------
  protected boolean safeRowBoundsEnabled = false;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase = false;   //数据库表字段驼峰
  protected boolean aggressiveLazyLoading = true;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys = false;          //自增主键
  protected boolean useColumnLabel = true;
  //默认启用缓存
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls = false;

  protected String logPrefix;
  protected Class <? extends Log> logImpl;
  //缓存作用域  本地缓存使用范围为SESSION  对相同SqlSession的不同调用将不会共享数据
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  protected Integer defaultStatementTimeout;
  //默认为简单执行器   实际上因为cacheEnabled = true 缓存开启而使用了CachingExecutor
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  //---------以上都是<settings>节点-------

  protected Properties variables = new Properties();
  //对象工厂和对象包装器工厂
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  //映射注册机
  protected MapperRegistry mapperRegistry = new MapperRegistry(this);

  //默认禁用延迟加载
  protected boolean lazyLoadingEnabled = false;
  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</a> (google code)
   */
  protected Class<?> configurationFactory;
  //拦截器链  在Executor创建的时候赋值在
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  //类型处理器注册机
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  //类型别名注册机
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  //映射的语句,存在Map里   对应这各个xxxDAOMapper.xml文件,将各个Mapper信息进行了汇总
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  //缓存,存在Map里
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  //结果映射,存在Map里    所有的ResultMap 全都放在resultMaps中
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

  //路径  对应xml中的mapper
  protected final Set<String> loadedResources = new HashSet<String>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

  //不完整的SQL语句
  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

  /*
   * A map holds cache-ref relationship. The key is the namespace that
   * references a cache bound to another namespace and the value is the
   * namespace which the actual cache is bound to.
   */
  protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  public Configuration() {
    //注册更多的类型别名,至于为何不直接在TypeAliasRegistry里注册,还需进一步研究
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); //事务处理器
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);  //这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); // 缓存策略
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);  //raw 图像格式

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);  //日志
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); //动态代理
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);   //默认使用xml
    languageRegistry.register(RawLanguageDriver.class);
  }
//创建元对象
  public MetaObject newMetaObject(Object object) {
    return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
  }

  //创建参数处理器
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    //创建ParameterHandler
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    //插件在这里插入
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  //创建结果集处理器
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    //创建DefaultResultSetHandler(稍老一点的版本3.1是创建NestedResultSetHandler或者FastResultSetHandler)
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    //插件在这里插入
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  //创建语句处理器
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //创建路由选择语句处理器
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //插件在这里插入
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  //产生执行器
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    //这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //此处调用插件,通过插件可以改变Executor行为
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

   ************************** get()和set()方法省略**************
}

Configrution小结:

  1. 设置配置文件中的对应属性

  2. 设置默认执行器 (简单执行器)

    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    
  3. 创建拦截器链 InterceptorChain

  4. 创建类型处理器注册机 TypeHandlerRegistry

  5. 创建类型别名注册机 TypeAliasRegistry

  6. 将各个mapper文件中的信息进行汇总存放

    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
    
  7. 为系统内部使用的类定义别名注册(在构造函数中设置)

  8. 创建元对象

  9. 创建参数处理器 parameterHandler

  10. 创建结果集处理器resultSetHandler

  11. 创建语句处理器statementHandler

  12. 创建执行器executor

MappedStatement

xxxDaoMapper.xml -----------------> MappedStatement

MappedStatement对象

对应的就是Mapper文件中的一个一个的配置

 //映射的语句,存在Map里   对应这各个xxxDAOMapper.xml文件,将各个Mapper信息进行了汇总
  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
<select id="getRole" parameterType="long" resultMap="roleMap" >
        select
        id,role_name as roleName,note from role where id=#{id}
    </select>

mapper中的一条标签语句对应着一个MappedStatement
MappedStatment 中使用BoundSql来封装sql语句

public final class MappedStatement {

  //对应的属性
  private String resource;
  private Configuration configuration;
  private String id;    //sql的id namespace.id
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;

  //SQL源码
  private SqlSource sqlSource;   //解析出来的sql   对应一条sql语句
  private Cache cache;
  private ParameterMap parameterMap;   //以废弃
  private List<ResultMap> resultMaps;   //对应的ResultMap
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;  //数据库ID,用来区分不同环境
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

  MappedStatement() {
    // constructor disabled
  }

  //静态内部类,建造者模式
  public static class Builder {
    private MappedStatement mappedStatement = new MappedStatement();

    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
      mappedStatement.configuration = configuration;
      mappedStatement.id = id;
      mappedStatement.sqlSource = sqlSource;
      mappedStatement.statementType = StatementType.PREPARED; //默认使用PREPARED
      mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
      mappedStatement.resultMaps = new ArrayList<ResultMap>();
      mappedStatement.timeout = configuration.getDefaultStatementTimeout();
      mappedStatement.sqlCommandType = sqlCommandType;   //select ||delete|| update ||insert || UNKNOWN
      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
      String logId = id;
      if (configuration.getLogPrefix() != null) {
        logId = configuration.getLogPrefix() + id;
      }
      mappedStatement.statementLog = LogFactory.getLog(logId);
      mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
    }

    public Builder resource(String resource) {
      mappedStatement.resource = resource;
      return this;
    }

    public String id() {
      return mappedStatement.id;
    }

    public Builder parameterMap(ParameterMap parameterMap) {
      mappedStatement.parameterMap = parameterMap;
      return this;
    }

    public Builder resultMaps(List<ResultMap> resultMaps) {
      mappedStatement.resultMaps = resultMaps;
      for (ResultMap resultMap : resultMaps) {
        mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
      }
      return this;
    }

    public Builder fetchSize(Integer fetchSize) {
      mappedStatement.fetchSize = fetchSize;
      return this;
    }

    public Builder timeout(Integer timeout) {
      mappedStatement.timeout = timeout;
      return this;
    }

    public Builder statementType(StatementType statementType) {
      mappedStatement.statementType = statementType;
      return this;
    }

    public Builder resultSetType(ResultSetType resultSetType) {
      mappedStatement.resultSetType = resultSetType;
      return this;
    }

    public Builder cache(Cache cache) {
      mappedStatement.cache = cache;
      return this;
    }

    public Builder flushCacheRequired(boolean flushCacheRequired) {
      mappedStatement.flushCacheRequired = flushCacheRequired;
      return this;
    }

    public Builder useCache(boolean useCache) {
      mappedStatement.useCache = useCache;
      return this;
    }

    public Builder resultOrdered(boolean resultOrdered) {
      mappedStatement.resultOrdered = resultOrdered;
      return this;
    }

    public Builder keyGenerator(KeyGenerator keyGenerator) {
      mappedStatement.keyGenerator = keyGenerator;
      return this;
    }

    public Builder keyProperty(String keyProperty) {
      mappedStatement.keyProperties = delimitedStringtoArray(keyProperty);
      return this;
    }

    public Builder keyColumn(String keyColumn) {
      mappedStatement.keyColumns = delimitedStringtoArray(keyColumn);
      return this;
    }

    public Builder databaseId(String databaseId) {
      mappedStatement.databaseId = databaseId;
      return this;
    }

    public Builder lang(LanguageDriver driver) {
      mappedStatement.lang = driver;
      return this;
    }

    public Builder resulSets(String resultSet) {
      mappedStatement.resultSets = delimitedStringtoArray(resultSet);
      return this;
    }

    public MappedStatement build() {
      assert mappedStatement.configuration != null;
      assert mappedStatement.id != null;
      assert mappedStatement.sqlSource != null;
      assert mappedStatement.lang != null;
      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
      return mappedStatement;
    }
  }
***************************get()和set()方法省略********************************************
}

MappedStatement 小结:

  1. 使用建造者模式构建
  2. 内部有configuration便于操作和使用
  3. 含有xml某个结点中的完整信息

操作类对象

Executor:Mybatis的执行器

主要功能:

  1. 增删改 update
  2. 查 query
  3. 事务 提交 回滚
  4. 缓存


Executor小结 :

  1. 调用StatementHandler来进行数据库相关的改、查操作
  2. 维护缓存

StatementHandler

ParameterHandler

ResultSetHandler

TypeHandler


Mybatis工作流程源码分析

将核心对象与SqlSession建立联系

对应代码:

RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
//sqlSession.selectList("com.wxx.mapper.RoleMapper.getRole");  上面语句是下面语句的封装

执行流程:

1、DefaultSqlSession(sqlSession默认实现方式)调用getMapper()方法 将xxxMapper.class 和 当前进行会话的sqlsession传入进去

//DefaultSqlSession类
  public <T> T getMapper(Class<T> type) {
    //最后会去调用MapperRegistry.getMapper
    return configuration.<T>getMapper(type, this);
  }

2、调用mapper注册机的getMapper()方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  1. 程序中的mapper都放在一个hashmap中,先查找这个hashmap中有没有key为当前类的代理工厂类。如果有则为当前mapper创建一个代理对象,并返回;如果没有则进行异常处理。
 @SuppressWarnings("unchecked")
  //返回代理类
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

代理实现原理分析:

对应代码:

mapperProxyFactory.newInstance(sqlSession)
  1. 动态代理创建具体实现类
  2. 具体实现类内部调用对应的sqlsession的方法
mybatis源码中的这些核心对象,在sqlsession调用对应功能时候建立联系
例如:
    SqlSession.insert()
        DefaultSqlSession
            Exctutor
                StatementHandler

MyBatis 完成代理创建核心类型 ---> DAO接口的实现类
执行顺序如下:

MapperProxy  implements InvocationHandler
                invoke(类加载器,方法名,参数)
                        sqlSession.insert
                                            update
                                            delete
                                            selectOne
                                            selectList
MapperProxyFactory
            Proxy.newProxyInstrance()
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {   //生成xxxDAO的实现类
    //用JDK自带的动态代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {     //传进sqlSession和对应mapperInterface 生成代理对象  methodCache是方法缓存
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
    //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    //这里优化了,去缓存中找MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行
    return mapperMethod.execute(sqlSession, args);
  }

  //去缓存中找MapperMethod
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //找不到才去new
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

invoke()中的method.invoke(this, args); 底层对应的相应的SqlSession.insertupdatedeleteselectOneselectList方法。

public class MapperMethod {

  private final SqlCommand command;  //对应的标签 是什么类型   才能具体的知道调用对应crud所对应的方法
  private final MethodSignature method;   //返回值  和 参数
    //需求Configuration对象
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, method);
  }

  //执行
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
    if (SqlCommandType.INSERT == command.getType()) {   //insert
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) { //update
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) { //delete
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) { //select
      if (method.returnsVoid() && method.hasResultHandler()) {
        //如果有结果处理器
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        //如果结果有多条记录
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        //如果结果是map
        result = executeForMap(sqlSession, args);
      } else {
        //否则就是一条记录   selectOne
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
    其他部分省略
}
//SQL命令,静态内部类
  public static class SqlCommand {

    private final String name;  //name.id
    private final SqlCommandType type; //select || update || insert || delete

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      String statementName = mapperInterface.getName() + "." + method.getName();
      MappedStatement ms = null;
      if (configuration.hasStatement(statementName)) {
        ms = configuration.getMappedStatement(statementName);
      } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
        //如果不是这个mapper接口的方法,再去查父类
        String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
        if (configuration.hasStatement(parentStatementName)) {
          ms = configuration.getMappedStatement(parentStatementName);
        }
      }
      if (ms == null) {
        throw new BindingException("Invalid bound statement (not found): " + statementName);
      }
      name = ms.getId();
      type = ms.getSqlCommandType();   //select delete update insert
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
    }

    public String getName() {
      return name;
    }

    public SqlCommandType getType() {
      return type;
    }
  }
//方法签名,静态内部类
  public static class MethodSignature { //对应sql语句中的参数

    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final SortedMap<Integer, String> params;
    private final boolean hasNamedParameters;

    public MethodSignature(Configuration configuration, Method method) {
      this.returnType = method.getReturnType();
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
      this.mapKey = getMapKey(method);
      this.returnsMap = (this.mapKey != null);
      this.hasNamedParameters = hasNamedParams(method);
      //以下重复循环2遍调用getUniqueParamIndex,是不是降低效率了
      //记下RowBounds是第几个参数
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      //记下ResultHandler是第几个参数
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
    }

StatementHandler执行流程:
时序图(图片来自于http://www.coderead.cn/

核心对象总结

configuration

默认初始化时,默认构造函数为各种类型起别名:

                1. 事务处理器
                2. 数据源类型 
                3. 缓存策略
                4. 图像格式
                5. 日志类型
                6. 动态代理类型
                7. 默认驱动类(xml方式)

其他配置:

  1. 默认使用简单执行器ExecutorType.SIMPLE
  2. 缓存作用域在session范围内LocalCacheScope.SESSION
  3. 创建拦截器链
  4. 创建类型处理器注册机
  5. 创建类型别名注册机

mappedStatement

用来表示xml中的一个sql,例如:

 <select id="findRole" parameterType="long" resultMap="roleMap">
        select
        id,role_name,note from role where role_name like CONCAT('%',#{roleName
        javaType=string,
        jdbcType=VARCHAR,typeHandler=com.wxx.handler.MyStringHandler},'%')
    </select>
用来描述<select|update|insert|delete>或者@Select、@Update等注解配置的SQL信息。

sqlsession

使用门面模式,只设定api,具体参数让executor来执行

executor

简单执行器(SimpleExecutor),可实现sqlsession中的全部功能 一次一次的执行

重用执行器(ReuseExecutor),先查缓存中时候有相应的sql语句,若有,则不再进行sql的预编译

批处理执行器(BatchExecutor),一次插入一批数据,只对操作有效,对查询操作和简单执行器一样

四大对象

StatementHandler

ParameterHandler 用来处理StatementHandler中的参数

ResultSetHandler 用来处理StatementHandler 中的结果集

TypeHandler 用来帮助ParameterHandlerResultSetHandler处理javaType和数据库类型的相互转换


缓存

缓存逻辑图

一级缓存

命中场景(会话级别缓存)

运行时参数相关:

  1. sql语句 和参数相同
  2. statementid需要相同
  3. sqlsession也必须相同
  4. RowBounds返回行范围必须相同

操作与配置相关:

①②③ 都会调用clearLocalCache()来对缓存进行清空

  1. 未手动清空 (提交 回滚)
  2. 未调用flushCache == true 的查询
  3. 未执行update操作 update操作会清空全部缓存
  4. 缓存作用域 不是STATEMENT

spring整合mybatis 以及缓存失效原因


使用拦截器(动态代理)来控制方法的调用,最终达到实现事务的效果

二级缓存

作用范围是整个应用,而且可以跨线程调用



使用流程:



使用场景

运行时参数相关:

  1. 会话提交之后
  2. sql语句 和参数相同
  3. statement的id需要相同
  4. RowBounds 返回行范围必须相同

为何提交之后才能命中缓存

解决方案:



为每个会话创建一个事务缓存管理器(transactionCaches),每次访问一个新的xxxMapper都会创建一个暂存区,对应着相应的缓存区


Mybatis中使用的设计模式

单例模式

configuration 使用单例 保证整个程序运行期间都可使用,且不重复创建,放置浪费资源

构造器模式

        在Mybatis的初始化的主要工作是加载并解析mybatis-config.xml的配置文件、映射配置文件以及相关的注解信息。因为使用了建造者模式,BashBuilder抽象类即为建造者接口的角色

public abstract class BaseBuilder {
    //需要配置,类型别名注册,类型处理器注册3个东西
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
   ………………
}

适配器模式

(Excutor 等核心对象,使用缺省适配器模式) 使用extends关键字来继承,只用实现想实现的功能

interface Test{
   void eat();
   void drink();
   void walk();
}


abstract class abstractClass implements Test{
   public void eat() {
       // TODO Auto-generated method stub
       
   }
   @Override
   public void drink() {
       // TODO Auto-generated method stub
       
   }

   @Override
   public void walk() {
       // TODO Auto-generated method stub
       
   }
}

class eatClass extends abstractClass{  
       @Override
       public void eat() {
           // TODO Auto-generated method stub
           super.eat();
       }
   }
   class drinkClass extends abstractClass{
       @Override
       public void eat() {
           // TODO Auto-generated method stub
           super.eat();
       }
   
   }

代理模式(动态代理)

 RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);  实现类使用动态字节码技术来创建,在jvm中运行时创建
    等价于
Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{RoleMapper.class},
                                                            new MyMapperProxy(sqlSession,RoleMapper.class));

工厂模式

使用mapper工厂来创建mapper

MapperProxyFactory  和  MapperProxy
    
protected T newInstance(MapperProxy<T> mapperProxy) {
    //用JDK自带的动态代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

门面模式

sqlsession使用门面模式,只定义api 具体的使用让Executor来进行实现

装饰器模式

在不改变原有类结构和继承的情况下,通过包装原对象去扩展一个新功能

Caching Executor 实现二级缓存,而其他的操作,如同获取连接等操作BaseExecutor已经做了,直接使用BaseExecutor

责任链模式

二级缓存中的责任链模式


举报

相关推荐

0 条评论