springboot-spring源码系列(十)

187 阅读14分钟

详解spring-mybatis是如何进行整合的

前言:

    本篇文章只会分析spring和mybatis是如何进行解析的,如果你是想了解mybatis的底层源码实现,可能不会带给你很大的帮助。还是以0xml的方式进来解析

测试环境:

@MapperScan("com.lihuia.mybatis.mapper")
@Configuration
public class AppConfig {

	@Bean
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
		factoryBean.setDataSource(dataSource());
		return factoryBean.getObject();
	}

	@Bean
	public DataSource dataSource() {
		DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
		driverManagerDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
		driverManagerDataSource.setUsername("root");
		driverManagerDataSource.setPassword("admin");
		driverManagerDataSource.setUrl("jdbc:mysql:///banksys?serverTimezone=UTC&characterEncoding=utf-8");
		return driverManagerDataSource;
	}
}

@MapperScan    可以说这一个注解完成spring与mybatis的整合

sqlSessionFactory这个类对于Mybatis来说是无与伦比的重要,他就相当于spring的beanFactory,至于具体的功能本篇文章不会进行说明,他以sqlSessionFactoryBean交给spring管理,并且必须要手动进行初始化然后交给spring,至于为什么?你总不能修改Mybatis的源码把。

//sqlSessionFactorybean实现了FactoryBean接口,所以直接看getObject()方法 
 public org.apache.ibatis.session.SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }

public void afterPropertiesSet() throws Exception {  
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

 protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    //省略的代码就是把配置文件中的内容保存在configuration,然后通过configuration构建sqlSessionFaactory
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

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

再讲一下spring为什么能够扫描@MapperScan()注解中的内容

//直接把@MapperScan上的注释拿过来 
 * Use this annotation to register MyBatis mapper interfaces when using Java
 * Config. It performs when same work as {@link MapperScannerConfigurer} via
 * {@link MapperScannerRegistrar}.
 * 使用Java时,使用此注释注册MyBatis映射器接口配置。{@link MapperScannerConfigurer}和{@link mapperscannerregistar}有着相同的功能
 * 所以我们直接去看 MapperScannerRegistrar 这个东西

//再看MapperScannerRegistrar上的注释
 * A {@link ImportBeanDefinitionRegistrar} to allow annotation configuration of
 * MyBatis mapper scanning. Using an @Enable annotation allows beans to be
 * registered via @Component configuration, whereas implementing
 * {@code BeanDefinitionRegistryPostProcessor} will work for XML configuration.
 *
 * @author Michael Lanyon
 * @author Eduardo Macarron
 *
 * @see MapperFactoryBean
 * @see ClassPathMapperScanner
 * @since 1.2.0
 */

//感觉这个ClassPathMapperScanner扫描器好像和我们想看的差不多,直接找这个类
/**
 * Calls the parent search that will search and register all the candidates. Then the registered objects are post
 * processed to set them as MapperFactoryBeans
 */
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

  if (beanDefinitions.isEmpty()) {
    LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
        + "' package. Please check your configuration.");
  } else {
    processBeanDefinitions(beanDefinitions);
  }

  return beanDefinitions;
}
DEBUG一下,可以看到这个就是扫描Mapper接口的方法,返回@MapperScan注解的value

ok,重点来了@MapperScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) //通过import注解,添加进spring ioc容器
public @interface MapperScan
//仅仅只是一个注解,直接看@import方法导入的类

