• mybatis专题(四)-----mybatis源码学习


    mybatis核心流程三大阶段

    核心流程

    Mybatis的初始化

    建造者模式

    建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模 式属于创建型模式,它提供了一种创建对象的最佳方式。

    Builder:给出一个抽象接口,以规范产品对 象的各个组成成分的建造。这个接口规定要实 现复杂对象的哪些部分的创建,并不涉及具体 的对象部件的创建; 

    ConcreteBuilder:实现Builder接口,针对 不同的商业逻辑,具体化复杂对象的各部分的 创建。 在建造过程完成后,提供产品的实例;

    Director:调用具体建造者来创建复杂对象的 各个部分,在指导者中不涉及具体产品的信息, 只负责保证对象各部分完整创建或按某种顺序 创建;

    Product:要创建的复杂对象 

    示例:

    1、Builder接口

    public interface RedPacketBuilder {
        
        RedPacketBuilder setPublisherName(String publishName);
    
        RedPacketBuilder setAcceptName(String acceptName);
    
        RedPacketBuilder setPacketAmount(BigDecimal packetAmount);
    
        RedPacketBuilder setPacketType(int packetType);
    
        RedPacketBuilder setPulishPacketTime(Date pushlishPacketTime);
    
        RedPacketBuilder setOpenPacketTime(Date openPacketTime);
    
        RedPacket build();
    }

    2、Builder实现类

    public class RedPacketBuilderImpl implements RedPacketBuilder {
        
        private String publisherName;
    
        private String acceptName;
    
        private BigDecimal packetAmount;
        
        private int packetType;
    
        private Date pulishPacketTime;
    
        private Date openPacketTime;
    
        public static RedPacketBuilderImpl getBulider(){
            return new RedPacketBuilderImpl();
        }
    
    
        @Override
        public RedPacketBuilder setPublisherName(String publishName) {
            this.publisherName = publishName;
            return this;
        }
    
        @Override
        public RedPacketBuilder setAcceptName(String acceptName) {
           this.acceptName = acceptName;
           return this;
        }
    
        @Override
        public RedPacketBuilder setPacketAmount(BigDecimal packetAmount) {
           this.packetAmount = packetAmount;
           return this;
        }
    
        @Override
        public RedPacketBuilder setPacketType(int packetType) {
            this.packetType = packetType;
            return this;
        }
    
        @Override
        public RedPacketBuilder setPulishPacketTime(Date pushlishPacketTime) {
           this.pulishPacketTime = pushlishPacketTime;
            return this;
        }
    
        @Override
        public RedPacketBuilder setOpenPacketTime(Date openPacketTime) {
          this.openPacketTime = openPacketTime;
            return this;
        }
    
    
        public RedPacket build() {
            return new RedPacket(publisherName,acceptName,packetAmount,packetType,pulishPacketTime,openPacketTime);
        }
    }

    3、Product类

    public class RedPacket {
        
        private String publisherName; //发包人
    
        private String acceptName; //手包人
    
        private BigDecimal packetAmount; //红包金额
    
        private int packetType; //红包类型
    
        private Date pulishPacketTime; //发包时间
    
        private Date openPacketTime; //抢包时间
    
        public RedPacket(String publisherName, String acceptName, BigDecimal packetAmount, int packetType, Date pulishPacketTime, Date openPacketTime) {
            this.publisherName = publisherName;
            this.acceptName = acceptName;
            this.packetAmount = packetAmount;
            this.packetType = packetType;
            this.pulishPacketTime = pulishPacketTime;
            this.openPacketTime = openPacketTime;
        }
    
        public String getPublisherName() {
            return publisherName;
        }
    
        public void setPublisherName(String publisherName) {
            this.publisherName = publisherName;
        }
    
        public String getAcceptName() {
            return acceptName;
        }
    
        public void setAcceptName(String acceptName) {
            this.acceptName = acceptName;
        }
    
        public BigDecimal getPacketAmount() {
            return packetAmount;
        }
    
        public void setPacketAmount(BigDecimal packetAmount) {
            this.packetAmount = packetAmount;
        }
    
        public int getPacketType() {
            return packetType;
        }
    
        public void setPacketType(int packetType) {
            this.packetType = packetType;
        }
    
        public Date getPulishPacketTime() {
            return pulishPacketTime;
        }
    
        public void setPulishPacketTime(Date pulishPacketTime) {
            this.pulishPacketTime = pulishPacketTime;
        }
    
        public Date getOpenPacketTime() {
            return openPacketTime;
        }
    
        public void setOpenPacketTime(Date openPacketTime) {
            this.openPacketTime = openPacketTime;
        }
    
        @Override
        public String toString() {
            return "RedPacket [publisherName=" + publisherName + ", acceptName="
                    + acceptName + ", packetAmount=" + packetAmount
                    + ", packetType=" + packetType + ", pulishPacketTime="
                    + pulishPacketTime + ", openPacketTime=" + openPacketTime + "]";
        }
    }

    4、Director

    public class Director {
        public static void main(String[] args) {
            RedPacket redPacket = RedPacketBuilderImpl.getBulider().setPublisherName("lison")
                                                                   .setAcceptName("vip群")
                                                                   .setPacketAmount(new BigDecimal("888"))
                                                                   .setPacketType(1)
                                                                   .setOpenPacketTime(new Date())
                                                                   .setPulishPacketTime(new Date()).build();
    
            System.out.println(redPacket);
        }
    }

    建造者模式 使用场景

    1、需要生成的对象具有复杂的内部结构,实例化对象时要屏蔽掉对象内部的细节,让上层代码与复杂对象的实例化过 程解耦,可以使用建造者模式;简而言之,如果“遇到多个构造器参数时要考虑用构建器”;

    2、一个对象的实例化是依赖各个组件的产生以及装配顺序,关注的是一步一步地组装出目标对象,可以使用建造器模式;

    XMLConfigBuilder: 主要负责解析mybatis-config.xml;

    XMLMapperBuilder: 主要负责解析映射配置文件;

    XMLStatementBuilder: 主要负责解析映射配置文件中的SQL节点;

    与工厂模式区别

    对象复杂度

    1、建造者建造的对象更加复杂,是一个复合产品,它由各个部件复合而成,部件不同产品对象不同,生成的产品 粒度细;

    2、在工厂方法模式里,我们关注的是一个产品整体,无须关心产品的各部分是如何创建出来的;

    客户端参与程度

    1、建造者模式,导演对象参与了产品的创建,决定了产品的类型和内容,参与度高;适合实例化对象时属性变化 频繁的场景;

    2、工厂模式,客户端对产品的创建过程参与度低,对象实例化时属性值相对比较固定;

    Mybatis的初始化

     映射器的关键类

     Configuration : Mybatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象 中, Configuration是单例的,生命周期是应用级的;

     MapperRegistry:mapper接口动态代理工厂类的注册中心。在MyBatis中,通过mapperProxy实现 InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象;

     ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等 子元素;

     MappedStatement:用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包 含了这些节点的很多重要属性;

     SqlSource:mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语 句最终仅仅包含?占位符,可以直接提交给数据库执行;

    图解

    1、configuration类图解

     

    2、ResultMap图解

     3、mappedStatment图解

    源码分析

    1、configuration

    public class Configuration {
    
      protected Environment environment;
    
      /* 是否启用行内嵌套语句**/
      protected boolean safeRowBoundsEnabled;
      protected boolean safeResultHandlerEnabled = true;
      /* 是否启用数据组A_column自动映射到Java类中的驼峰命名的属性**/
      protected boolean mapUnderscoreToCamelCase;
      
      /*当对象使用延迟加载时 属性的加载取决于能被引用到的那些延迟属性,否则,按需加载(需要的是时候才去加载)**/
      protected boolean aggressiveLazyLoading;
      
      /*是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true **/
      protected boolean multipleResultSetsEnabled = true;
      
      /*-允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false**/
      protected boolean useGeneratedKeys;
      
      /* 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。**/
      protected boolean useColumnLabel = true;
      
      /*配置全局性的cache开关,默认为true**/
      protected boolean cacheEnabled = true;
      protected boolean callSettersOnNulls;
      protected boolean useActualParamName = true;
      protected boolean returnInstanceForEmptyRow;
    
      /* 日志打印所有的前缀 **/
      protected String logPrefix;
      
      /* 指定 MyBatis 所用日志的具体实现,未指定时将自动查找**/
      protected Class <? extends Log> logImpl;
      protected Class <? extends VFS> vfsImpl;
      /* 设置本地缓存范围,session:就会有数据的共享,statement:语句范围,这样不会有数据的共享**/
      protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
      /* 设置但JDBC类型为空时,某些驱动程序 要指定值**/
      protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
      
      /* 设置触发延迟加载的方法**/
      protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
      
      /* 设置驱动等待数据响应超时数**/
      protected Integer defaultStatementTimeout;
      
      /* 设置驱动返回结果数的大小**/
      protected Integer defaultFetchSize;
      
      /* 执行类型,有simple、resue及batch**/
      protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
      
      /*指定 MyBatis 应如何自动映射列到字段或属性*/
      protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
      protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    
      protected Properties variables = new Properties();
      
      protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
      
      /*MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO*/
      protected ObjectFactory objectFactory = new DefaultObjectFactory();
      protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    
      /*延迟加载的全局开关*/
      protected boolean lazyLoadingEnabled = false;
      
      /*指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具*/
      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 (google code)</a>
       */
      protected Class<?> configurationFactory;
      
      /*插件集合*/
      protected final InterceptorChain interceptorChain = new InterceptorChain();
      
      /*TypeHandler注册中心*/
      protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
      
      /*TypeAlias注册中心*/
      protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
      protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
      //-------------------------------------------------------------
    
      /*mapper接口的动态代理注册中心*/
      protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    
      /*mapper文件中增删改查操作的注册中心*/
      protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
      
      /*mapper文件中配置cache节点的 二级缓存*/
      protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
      
      /*mapper文件中配置的所有resultMap对象  key为命名空间+ID*/
      protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
      protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
      
      /*mapper文件中配置KeyGenerator的insert和update节点,key为命名空间+ID*/
      protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
    
      /*加载到的所有*mapper.xml文件*/
      protected final Set<String> loadedResources = new HashSet<>();
      
      /*mapper文件中配置的sql元素,key为命名空间+ID*/
      protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
    
      protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
      protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
      protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
      protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
    
      /*
       * 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<>();
    
      public Configuration(Environment environment) {
        this();
        this.environment = environment;
      }
    
      public Configuration() {
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    
        typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        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);
    
        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);
        languageRegistry.register(RawLanguageDriver.class);
      }
    }

    2、 MapperRegistry

    /**
     * 
     * mapper接口和对应的代理对象工厂的注册中心
     * @author Clinton Begin
     * @author Eduardo Macarron
     * @author Lasse Voss
     */
    public class MapperRegistry {
    
      private final Configuration config;//config对象,mybatis全局唯一的
      //记录了mapper接口与对应MapperProxyFactory之间的关系
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    
      public MapperRegistry(Configuration config) {
        this.config = config;
      }
      
      //将mapper接口的工厂类添加到mapper注册中心
      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 {
            //实例化Mapper接口的代理工程类,并将信息添加至knownMappers
            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.
            //解析接口上的注解信息,并添加至configuration对象
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
      }
    }

    3、ResultMap、ResultMapping

    public class ResultMap {
      private Configuration configuration;//configuration对象
    
      private String id;//resultMap的id属性
      private Class<?> type;//resultMap的type属性
      private List<ResultMapping> resultMappings;//除discriminator节点之外的映射关系
      private List<ResultMapping> idResultMappings;//记录ID或者<constructor>中idArg的映射关系
      private List<ResultMapping> constructorResultMappings;////记录<constructor>标志的映射关系
      private List<ResultMapping> propertyResultMappings;//记录非<constructor>标志的映射关系
      private Set<String> mappedColumns;//记录所有有映射关系的columns字段
      private Set<String> mappedProperties;//记录所有有映射关系的property字段
      private Discriminator discriminator;//鉴别器,对应discriminator节点
      private boolean hasNestedResultMaps;//是否有嵌套结果映射
      private boolean hasNestedQueries;////是否有嵌套查询
      private Boolean autoMapping;//是否开启了自动映射
    
      private ResultMap() {
      }
    }
    public class ResultMapping {
    
      private Configuration configuration;//引用的configuration对象
      private String property;//对应节点的property属性
      private String column;//对应节点的column属性
      private Class<?> javaType;//对应节点的javaType属性
      private JdbcType jdbcType;//对应节点的jdbcType属性
      private TypeHandler<?> typeHandler;//对应节点的typeHandler属性
      private String nestedResultMapId;////对应节点的resultMap属性,嵌套结果时使用
      private String nestedQueryId;////对应节点的select属性,嵌套查询时使用
      private Set<String> notNullColumns;//对应节点的notNullColumn属性
      private String columnPrefix;//对应节点的columnPrefix属性
      private List<ResultFlag> flags;//标志,id 或者 constructor
      private List<ResultMapping> composites;
      private String resultSet;//对应节点的resultSet属性
      private String foreignColumn;//对应节点的foreignColumn属性
      private boolean lazy;//对应节点的fetchType属性,是否延迟加载
    
      ResultMapping() {
      }
    }

    4、MappedStatement

    public final class MappedStatement {
    
      private String resource;//节点的完整的id属性,包括命名空间
      private Configuration configuration;
      private String id;//节点的id属性
      private Integer fetchSize;//节点的fetchSize属性,查询数据的条数
      private Integer timeout;//节点的timeout属性,超时时间
      private StatementType statementType;//节点的statementType属性,默认值:StatementType.PREPARED;疑问?
      private ResultSetType resultSetType;//节点的resultSetType属性,jdbc知识
      private SqlSource sqlSource;//节点中sql语句信息
      private Cache cache;//对应的二级缓存
      private ParameterMap parameterMap;//已废弃
      private List<ResultMap> resultMaps;//节点的resultMaps属性
      private boolean flushCacheRequired;//节点的flushCache属性是否刷新缓存
      private boolean useCache;//节点的useCache属性是否使用二级缓存
      private boolean resultOrdered;
      private SqlCommandType sqlCommandType;//sql语句的类型,包括:INSERT, UPDATE, DELETE, SELECT
      private KeyGenerator keyGenerator;//节点keyGenerator属性
      private String[] keyProperties;
      private String[] keyColumns;
      private boolean hasNestedResultMaps;//是否有嵌套resultMap
      private String databaseId;
      private Log statementLog;
      private LanguageDriver lang;
      private String[] resultSets;//多结果集使用
    
      MappedStatement() {
        // constructor disabled
      }
    }

    为什么使用mapper接口就能操作数据库?

    答:配置文件解读 +动态代理的增强

    binding模块分析

    MapperRegistry : mapper接口和对应的代理 对象工厂的注册中心;

    MapperProxyFactory:用于生成mapper接口动 态代理的实例对象;

    MapperProxy:实现了InvocationHandler接口, 它是增强mapper接口的实现;

    MapperMethod:封装了Mapper接口中对应方 法的信息,以及对应的sql语句的信息;它是 mapper接口与映射配置文件中sql语句的桥梁;

    1、MapperProxyFactory

    /**
     * 
     * 用于生成mapper接口动态代理的实例对象;
     * @author Lasse Voss
     */
    public class MapperProxyFactory<T> {
    
      //mapper接口的class对象
      private final Class<T> mapperInterface;
    //key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
      private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
    
      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) {
        //创建实现了mapper接口的动态代理对象
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
      public T newInstance(SqlSession sqlSession) {
         //每次调用都会创建新的MapperProxy对象
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }
    
    }

    2、MethodProxy

    public class MapperProxy<T> implements InvocationHandler, Serializable {
    
      private static final long serialVersionUID = -6424540398559729838L;
      private final SqlSession sqlSession;//记录关联的sqlsession对象
      private final Class<T> mapperInterface;//mapper接口对应的class对象;
    //key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享
      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 {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强
            return method.invoke(this, args);
          } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        //从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        //调用execute方法执行sql
        return mapperMethod.execute(sqlSession, args);
      }
    }

    解读MapperMethod

    MapperMethod:封装了Mapper接口中对应方法的信息,以及对应的sql语句的信息;它 是mapper接口与映射配置文件中sql语句的桥梁; MapperMethod对象不记录任何状态信 息,所以它可以在多个代理对象之间共享

      SqlCommand : 从configuration中获取方法的命名空间.方法名以及SQL语句的类型;

      MethodSignature:封装mapper接口方法的相关信息(入参,返回类型);

      ParamNameResolver: 解析mapper接口方法中的入参;

    public class MapperMethod {
      //从configuration中获取方法的命名空间.方法名以及SQL语句的类型
      private final SqlCommand command;
      //封装mapper接口方法的相关信息(入参,返回类型);
      private final MethodSignature method;
    
      public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, mapperInterface, method);
      }
    
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        //根据sql语句类型以及接口返回的参数选择调用不同的
        switch (command.getType()) {
          case INSERT: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
          }
          case UPDATE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
          }
          case DELETE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
          }
          case SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {//返回值为void
              executeWithResultHandler(sqlSession, args);
              result = null;
            } else if (method.returnsMany()) {//返回值为集合或者数组
              result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {//返回值为map
              result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {//返回值为游标
              result = executeForCursor(sqlSession, args);
            } else {//处理返回为单一对象的情况
              //通过参数解析器解析解析参数
              Object param = method.convertArgsToSqlCommandParam(args);
              result = sqlSession.selectOne(command.getName(), param);
              if (method.returnsOptional() &&
                  (result == null || !method.getReturnType().equals(result.getClass()))) {
                result = OptionalUtil.ofNullable(result);
              }
            }
            break;
          case FLUSH:
            result = sqlSession.flushStatements();
            break;
          default:
            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;
      }
    
      public static class SqlCommand {
        //sql的名称,命名空间+方法名称
        private final String name;
        //获取sql语句的类型
        private final SqlCommandType type;
    
        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
          final String methodName = method.getName();//获取方法名称
          final Class<?> declaringClass = method.getDeclaringClass();
          //从configuration中获取mappedStatement
          MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
              configuration);
          if (ms == null) {
            if(method.getAnnotation(Flush.class) != null){
              name = null;
              type = SqlCommandType.FLUSH;
            } else {
              throw new BindingException("Invalid bound statement (not found): "
                  + mapperInterface.getName() + "." + methodName);
            }
          } else {//如果mappedStatement不为空
            name = ms.getId();//获取sql的名称,命名空间+方法名称
            type = ms.getSqlCommandType();//获取sql语句的类型
            if (type == SqlCommandType.UNKNOWN) {
              throw new BindingException("Unknown execution method for: " + name);
            }
          }
        }
    
      //从configuration中获取mappedStatement
        private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
            Class<?> declaringClass, Configuration configuration) {
          //sql语句的id为命名空间+方法名字
          String statementId = mapperInterface.getName() + "." + methodName;
          if (configuration.hasStatement(statementId)) {
            return configuration.getMappedStatement(statementId);//从configuration中获取mappedStatement
          } else if (mapperInterface.equals(declaringClass)) {
            return null;
          }
          for (Class<?> superInterface : mapperInterface.getInterfaces()) {
            if (declaringClass.isAssignableFrom(superInterface)) {
              MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                  declaringClass, configuration);
              if (ms != null) {
                return ms;
              }
            }
          }
          return null;
        }
      }
    
      public static class MethodSignature {
    
        private final boolean returnsMany;//返回参数是否为集合类型或数组
        private final boolean returnsMap;//返回参数是否为map
        private final boolean returnsVoid;//返回值为空
        private final boolean returnsCursor;//返回值是否为游标类型
        private final boolean returnsOptional;//返回值是否为Optional
        private final Class<?> returnType;//返回值类型
        private final String mapKey;
        private final Integer resultHandlerIndex;
        private final Integer rowBoundsIndex;
        private final ParamNameResolver paramNameResolver;//该方法的参数解析器
    
        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
          //通过类型解析器获取方法的返回值类型
          Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
          if (resolvedReturnType instanceof Class<?>) {
            this.returnType = (Class<?>) resolvedReturnType;
          } else if (resolvedReturnType instanceof ParameterizedType) {
            this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
          } else {
            this.returnType = method.getReturnType();
          }
          //初始化返回值等字段
          this.returnsVoid = void.class.equals(this.returnType);
          this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
          this.returnsCursor = Cursor.class.equals(this.returnType);
          this.returnsOptional = Jdk.optionalExists && Optional.class.equals(this.returnType);
          this.mapKey = getMapKey(method);
          this.returnsMap = this.mapKey != null;
          this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
          this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
          this.paramNameResolver = new ParamNameResolver(configuration, method);
        }
    }

    创建SqlSession

    策略模式

    策略模式(Strategy Pattern):策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。

    策略模式的使用场景

    •   针对同一类型问题的多种处理方式,仅仅 是具体行为有差别时;
    •   出现同一抽象类有多个子类,而又需要使 用 if-else 或者 switch-case 来选择具体子 类时。

     Context:算法调用者,使用setStrategy方法灵活的选择策略(strategy);

     Strategy:算法的统一接口;

     ConcreteStrategy:算法的具体实现;

    代码示例:

    1. Context上下文-----Context上下文角色,也叫Context封装角色,起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。

    public class Context {
        
        Strategy strategy;
        
        public Context(Strategy strategy) {
            this.strategy = strategy;
        }
        
        //上下文接口
        public void contextInterface() {
            strategy.algorithmInterface();
        }
    
    }

    2、策略角色-----抽象策略角色,是对策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。algorithm是“运算法则”的意思。

    public abstract class Strategy {
        //算法方法
        public abstract void algorithmInterface();
    }

    3、具体策略角色-----用于实现抽象策略中的操作,即实现具体的算法,下方用print代替。测试类共3个ConcreteStrategy,其它两个类与ConcreteStrategyA同理,就不再赘述了。

    public class ConcreteStrategyA extends Strategy {
    
        @Override
        public void algorithmInterface() {
            System.out.println("算法A实现");
        }
    
    }

    4、Client客户端

    public class Client {
        
        public static void main(String[] args) {
            Context context;
            
            context = new Context(new ConcreteStrategyA());
            context.contextInterface();
            
            context = new Context(new ConcreteStrategyB());
            context.contextInterface();
            
            context = new Context(new ConcreteStrategyC());
            context.contextInterface();
        }
    
    }

    运行结果如下:

    SqlSession相关类UML

    SqlSession是MyBaits对外提供的最关键的核心接口,通过它可以执行数据库读写命令、获取映射器、管 理事务等;

     1、DefaultSqlSessionFactory

    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
      //从数据源获取数据库连接
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
            //获取mybatis配置文件中的environment对象
          final Environment environment = configuration.getEnvironment();
          //从environment获取transactionFactory对象
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //创建事务对象
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          //根据配置创建executor
          final Executor executor = configuration.newExecutor(tx, execType);
          //创建DefaultSqlSession
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      //从数据库连接获取sqlSession(用的较少,一般都是通过数据源连接池方式来使用)
      private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        try {
         //获取当前连接是否设置事务自动提交
          boolean autoCommit;
          try {
            autoCommit = connection.getAutoCommit();
          } catch (SQLException e) {
            // Failover to true, as most poor drivers
            // or databases won't support transactions
            autoCommit = true;
          }
          //获取mybatis配置文件中的environment对象
          final Environment environment = configuration.getEnvironment();
          //从environment获取transactionFactory对象
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //创建事务对象
          final Transaction tx = transactionFactory.newTransaction(connection);
          //根据配置创建executor
          final Executor executor = configuration.newExecutor(tx, execType);
          //创建DefaultSqlSession
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    }

    2、DefaultSqlSession

    public class DefaultSqlSession implements SqlSession {
    
      private final Configuration configuration;//configuration对象,全局唯一
      private final Executor executor;//底层依赖的excutor对象
    
      private final boolean autoCommit;//是否自动提交事务
      private boolean dirty;//当前缓存是否有脏数据
      private List<Cursor<?>> cursorList;
    
      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }
    
      public DefaultSqlSession(Configuration configuration, Executor executor) {
        this(configuration, executor, false);
      }
    
      @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          //从configuration中获取要执行的sql语句的配置信息
          MappedStatement ms = configuration.getMappedStatement(statement);
          //通过executor执行语句,并返回指定的结果集
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
          executor.query(ms, wrapCollection(parameter), rowBounds, handler);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      @Override
      public int insert(String statement) {
        return insert(statement, null);
      }
    
      @Override
      public int insert(String statement, Object parameter) {
        return update(statement, parameter);
      }
    
      @Override
      public int update(String statement) {
        return update(statement, null);
      }
    
      @Override
      public int update(String statement, Object parameter) {
        try {
          dirty = true;
          MappedStatement ms = configuration.getMappedStatement(statement);
          return executor.update(ms, wrapCollection(parameter));
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    }

    SqlSessionManager

     sqlSessionManager同时继承了SqlSession接口和SqlSessionFactroy接口,提供了创建SqlSession对象和操纵数据库的能力;

     SqlSessionManager有两种获取SqlSession的模式:

       第一种模式和SqlSessionFactroy 相同,同一个线程每次访问数据库,每次都可以创建新的SqlSession对象;

       第二种模式,同一个线程每次访问数据库,都是使用同一个SqlSession对象,通过localSqlSession实现;

    SqlSession查询接口嵌套关系

    Executor组件分析 

    Executor是MyBaits核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的;

    模板模式

    模板模式(Template Pattern):一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重 写方法实现,但调用将以抽象类中定义的方式进行。定义一个操作中的算法的骨架,而将一些步骤延迟到子类 中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定实现;

    BaseExecutor代码分析 模板模式应用场景

    遇到由一系列步骤构成的过程需要执行,这个过程从高层次上看是相同的,但是有些步骤的实现可能不同,这个时 候就需要考虑用模板模式了。比如:Executor查询操作流程:

    BaseExecutor:抽象类,实现了executor接口的大部 分方法,主要提供了缓存管理和事务管理的能力,其他 子类需要实现的抽象方法为:doUpdate,doQuery等方 法; 

    1、Executor

    /**
     * @author Clinton Begin
     * 
     * MyBaits核心接口之一,定义了数据库操作最基本的方法,SqlSession的功能都是基于它来实现的
     * 
     */
    public interface Executor {
    
      ResultHandler NO_RESULT_HANDLER = null;
    
      //执行增删改的三种类型sql语句
      int update(MappedStatement ms, Object parameter) throws SQLException;
    
      //执行select类型的sql语句,返回结果为集合对象
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
    
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
      
      //执行select类型的sql语句,返回结果为游标
      <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
    
      //批量执行sql语句
      List<BatchResult> flushStatements() throws SQLException;
      
      //提交事务
      void commit(boolean required) throws SQLException;
    
      //回滚事务
      void rollback(boolean required) throws SQLException;
    
      //创建缓存key值
      CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
    
      //是否缓存
      boolean isCached(MappedStatement ms, CacheKey key);
    
      //清空一级缓存
      void clearLocalCache();
    
      //延迟加载一级缓存数据
      void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
    
      //获取事务对象
      Transaction getTransaction();
    
      //关闭executor对象
      void close(boolean forceRollback);
    
      //判断是否关闭
      boolean isClosed();
    
      void setExecutorWrapper(Executor executor);
    
    }

    2、BaseExecutor

    /**
     * @author Clinton Begin
     * 
     * 抽象类,实现了executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,其他子类需要实现的抽象方法为:doUpdate,doQuery等方法;
     * 
     */
    public abstract class BaseExecutor implements Executor {
    
      private static final Log log = LogFactory.getLog(BaseExecutor.class);
    
      protected Transaction transaction;//事务对象
      protected Executor wrapper;//封装的Executor对象
    
      protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//延迟加载的队列
      protected PerpetualCache localCache;//一级缓存的实现,PerpetualCache
      protected PerpetualCache localOutputParameterCache;//一级缓存用于缓存输出的结果
      protected Configuration configuration;//全局唯一configuration对象的引用
    
      protected int queryStack;//用于嵌套查询的的层数
      private boolean closed;
    
      protected BaseExecutor(Configuration configuration, Transaction transaction) {
        this.transaction = transaction;
        this.deferredLoads = new ConcurrentLinkedQueue<>();
        this.localCache = new PerpetualCache("LocalCache");
        this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
        this.closed = false;
        this.configuration = configuration;
        this.wrapper = this;
      }
    
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //获取sql语句信息,包括占位符,参数等信息
        BoundSql boundSql = ms.getBoundSql(parameter);
        //拼装缓存的key值
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
     }
    
      @SuppressWarnings("unchecked")
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {//检查当前executor是否关闭
          throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {//非嵌套查询,并且FlushCache配置为true,则需要清空一级缓存
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;//查询层次加一
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//查询以及缓存
          if (list != null) {
             //针对调用存储过程的结果处理
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
             //缓存未命中,从数据库加载数据
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        
        
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {//延迟加载处理
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {//如果当前sql的一级缓存配置为STATEMENT,查询完既清空一集缓存
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
    
      //真正访问数据库获取结果的方法
      private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);//在缓存中添加占位符
        try {
          //调用抽象方法doQuery,方法查询数据库并返回结果,可选的实现包括:simple、reuse、batch
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          localCache.removeObject(key);//在缓存中删除占位符
        }
        localCache.putObject(key, list);//将真正的结果对象添加到一级缓存
        if (ms.getStatementType() == StatementType.CALLABLE) {//如果是调用存储过程
          localOutputParameterCache.putObject(key, parameter);//缓存输出类型结果参数
        }
        return list;
      }
    }

    Executor的三个实现类解读

     SimpleExecutor:默认配置,使用PrepareStatement对象访问数据库,每次访问都要创建新的 PrepareStatement对象;

     ReuseExecutor:使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象;

     BatchExecutor:实现批量执行多条SQL语句的能力;

    1、SimpleExecutor

    public class SimpleExecutor extends BaseExecutor {
    
      public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      @Override
      //查询的实现
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();//获取configuration对象
          //创建StatementHandler对象,
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          //StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
          stmt = prepareStatement(handler, ms.getStatementLog());
          //通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    
      //创建Statement
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        //获取connection对象的动态代理,添加日志能力;
        Connection connection = getConnection(statementLog);
        //通过不同的StatementHandler,利用connection创建(prepare)Statement
        stmt = handler.prepare(connection, transaction.getTimeout());
        //使用parameterHandler处理占位符
        handler.parameterize(stmt);
        return stmt;
      }
    
    }

    2、ReuseExecutor

    public class ReuseExecutor extends BaseExecutor {
    
      private final Map<String, Statement> statementMap = new HashMap<>();
    
      public ReuseExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      @Override
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();//获取configuration对象
        //创建StatementHandler对象
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //StatementHandler对象创建stmt,并使用parameterHandler对占位符进行处理
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
      //通过statementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
        return handler.<E>query(stmt, resultHandler);
      }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();//获取sql语句
        if (hasStatementFor(sql)) {//根据sql语句检查是否缓存了对应的Statement
          stmt = getStatement(sql);//获取缓存的Statement
          applyTransactionTimeout(stmt);//设置新的超时时间
        } else {//缓存中没有statment,创建statment过程和SimpleExecutor类似
          Connection connection = getConnection(statementLog);
          stmt = handler.prepare(connection, transaction.getTimeout());
          putStatement(sql, stmt);//放入缓存中
        }
      //使用parameterHandler处理占位符
        handler.parameterize(stmt);
        return stmt;
      }
    }

    3、CachingExecutor-----二级缓存实现

    public class CachingExecutor implements Executor {
    
      private final Executor delegate;
      private final TransactionalCacheManager tcm = new TransactionalCacheManager();
    
      public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
      }
    
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //获取sql语句信息,包括占位符,参数等信息
        BoundSql boundSql = ms.getBoundSql(parameterObject);
      //拼装缓存的key值
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        //从MappedStatement中获取二级缓存
        Cache cache = ms.getCache();
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);//从二级缓存中获取数据
            if (list == null) {
              //二级缓存为空,才会调用BaseExecutor.query
              list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    }

    Executor的三个重要小弟

    通过对SimpleExecutor doQuery()方法的解读发现,Executor是个指挥官,它在调度三个小弟工作:

       StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;

       ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的的占位符“?”都对应 BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性

       ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;

    1、StatementHandler分析

    StatementHandler完成Mybatis最核心的工作,也是Executor实现的基础;功能包括:创建statement对象, 为sql语句绑定参数,执行增删改查等SQL语句、将结果映射集进行转化;

    /**
     * Executor实现的基础;功能包括:创建statement对象,为sql语句绑定参数,执行增删改查等SQL语句、将结果映射集进行转化
     * @author Clinton Begin
     * 
     */
    public interface StatementHandler {
    
     //从连接中获取一个Statement
      Statement prepare(Connection connection, Integer transactionTimeout)
          throws SQLException;
    
      //占位符处理,绑定statement执行时需要的实参
      void parameterize(Statement statement)
          throws SQLException;
     //批量执行sql语句
      void batch(Statement statement)
          throws SQLException;
    //执行update/insert/delete语句
      int update(Statement statement)
          throws SQLException;
    
    //执行select语句
      <E> List<E> query(Statement statement, ResultHandler resultHandler)
          throws SQLException;
    
      <E> Cursor<E> queryCursor(Statement statement)
          throws SQLException;
    
      //获取sql语句
      BoundSql getBoundSql();
    
      //获取封装的参数处理器
      ParameterHandler getParameterHandler();
    
    }
    public abstract class BaseStatementHandler implements StatementHandler {
    
      protected final Configuration configuration;
      protected final ObjectFactory objectFactory;
      protected final TypeHandlerRegistry typeHandlerRegistry;
      //结果处理器,对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型;
      protected final ResultSetHandler resultSetHandler;
      //sql占位符处理器,对预编译的SQL语句进行参数设置
      protected final ParameterHandler parameterHandler;
      //记录执行语句的executor对象
      protected final Executor executor;
      //sql语句对应的MappedStatement
      protected final MappedStatement mappedStatement;
      //分页信息
      protected final RowBounds rowBounds;
      //sql语句
      protected BoundSql boundSql;
    
      @Override
      //使用模板模式,定义了获取Statement的步骤,其子类实现实例化Statement的具体的方式;
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
          //通过不同的子类实例化不同的Statement,分为三类:simple(statment)、prepare(prepareStatement)、callable(CallableStatementHandler)
          statement = instantiateStatement(connection);
          //设置超时时间
          setStatementTimeout(statement, transactionTimeout);
          //设置数据集大小
          setFetchSize(statement);
          return statement;
        } catch (SQLException e) {
          closeStatement(statement);
          throw e;
        } catch (Exception e) {
          closeStatement(statement);
          throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
        }
      }
    }
    public class SimpleStatementHandler extends BaseStatementHandler {
    
      public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
      }
    
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = boundSql.getSql();//获取sql语句
        statement.execute(sql);//执行sql语句
        return resultSetHandler.<E>handleResultSets(statement);//使用resultSetHandler处理查询结果
      }
    
      @Override
      //使用底层的statment对象来完成对数据库的操作
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        if (mappedStatement.getResultSetType() != null) {
            //设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
          return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
          return connection.createStatement();
        }
      }
    }
    public class PreparedStatementHandler extends BaseStatementHandler {
    
      public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
      }
    
      @Override
      //使用底层的prepareStatement对象来完成对数据库的操作
      protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        //根据mappedStatement.getKeyGenerator字段,创建prepareStatement
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {//对于insert语句
          String[] keyColumnNames = mappedStatement.getKeyColumns();
          if (keyColumnNames == null) {
            //返回数据库生成的主键
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
          } else {
            //返回数据库生成的主键填充至keyColumnNames中指定的列
            return connection.prepareStatement(sql, keyColumnNames);
          }
        } else if (mappedStatement.getResultSetType() != null) {
         //设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新
          return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
          //创建普通的prepareStatement对象
          return connection.prepareStatement(sql);
        }
      }
    
      @Override
      //使用parameterHandler对sql语句的占位符进行处理
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    
    }

    2、ParameterHandler

    public interface ParameterHandler {
    
      Object getParameterObject();
    
      void setParameters(PreparedStatement ps)
          throws SQLException;
    
    }
    public class DefaultParameterHandler implements ParameterHandler {
    
    //typeHandler注册中心
      private final TypeHandlerRegistry typeHandlerRegistry;
      //对应的sql节点的信息
      private final MappedStatement mappedStatement;
      //用户传入的参数
      private final Object parameterObject;
      //SQL语句信息,其中还包括占位符和参数名称信息
      private final BoundSql boundSql;
      private final Configuration configuration;
    
      public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.mappedStatement = mappedStatement;
        this.configuration = mappedStatement.getConfiguration();
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
      }
    
      @Override
      public Object getParameterObject() {
        return parameterObject;
      }
    
      @Override
      public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        //从boundSql中获取sql语句的占位符对应的参数信息
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        //遍历这个参数列表,把参数设置到PreparedStatement中
        if (parameterMappings != null) {
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {//对于存储过程中的参数不处理
              Object value;//绑定的实参
              String propertyName = parameterMapping.getProperty();//参数的名字
              if (boundSql.hasAdditionalParameter(propertyName)) { // 获取对应的实参值
                value = boundSql.getAdditionalParameter(propertyName);
              } else if (parameterObject == null) {
                value = null;
              } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                value = parameterObject;
              } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                value = metaObject.getValue(propertyName);
              }
              TypeHandler typeHandler = parameterMapping.getTypeHandler();//从parameterMapping中获取typeHandler对象
              JdbcType jdbcType = parameterMapping.getJdbcType();//获取参数对应的jdbcType
              if (value == null && jdbcType == null) {
                jdbcType = configuration.getJdbcTypeForNull();
              }
              try {
                 //为statment中的占位符绑定参数
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
              } catch (TypeException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              } catch (SQLException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              }
            }
          }
        }
      }
    
    }

    3、ResultSetHandler分析

    ResultSetHandler将从数据库查询得到的结果按照映射配置文件的映射规则,映射成相应的结果集对象;

    public interface ResultSetHandler {
    
      <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    
      <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
    
      void handleOutputParameters(CallableStatement cs) throws SQLException;
    
    }
    public class DefaultResultSetHandler implements ResultSetHandler {
    
      @Override
      public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
        //用于保存结果集对象
        final List<Object> multipleResults = new ArrayList<>();
    
        int resultSetCount = 0;
        //statment可能返回多个结果集对象,这里先取出第一个结果集
        ResultSetWrapper rsw = getFirstResultSet(stmt);
        //获取结果集对应resultMap,本质就是获取字段与java属性的映射规则
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);//结果集和resultMap不能为空,为空抛出异常
        while (rsw != null && resultMapCount > resultSetCount) {
         //获取当前结果集对应的resultMap
          ResultMap resultMap = resultMaps.get(resultSetCount);
          //根据映射规则(resultMap)对结果集进行转化,转换成目标对象以后放入multipleResults中
          handleResultSet(rsw, resultMap, multipleResults, null);
          rsw = getNextResultSet(stmt);//获取下一个结果集
          cleanUpAfterHandlingResultSet();//清空nestedResultObjects对象
          resultSetCount++;
        }
        //获取多结果集。多结果集一般出现在存储过程的执行,存储过程返回多个resultset,
        //mappedStatement.resultSets属性列出多个结果集的名称,用逗号分割;
        //多结果集的处理不是重点,暂时不分析
        String[] resultSets = mappedStatement.getResultSets();
        if (resultSets != null) {
          while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
              String nestedResultMapId = parentMapping.getNestedResultMapId();
              ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
              handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
        }
    
        return collapseSingleResultList(multipleResults);
      }
    
      private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
          if (parentMapping != null) {//处理多结果集的嵌套映射
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
          } else {
            if (resultHandler == null) {//如果resultHandler为空,实例化一个人默认的resultHandler
              DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
              //对ResultSet进行映射,映射结果暂存在resultHandler中
              handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
              //将暂存在resultHandler中的映射结果,填充到multipleResults
              multipleResults.add(defaultResultHandler.getResultList());
            } else {
              //使用指定的rusultHandler进行转换
              handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
          }
        } finally {
          // issue #228 (close resultsets)
          //调用resultset.close()关闭结果集
          closeResultSet(rsw.getResultSet());
        }
      }
    
      public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        if (resultMap.hasNestedResultMaps()) {//处理有嵌套resultmap的情况
          ensureNoRowBounds();
          checkResultHandler();
          handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {//处理没有嵌套resultmap的情况
          handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
      }
    
      //简单映射处理
      private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
          throws SQLException {
        //创建结果上下文,所谓的上下文就是专门在循环中缓存结果对象的
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
        //1.根据分页信息,定位到指定的记录
        skipRows(rsw.getResultSet(), rowBounds);
        //2.shouldProcessMoreRows判断是否需要映射后续的结果,实际还是翻页处理,避免超过limit
        while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
          //3.进一步完善resultMap信息,主要是处理鉴别器的信息
          ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
          //4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
          Object rowValue = getRowValue(rsw, discriminatedResultMap);
          //5.保存映射结果对象
          storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
      }
    
      //保存映射结果对象
      private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        if (parentMapping != null) {//如果是嵌套结果或嵌套查询,将对象保存至父对象
          linkToParents(rs, parentMapping, rowValue);
        } else {//普通映射则把对象保存至resultHandler和resultContext
          callResultHandler(resultHandler, resultContext, rowValue);
        }
      }
    
      private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
         //检测上下文的stop状态,并检测映射的行数是否达到了limit的上限
        return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
      }
    
      //映射之前处理翻页信息
      private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
        //根据ResultSet的类型定位
        if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
          if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
            rs.absolute(rowBounds.getOffset());//直接定位到offset指定的记录
          }
        } else {
          //通过多次调用next移动到目标记录
          for (int i = 0; i < rowBounds.getOffset(); i++) {
            rs.next();
          }
        }
      }
    
      //
      // GET VALUE FROM ROW FOR SIMPLE RESULT MAP
      //
      //4.读取resultSet中的一行记录并进行映射,转化并返回目标对象
      private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        //4.1 根据resultMap的type属性,实例化目标对象
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
          //4.2 对目标对象进行封装得到metaObjcect,为后续的赋值操作做好准备
          final MetaObject metaObject = configuration.newMetaObject(rowValue);
          boolean foundValues = this.useConstructorMappings;//取得是否使用构造函数初始化属性值
          if (shouldApplyAutomaticMappings(resultMap, false)) {//是否使用自动映射
             //4.3一般情况下 autoMappingBehavior默认值为PARTIAL,对未明确指定映射规则的字段进行自动映射
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
          }
           //4.4 映射resultMap中明确指定需要映射的列
          foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
          foundValues = lazyLoader.size() > 0 || foundValues;
          //4.5 如果没有一个映射成功的属性,则根据<returnInstanceForEmptyRow>的配置返回null或者结果对象
          rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
      }
    
      private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        //从resultMap中获取明确需要转换的列名集合
        final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        //获取ResultMapping集合
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
          String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);//获得列名,注意前缀的处理
          if (propertyMapping.getNestedResultMapId() != null) {
            // the user added a column attribute to a nested result map, ignore it
            //如果属性通过另外一个resultMap映射,则忽略
            column = null;
          }
          if (propertyMapping.isCompositeResult()//如果是嵌套查询,column={prop1=col1,prop2=col2}
              || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))//基本类型映射
              || propertyMapping.getResultSet() != null) {//嵌套查询的结果
            //获得属性值
            Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
            // issue #541 make property optional
            //获得属性名称
            final String property = propertyMapping.getProperty();
            if (property == null) {//属性名为空跳出循环
              continue;
            } else if (value == DEFERED) {//属性名为DEFERED,延迟加载的处理
              foundValues = true;
              continue;
            }
            if (value != null) {
              foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
              // gcode issue #377, call setter on nulls (value is not 'found')
              //通过metaObject为目标对象设置属性值
              metaObject.setValue(property, value);
            }
          }
        }
        return foundValues;
      }
      
      //获得属性值
      private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
          throws SQLException {
        if (propertyMapping.getNestedQueryId() != null) {//嵌套查询的处理
          return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
        } else if (propertyMapping.getResultSet() != null) {//嵌套结果的处理
          addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?
          return DEFERED;
        } else {//基本类型直接通过typeHandler获取属性值
          final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
          final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
          return typeHandler.getResult(rs, column);
        }
      }
      //获取resultSet中存在的,但是ResultMap中没有明确映射的列,填充至autoMapping中
      private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        final String mapKey = resultMap.getId() + ":" + columnPrefix;
        List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);//从缓存中获取
        if (autoMapping == null) {
          autoMapping = new ArrayList<>();
          //获取未映射的列名
          final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
          for (String columnName : unmappedColumnNames) {
            String propertyName = columnName;
            if (columnPrefix != null && !columnPrefix.isEmpty()) {//前缀的处理,如果有前缀,属性名为列名去除前缀
              // When columnPrefix is specified,
              // ignore columns without the prefix.
              if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {//如果有前缀,属性名为列名去除前缀
                propertyName = columnName.substring(columnPrefix.length());
              } else {
                continue;
              }
            }
            //在结果对象中查找指定的属性名
            final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
            //检查该属性是否有setter方法
            if (property != null && metaObject.hasSetter(property)) {
              if (resultMap.getMappedProperties().contains(property)) {//如果该属性在resultMap中已经指定,则忽略此属性
                continue;
              }
              final Class<?> propertyType = metaObject.getSetterType(property);
              if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {//判断是否有匹配的typeHandler
                final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);//找到类型转换器
                //创建UnMappedColumnAutoMapping,并填充至autoMapping
                autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
              } else {
                configuration.getAutoMappingUnknownColumnBehavior()
                    .doAction(mappedStatement, columnName, property, propertyType);
              }
            } else {
              configuration.getAutoMappingUnknownColumnBehavior()
                  .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
            }
          }
          autoMappingsCache.put(mapKey, autoMapping);
        }
        return autoMapping;
      }
      //对未明确指定映射规则的字段进行自动映射
      private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        //获取resultSet中存在的,但是ResultMap中没有明确映射的列,填充至autoMapping中
        List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
        boolean foundValues = false;
        if (!autoMapping.isEmpty()) {
          //遍历autoMapping,通过自动匹配的方式为属性复制
          for (UnMappedColumnAutoMapping mapping : autoMapping) {
            //通过typeHandler从resultset中拿值
            final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
            if (value != null) {
              foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
              // gcode issue #377, call setter on nulls (value is not 'found')
              //通过metaObject给属性赋值
              metaObject.setValue(mapping.property, value);
            }
          }
        }
        return foundValues;
      }
    }

    Executor内部运作过程

     

  • 相关阅读:
    ios-UIScrollView-常用属性和方法
    ios-后台运行UIApplication
    ios-UIImage写入相册
    ios-时间格式化
    ios-block-对象与对象之间的解偶合
    ios-通知
    Copy List with Random Pointer
    leetcode面试频率
    TCP的连接(三次握手)和释放(四次挥手)
    Longest Palindromic Substring(字符串的最大回文子串)
  • 原文地址:https://www.cnblogs.com/alimayun/p/12305431.html
Copyright © 2020-2023  润新知