/**
 * 实现了ImportBeanDefinitionRegistrar接口,扫描该类的时候会执行registerBeanDefinitions方法
 * spring提供的扩展点
 */
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

	private ResourceLoader resourceLoader;

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		/**
		 * 获取 @MapperScan("xxxx")上的内容
		 */
		AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
		/**
		 * 扩展spring中ClassPathBeanDefinitionScanner类将类扫描成BeanDefinition
		 */
		ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
		/**
		 * 设置资源加载器,作用:扫描指定包下的class文件。
		 */
		if (resourceLoader != null) {
			scanner.setResourceLoader(resourceLoader);
		}
		/**
		 * 把MapperScan中的属性设置到ClassPathMapperScanner
		 */
		Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
		if (!Annotation.class.equals(annotationClass)) {
			scanner.setAnnotationClass(annotationClass);
		}

		Class<?> markerInterface = annoAttrs.getClass("markerInterface");
		if (!Class.class.equals(markerInterface)) {
			scanner.setMarkerInterface(markerInterface);
		}

		Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
		if (!BeanNameGenerator.class.equals(generatorClass)) {
			scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
		}

		Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
		if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
			scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
		}
		/**
		 * 设置SqlSessionFactory的名称
		 * 这也证实了使用的 sqlSession 是 sqlSessionTemplate
		 */
		scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
		scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
		//设置集合保存路径值
		List<String> basePackages = new ArrayList<String>();
		//拿到 @MpperScan("xxx") 中的value值,添加进basePackages中
		for (String pkg : annoAttrs.getStringArray("value")) {
			if (StringUtils.hasText(pkg)) {
				basePackages.add(pkg);
			}
		}
		//拿到 @MpperScan("xxx")中的basePackages值,添加进basePackages中
		for (String pkg : annoAttrs.getStringArray("basePackages")) {
			if (StringUtils.hasText(pkg)) {
				basePackages.add(pkg);
			}
		}
		// 拿到 @MpperScan("xxx") 中的basePackageClasses值,解析成该类的全限定名,添加进basePackages中
		for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
			basePackages.add(ClassUtils.getPackageName(clazz));
		}
		// 注册过滤器,过滤掉不符合规范的mapper
		scanner.registerFilters();
		/**
		 * 重点!!!!
		 *  扫描指定包
		 */
		scanner.doScan(StringUtils.toStringArray(basePackages));
	}

}

    //分析一下过滤器    
    public void registerFilters() {
                //添加一个标识
		boolean acceptAllInterfaces = true;
		/**
		 * 对于annotationClass属性的处理
		 * 如果annotationClass不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,
		 * 而封装此属性的过滤器就是AnnotationTypeFilter。
		 * AnnotationTypeFilter保证在扫描对应Java文件时只接受标记有注解为annotationClass的接口。
		 */
		if (this.annotationClass != null) {
			addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
			acceptAllInterfaces = false;
		}	
		/**
		 * 对于markerInterface属性的处理
		 * 如果markerInterface不为空,表示用户设置了此属性,那么就要根据此属性生成过滤器以保证达到用户想要的效果,
		 * 而封装此属性的过滤器就是实现AssignableTypeFilter接口的局部类。
		 * 表示扫描过程中只有实现markerInterface接口的接口才会被接受。
		 */
		if (this.markerInterface != null) {
			addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
				@Override
				protected boolean matchClassName(String className) {
					return false;
				}
			});
			acceptAllInterfaces = false;
		}
		//默认对所有接口都扫描
		if (acceptAllInterfaces) {
			// default include filter that accepts all classes
			addIncludeFilter(new TypeFilter() {
				@Override
				public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
					return true;
				}
			});
		}
		/**
		 对于命名为package-info的Java文件,默认不作为逻辑实现接口,将其排除掉,
		 使用TypeFilter接口的局部类实现match方法。
		 从上面的函数我们看出,控制扫描文件Spring通过不同的过滤器完成,
		 这些定义的过滤器记录在了includeFilters和excludeFilters属性中。
		 */
		addExcludeFilter(new TypeFilter() {
			@Override
			public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
				String className = metadataReader.getClassMetadata().getClassName();
				return className.endsWith("package-info");
			}
		});
    }

    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
		/**
		 * 调用父类的扫描器,也就是spring提供的扫描器
		 * 根据传入的路径,把路径下的class文件变成beanDefinition,注入beanDefinitionMap中
                 * 返回所有成功注入的beanDefinition集合
		 */
		Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
		if (beanDefinitions.isEmpty()) {
			logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
		} else {		
			//如果不为空,继续对beanDefinitions中的所有beanDefinition进行属性的修改
			processBeanDefinitions(beanDefinitions);
		}
		return beanDefinitions;
	}

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
		GenericBeanDefinition definition;
		//遍历所有的beanDefinition
		for (BeanDefinitionHolder holder : beanDefinitions) {
			definition = (GenericBeanDefinition) holder.getBeanDefinition();
			/**
			 * 把beanDefinition的beanName设置为实例化所需构造器的参数
			 * 也就说当该beanDefinition进行实例化时,推断带参数的构造方法
			 */
			definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
			/**
			 * 原始的接口类型的bean替换成一个MapperFactoryBean的类型。
			 * 所以它会根据 MapperFactoryBean这个类来实例化对象
			 * 又因为该类实现了FactoryBean接口,所以会在spring ioc容器中创建两个实例
			 *  xxMapper   &xxMapper
			 */
			definition.setBeanClass(this.mapperFactoryBean.getClass());
			//向beanDefinition添加属性 addToConfig=false
			definition.getPropertyValues().add("addToConfig", this.addToConfig);
                        //定义一个标识
			boolean explicitFactoryUsed = false;
                         //判断sqlSessionFactory是否为null,
                         if (this.sqlSessionFactory != null) {				
                                //向beanDefinition添加属性 sqlSessionFactory = DefaultSqlSessionFactory				definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
				explicitFactoryUsed = true;
			}else if (this.sqlSessionTemplate != null) {
				//向beanDefinition中添加属性 sqlSessionTemplat = SqlSessionTemplate
				definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
                                //修改标识
				explicitFactoryUsed = true;
			}
			if (!explicitFactoryUsed) {				
				/**
				 *  设置注入模型为 BY_TYPE
				 *  进行属性赋值时populateBean(beanName, mbd, instanceWrapper);
				 *  会通过反射调用所有set方法进行赋值,通过类型从单例池找
                                 *  
                                 * 扩展一下其他的注入模型
                                 *    AUTOWIRE_NO:不进行自动注入,只注入加了@Autowire注解的,先通过类型从单例池找.如果找到多个就根据名字选择一个
                                 *    AUTOWIRE_BY_NAME:也是通过set方法只不过,通过名字从单例池中找
                                 *    AUTOWIRE_CONSTRUCTOR:通过实例化时候的构造方法进行注入
				 */
				definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
			}
		}
	}

@MapperScan差不多了解了,把你规定的包路径下的所有类变成beanDefinition放到beanDefinitionMap中,并且设置他的beanClass为MapperFactoryBean而且它实现了MapperFactory,所以直接看这个类,就能清楚spring-mybatis的整合

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
	/**
	 * 使用BY_TYPE类型,先拿到所有的属性,如果有get/set方法就证明该属性需要被注入
	 * 但是会经过过滤,过滤掉某些属性  1.Class类型 2.显示赋值了,所以下面这两个属性都不会赋值
         * 但是别忘记此时还有父类的属性被注入
         *  private SqlSession sqlSession;
	 *
	 *  因为是By_Type注入模型,所以会调用父类所有setXX()方法,因为只设置了sqlSessionFactory的属性参数,所以实际上只会调用这一个setXx()方法
         *  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
         *       if (!this.externalSqlSession) {
         *           this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
         *           }
         *     }
         *  //该方法由于大部分情况没有设置方法参数,所以不会执行
         *  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
         *       this.sqlSession = sqlSessionTemplate;
         *       this.externalSqlSession = true;
         *     }
	 */

      private Class<T> mapperInterface;
      private boolean addToConfig = true;

      public MapperFactoryBean() {
        //intentionally empty 
      }
	/**
	 *  构造器,此时beanDefinition中已经设置了构造器输入参数
	 *    所以在通过反射调用构造器实例化时,一定会通过该构造方法去实例化对象
	 *    并且获取在BeanDefinition设置的构造器输入参数
	 *    也就是对应得每个Mapper接口的全限定类名(com.mapper.xxMapper)赋给mapperInterface属性
	 */
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }

  /**
   *  因为该类实现了 InitializingBean 接口,会在属性注入(@Autowired)之后、执行afterPropertiesSet方法
   *  afterPropertiesSet方法执行了checkDaoConfig方法
   *  这个方法就是完成了方法和sqlSource建立映射
   *  为什么要在属性注入之后才执行该方法
   *  因为当属性注入完成之后,该对象的属性才有值,才能实现映射
   */
      @Override
      protected void checkDaoConfig() {   
        //不重要的代码直接删除了

         /**
	  * 此时sqlSession是存在的,因为我们对beanDefinition设置了sqlSessionFactory属性
	  * 在实例化后的属性注入时,通过setSqlSessionFactory() 设值 sqlSessionTemplate
	  * sqlSessionTemplate对象也是一个单例,全局唯一,供所有的Mapper代理类使用
          *
          * getConfiguration()通过sqlSessionTemplate.DefaultSqlSessionFactory拿到的Configuration此时仅仅完成初始化
	  */
         Configuration configuration = getSqlSession().getConfiguration();
         //addToConfig一定为true && 一定不存在取反一定为true 
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
          try {
		//往 MappedStatement 中添加了全限定名+方法名作为key,sqlSource作为value代理对象
    		configuration.addMapper(this.mapperInterface);
          }
      }

      @Override
      public T getObject() throws Exception {
	  /**
	   *  获取父类setSqlSessionFactory方法中创建的 SqlSessionTemplate
	   *  通过SqlSessionTemplate获取mapperInterface的代理类
	   *  也就是说不会在单例池中存在,singletonObjects只存在MapperFactoryBean这个类,不存在Mapper接口的代理类
	   *  所以说该方法只会执行一次,除非清除掉缓存,才会再次执行
	   */
        return getSqlSession().getMapper(this.mapperInterface);
      }

先看一下this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);是个什么东西

        /**
	 * SqlSessionTemplate在内部访问数据库时,其实是委派给sqlSessionProxy来执行数据库操作的,
	 * SqlSessionTemplate不是自身重新实现了一套mybatis数据库访问的逻辑。
	 *
	 * SqlSessionTemplate通过静态代理机制来提供SqlSession接口的行为,
	 * 即实现SqlSession接口来获取SqlSession的所有方法;SqlSessionTemplate的定义如下:标准的静态代理实现模式,
	 * 即实现SqlSession接口并在内部包含一个SqlSession接口实现类引用sqlSessionProxy。
	 */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    //创建了一个代理对象,重点看一下用来增强的方法,至于为什么JDK动态代理自己去看
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

 private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		/**
		 * 开启 sqlSession,
		 * 获取一个sqlSession来执行proxy的method对应的SQL,
		 * 每次调用都获取创建一个sqlSession线程局部变量,故不同线程相互不影响,
		 * 在这里实现了SqlSessionTemplate的线程安全性
		 *
		 */
		SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
		  /**
		   *  直接通过新创建的SqlSession反射调用method,在下面的知识就是Mybatis源码了
		   */
		  Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
			/**
			 *   如果当前操作没有在一个Spring事务中,则手动commit一下
			 *   如果当前业务没有使用@Transation,那么每次执行了Mapper接口的方法直接commit
			 *   还记得我们前面讲的Mybatis的一级缓存吗,这里一级缓存不能起作用了,
			 *   因为每执行一个Mapper的方法,sqlSession都提交了
			 *   sqlSession提交,会清空一级缓存
			 */
			sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
			/**
			 * session关闭,所以session是交给了spring管理的
			 * 我们手动开关session是没什么作用的。这样一级缓存就失效了
			 */
			closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

在看一下是如何建立映射关系的configuration.addMapper(this.mapperInterface);

  //看一下如何添加一个映射
  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 {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
		  /**
		   * 实现了往 MapperStatement 中添加数据,至于这是干啥的,Mybatis源码自己看,反正方法和具体的sql语句都保存在这个集合中
		   */
		  parser.parse();
        loadCompleted = true;
      } finally {
        //如果加载过程中出现异常需要再将这个mapper从mybatis中删除,这种方式比较丑陋吧,难道是不得已而为之?
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

	public void parse() {
		String resource = type.toString();
		if (!configuration.isResourceLoaded(resource)) {
			//加载xml资源
			loadXmlResource();
			//添加加载资源
			configuration.addLoadedResource(resource);
			//设置当前命名空间
			assistant.setCurrentNamespace(type.getName());
			//解析缓存
			parseCache();
			//解析缓存引用
			parseCacheRef();
			Method[] methods = type.getMethods();
			/**
			 * 循环所有方法
			 */
			for (Method method : methods) {
				try {
					// issue #237
					if (!method.isBridge()) {
						/**
						 * 解析方法中的sql注解语句
						 */
						parseStatement(method);
					}
				} catch (IncompleteElementException e) {
					configuration.addIncompleteMethod(new MethodResolver(this, method));
				}
			}
		}
		parsePendingMethods();
	}

最后看一下MapperFactory.getObject()方法

  @Override
  public <T> T getMapper(Class<T> type) {
	  /**
	   *  通过sqlSessionFactory创建代理类,sqlSessionTemplate作为代理类的模板,并且该类还有一个内部类
	   *  InvocationHandler(),作为对每个方法的增强,通过拿到方法上的注解 进行具体的执行操作
	   *
	   * 实现SqlSession接口,单例、线程安全,使用spring的事务管理器的sqlSession,
	   * 具体的SqlSession的功能,则是通过内部包含的sqlSessionProxy来来实现,这也是静态代理的一种实现。
	   * 同时内部的sqlSessionProxy实现InvocationHandler接口,则是动态代理的一种实现,而线程安全也是在这里实现的。
	   * 注意mybatis默认的sqlSession不是线程安全的,需要每个线程有一个单例的对象实例。
	   * SqlSession的主要作用是提供SQL操作的API,执行指定的SQL语句,mapper需要依赖SqlSession来执行其方法对应的SQL
	   *
	   * Configuration就像是Mybatis的总管,Mybatis的所有配置信息都存放在这里
	   * 比如:结果集处理器,是否开启缓存,驼峰表示法,slf4j,各种缓存等等
	   * 此外,它还提供了设置这些配置信息的方法。
	   * Configuration可以从配置文件里获取属性值,也可以通过程序直接设置。
	   *
	   * 他把自己传进去了重点
	   */
    return getConfiguration().getMapper(type, this);
  }

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	  /**
	   * 把sqlSessionTemplate当参数传进去
	   */
    return mapperRegistry.getMapper(type, sqlSession);
  }

  //返回代理类
  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 {
		/**
		 * 创建代理类
		 * 重点!!!!!!!
		 * 最外层的动态代理是MapperProxy,然后该代理类封装了sqlSessionTemplate
		 * 当执行完  MapperProxy 的 invoke方法后,还要执行 sqlSessionTemplate的sql执行语句
		 * 又因为sql执行语句调用了sqlSessionProxy的sql执行语句,
		 * 而sqlSessionProxy是一个代理类,所有先执行他的invoke方法,
		 * 最终执行完毕
		 */
		return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

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

  //这就是代理完成后的某一个增强方法,明显看出它是通过sqlSessionProxy代理类实现的SQL语句执行
  public int update(String statement) {
    return this.sqlSessionProxy.update(statement);
  }

OK第一遍不理解很正常,跟着DEBUG多走几遍就能明白,最后小总结一下

        /**
	 * 这里大概讲一下Mapper代理类调用方法执行逻辑:
	 *
	 * 1、SqlSessionTemplate生成Mapper代理类时,将本身传进去做为Mapper代理类的属性,调用Mapper代理类的方法时,
	 * 最后会通过SqlSession类执行,也就是调用SqlSessionTemplate中的方法。
	 *
	 * 2、SqlSessionTemplate中操作数据库的方法中又交给了sqlSessionProxy这个代理类去执行,
	 * 那么每次执行的方法都会回调其SqlSessionInterceptor这个InvocationHandler的invoke方法
	 *
	 * 3、在invoke方法中,为每个线程创建一个新的SqlSession,并通过反射调用SqlSession的method。
	 * 这里sqlSession是一个线程局部变量,不同线程相互不影响,实现了SqlSessionTemplate的线程安全性
	 *
	 * 4、如果当前操作并没有在Spring事务中,那么每次执行一个方法,都会提交,相当于数据库的事务自动提交,
	 * Mysql的一级缓存也将不可用
	 */

        /**
	 * mybatis和spring整合后一级缓存失效的问题
         *
	 * 一级缓存: MyBatis会创建出一个SqlSession对象表示一次数据库会话。
	 * 	在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,
	 * 	由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。
	 *	为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,
	 *	将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,
	 *	会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了,
	 *	当sqlSession关闭的时候就会清除缓存
	 *
	 *为什么spring-mybatis会手动关闭一级缓存
	 *        1.spring把所有的bean都放在spring ioc容器中,他并没有提供给用户 applicationContext
	 *	  所以用户无法拿到Mapper对象,更拿不到sqlSession,也就无法做到手动关闭
	 *	  mybatis提供了api使用户能随时关闭
	 *	  2.mybatis的一级缓存很鸡肋,因为一级缓存是存在每个线程中,每次的请求都是一个新的线程
	 *	  那么这个一级缓存可以说是完全没有用,但是如果他是进程级别的,就很牛b了,能节省大量的时间
	 */