Spring源码之整合Mybatis底层实现

目录

1. Spring整合Mybatis底层源码

2. SqlSessionTemplate类的作用

3. Mybatis一级缓存失效问题


1. Spring整合Mybatis底层源码

        Mybatis框架可以单独使用,需要用到Mybatis所提供的一些类构造出对应的Mapper对象,然后就能使用Mybatis框架提供的功能,我们先看一个Demo:

	@Test
	public void testMybatis() throws IOException {
        // 读取配置文件
		InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        // 解析配置文件,得到SqlSession工厂类
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 从工厂中获取SqlSession对象
		SqlSession sqlSession = sqlSessionFactory.openSession();
        // 然后从SqlSession中获取Mapper的代理对象
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		String result = mapper.selectById();
		sqlSession.commit();
		sqlSession.flushStatements();
		sqlSession.close();
	}

        这是一段很普通的Mybatis的代码,我们点进去看一下getMapper()方法,看一下Mybatis如何生成UserMapper代理对象,这个方法是SqlSession接口的方法,这个接口里面有很多我们平常使用的CRUD方法:

public interface SqlSession extends Closeable {
    <T> T selectOne(String var1);

    <T> T selectOne(String var1, Object var2);

    <E> List<E> selectList(String var1);

    <E> List<E> selectList(String var1, Object var2);

    <E> List<E> selectList(String var1, Object var2, RowBounds var3);

    <K, V> Map<K, V> selectMap(String var1, String var2);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);

    <T> Cursor<T> selectCursor(String var1);

    <T> Cursor<T> selectCursor(String var1, Object var2);

    <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);

    void select(String var1, Object var2, ResultHandler var3);

    void select(String var1, ResultHandler var2);

    void select(String var1, Object var2, RowBounds var3, ResultHandler var4);

    int insert(String var1);

    int insert(String var1, Object var2);

    int update(String var1);

    int update(String var1, Object var2);

    int delete(String var1);

    int delete(String var1, Object var2);

    void commit();

    void commit(boolean var1);

    void rollback();

    void rollback(boolean var1);

    List<BatchResult> flushStatements();

    void close();

    void clearCache();

    Configuration getConfiguration();

    <T> T getMapper(Class<T> var1);

    Connection getConnection();
}

        主要看它默认的实现类DefaultSqlSession的方法:

        所属类:org.apache.ibatis.session.defaults.DefaultSqlSession

    public <T> T getMapper(Class<T> type) {
        return this.configuration.getMapper(type, this);
    }

         所属类:org.apache.ibatis.session.Configuration

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return this.mapperRegistry.getMapper(type, sqlSession);
    }

        所属类:org.apache.ibatis.binding.MapperRegistry

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

        mapperProxyFactory.newInstance(sqlSession)就会返回代理对象:

        所属类:org.apache.ibatis.binding.MapperProxyFactory

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

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

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

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

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

        把mapper传进去,通过JDK的动态代理生成代理对象。我们可以看一下MapperProxy类,它实现了InvocationHandler接口。

public class MapperProxy<T> implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

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

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }

        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

        当我们得到代理对象,去执行Mapper接口里面的方法时,Mybatis就会进入MapperProxy类的invoke()方法,得到方法上面的注解(如:@Select("select 'user'")),然后去执行目标方法。

        所有MapperProxy类很重要,上面这些生成代理逻辑,都是Mybatis提供的。

        回到Spring,如果Mybatis要和Spring整合,除了Mybatis自带的jar包之外,还需整合的jar包:

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        为什么呢?举个例子:

@Component
public class UserService {

	@Autowired
	private UserMapper userMapper;

	public void test() {
		System.out.println(userMapper.selectById());
	}

}

        上面的代码貌似没问题,但是如果没有整合jar包,就会报找不到userMapper这个Bean的错误。

        我们知道,Spring在Bean的实例化中进行属性注入时,找不到userMapper,因为在过程中需要把Mybatis生成的UserMapper代理对象注入到这里,赋值该属性,才能进行后续的操作,Spring和其他框架整合亦如此。会把Mybatis创建的类(或代理对象)注册到Spring容器,Spring在用的时候才能找到。

        这个章节要说的就是如何把Mybatis生成userMapper变成Spring所需的Bean,但是UserMapper是接口,所以不能用固有的思路通过Spring上下文去getBean(),接口是无法实例化的。

        答案就是利用FactoryBean接口,例如:

/**
 * @author Kieasar
 */
@Component
public class BaecFactoryBean implements FactoryBean { 


	@Override
	public Object getObject() {
		// 参数中把类加载器传进去,然后new一个UserMapper,生成代理对象
		Object newInstance = Proxy.newProxyInstance(BaecFactoryBean.class.getClassLoader(), new Class[]{UserMapper.class}, new InvocationHandler() {
			@Override
			public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
				System.out.println("执行代理对象的方法");
				return null;
			}
		});
		return newInstance;
	}

	// 把生成的代理对象的类型返回
	@Override
	public Class<?> getObjectType() {
		return UserMapper.class;
	}
}

        此时,这个Bean对象所对应的Bean就是getObject()方法返回的代理对象(newInstance),接下来,单元测试一下:

	@Test
	public void main() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AppConfig.class);
		context.refresh();

		UserService userService = (UserService) context.getBean("userService");
		userService.test();
	}

        执行结果没有报错,说明属性被赋值了,很明显,因为通过属性去找,能找到上面BaecFactoryBean生成的代理对象。

        但是test()方法打印结果却为null,因为此时执行的是代理对象的invoke()方法。

        上面的例子,貌似我们实现了Mybatis的功能,但是有个问题,此时只有一个UserMapper,如果再有OrderMapper或其他很多的类需要注入,难道我们要写很多个FactoryBean吗?所以,当当前的思路是不行滴~

        于是,又想到,代码不要写死,这样不就可以了嘛:

/**
 * @author Kieasar
 */
@Component
public class BaecFactoryBean implements FactoryBean { 

	private Class mapperInterface;

	public BaecFactoryBean(Class mapperInterface) {
		this.mapperInterface = mapperInterface;
	}

	@Autowired
	public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
		sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
		this.sqlSession = sqlSessionFactory.openSession();
	}

	@Override
	public Object getObject() {
		Object newInstance = Proxy.newProxyInstance(BaecFactoryBean.class.getClassLoader(), new Class[]{mapperInterface}, new InvocationHandler() {
			@Override
			public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
				System.out.println("执行代理对象的方法");
				return null;
			}
		});
		return newInstance;
	}

	// 把生成的代理对象的类型返回
	@Override
	public Class<?> getObjectType() {
		return mapperInterface;
	}
}

        但是,@Component注解修饰的类,注入的时候是单例的,支持生成一个代理对象,不能生成我们需要的若干个Bean,但是,这样可以呀~

	@Test
	public void main() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AppConfig.class);
		context.refresh();

		AbstractBeanDefinition userMapperBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		userMapperBeanDefinition.setBeanClass(BaecFactoryBean.class);
		userMapperBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
		// 得到一个userMapper代理对象
		context.registerBeanDefinition("userMapper",userMapperBeanDefinition);

		AbstractBeanDefinition orederMapperBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		orederMapperBeanDefinition.setBeanClass(BaecFactoryBean.class);
		orederMapperBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
		// 得到一个orderMapper代理对象
		context.registerBeanDefinition("orderMapper",orederMapperBeanDefinition);

		UserService userServcie = (UserService) context.getBean("userServcie");
		userServcie.test();
	}

        确实可以,但是如果有很多的类需要注入呢……

        用扫描的方式,则需要一个扫描器,继承ClassPathBeanDefinitionScanner类,就有了扫描器的功能。

public class BaecBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

	public BaecBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		super(registry);
	}

	@Override
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);

		for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
			BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
			
			// 往构造方法的入参中传值的话,传的就是这个mapper解析之后的BeanDefinition的名称
			beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
			beanDefinition.setBeanClassName(BaecFactoryBean.class.getName());
		}
		return beanDefinitionHolders;
	}

	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return beanDefinition.getMetadata().isInterface();
	}
}

        但是Spring不关心接口,Mybatis只关心接口,所以,还需要加工一下,重写isCandidateComponent()方法,如果是接口,返回true,只拦截接口,不拦截类,如上。

        扫描哪儿呢?还是注解方便,自定义一个注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(BaecBeanDefinitionRegistrar.class)
public @interface BaecMapperScan {

	String value();
}

        并且需要把BaecBeanDefinitionRegistrar类导入进来,这个类用来注册BeanDefinition:

public class BaecBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

	// 参数AnnotationMetadata就是注解的元数据信息
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        // 获取注解信息
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(BaecMapperScan.class.getName());
		// 获取扫描路径
        String path = (String) annotationAttributes.get("value");
		// 引入扫描器
        BaecBeanDefinitionScanner scanner = new BaecBeanDefinitionScanner(registry);
		// ClassPathBeanDefinitionScanner扫描器默认只扫描@Component注解的类
		// 所以,重写IncludeFilter()方法,让它扫描所有的类
        scanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(@NotNull MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory){
                return true;
            }
        });
		// 扫描路径
        scanner.scan(path);
    }
}

        我们定义的BaecBeanDefinitionScanner继承自ClassPathBeanDefinitionScanner扫描器,默认只扫描@Component注解的类,可以看一下ClassPathBeanDefinitionScanner的扫描过程,doScan()方法主要会调到这里:

所属类:springframework.context.annotation.ClassPathScanningCandidateComponentProvider

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 获取basePackage下所以的文件资源
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			// 根据路径获取资源,class文件的file对象
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						// 元数据读取器
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						// excludeFilters includeFilters过滤器判断
						if (isCandidateComponent(metadataReader)) {
							// 构造BeanDefiniiton
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);
							// 又判断一次,符合条件才加入
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}

      ----省略无关代码---- 

         这个方法isCandidateComponent()就是用来判断有没有@Component注解:

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		// 排除过滤器,返回false
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		// 符合includeFilters的才会进行条件匹配,通过了才是Bean,也就是先看有没有@Component,再看是否符合@Conditianal
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

        所以,UserMapper和OrderMapper要想被扫描到,就重写IncludeFilter()方法,让它扫描所有的类。

   scanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(@NotNull MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory){
                return true;
            }
        });

        但是自定义的BaecFactoryBean是自己实现代理对象,要整合Mybatis,就需要Mybatis生成得代理对象,顺着这个思路,换成MyBatis的。

@Component
public class BaecFactoryBean implements FactoryBean {

    private Class mapperInterface;

    private SqlSession sqlSession;

    public BaecFactoryBean(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    // 通过注入SqlSessionFactory拿到SqlSession
    @Autowired
    public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
        sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
        this.sqlSession = sqlSessionFactory.openSession();
    }

    @Override
    public Object getObject() {
        return sqlSession.getMapper(mapperInterface);
    }

    // 把生成的代理对象的类型返回
    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }
}

       那么,只要Spring能拿到SqlSessionFactory这个Bean,就能取到SqlSession对象,如何拿呢?这就需要程序员自己实现了:

@ComponentScan("com.baec")
@BaecMapperScan("com.baec.mapper")
public class AppConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }

}

        当然,也需要提前把Mybatis的配置文件mybatis.xml准备好。

        另外,如果不使用Spring的注解,有什么办法注入SqlSession呢?

public class BaecBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

	public BaecBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		super(registry);
	}

	@Override
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);

		for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
			GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();

			// 往构造方法的入参中传值的话,传的就是这个mapper解析之后的BeanDefinition的名称
			beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
			beanDefinition.setBeanClassName(BaecFactoryBean.class.getName());
			// 自动找到BaecFactoryBean类里面的set方法,然后根据类型去找Bean
			beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		}
		return beanDefinitionHolders;
	}

	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return beanDefinition.getMetadata().isInterface();
	}
}

        把BeanDefinition强转成GenericBeanDefinition,位置AutowiredMode属性为按类型注入,Spring会自动找到BaecFactoryBean类里面的set方法,然后根据类型去找Bean。

        然后,上面的BaecFactoryBean就可以修改为:

public class BaecFactoryBean implements FactoryBean {

    private Class mapperInterface;

    private SqlSession sqlSession;

    public BaecFactoryBean(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public void setSqlSession(SqlSessionFactory sqlSessionFactory) {
        sqlSessionFactory.getConfiguration().addMapper(mapperInterface);
        this.sqlSession = sqlSessionFactory.openSession();
    }

    @Override
    public Object getObject() {
        return sqlSession.getMapper(mapperInterface);
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }
}

        没有使用Spring的注解,但是实现了注入。

        接下来,我们看一下Mybatis的原理,从@MapperScan注解入手:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
public @interface MapperScan {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends Annotation> annotationClass() default Annotation.class;

    Class<?> markerInterface() default Class.class;

    String sqlSessionTemplateRef() default "";

    String sqlSessionFactoryRef() default "";

    Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}

        它也是导入了MapperScannerRegistrar类,该类也是实现了ImportBeanDefinitionRegistrar接口:

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    public MapperScannerRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        if (this.resourceLoader != null) {
            scanner.setResourceLoader(this.resourceLoader);
        }

        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((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass));
        }

        Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
        if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
            scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass));
        }

        scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
        scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
        List<String> basePackages = new ArrayList();
        String[] var10 = annoAttrs.getStringArray("value");
        int var11 = var10.length;

        int var12;
        String pkg;
        for(var12 = 0; var12 < var11; ++var12) {
            pkg = var10[var12];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        var10 = annoAttrs.getStringArray("basePackages");
        var11 = var10.length;

        for(var12 = 0; var12 < var11; ++var12) {
            pkg = var10[var12];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        Class[] var14 = annoAttrs.getClassArray("basePackageClasses");
        var11 = var14.length;

        for(var12 = 0; var12 < var11; ++var12) {
            Class<?> clazz = var14[var12];
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(basePackages));
    }

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

        在这里面,Mybatis同样创建了一个扫描器ClassPathMapperScanner,扫描@MapperScan注解,毋庸置疑,该类也是继承自Spring的扫描器——ClassPathBeanDefinitionScanner类,在它的doScan方法中的processBeanDefinitions()中,同样也设置了AutowireMode属性为:

beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        接下来,再看一下ClassPathMapperScanner中创建的MapperFactoryBean,是把Mybatis生成代理对象转化为Bean最关键的一环,这个类也实现了FactoryBean接口:

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    private Class<T> mapperInterface;
    private boolean addToConfig = true;

    public MapperFactoryBean() {
    }

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

    protected void checkDaoConfig() {
        super.checkDaoConfig();
        Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
        Configuration configuration = this.getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
            try {
                configuration.addMapper(this.mapperInterface);
            } catch (Exception var6) {
                this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
                throw new IllegalArgumentException(var6);
            } finally {
                ErrorContext.instance().reset();
            }
        }

    }

    public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }

    public Class<T> getObjectType() {
        return this.mapperInterface;
    }

    public boolean isSingleton() {
        return true;
    }

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

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

    public void setAddToConfig(boolean addToConfig) {
        this.addToConfig = addToConfig;
    }

    public boolean isAddToConfig() {
        return this.addToConfig;
    }
}

        getObject()方法中也是通过getSqlSession().getMapper(this.mapperInterface)拿到的,属性mapperInterface也是通过MapperFactoryBean类的构造方法注入的。

        而getSqlSession()方法在它的父类SqlSessionDaoSupport中定义了,和上面的例子一样。

        我们回到文章一开头的代码,这句代码就相当于开了一个事务。

        // 从工厂中获取SqlSession对象
		SqlSession sqlSession = sqlSessionFactory.openSession();

        它与Mybatis的一级缓存和二级缓存有关,但是,如果一级缓存打开了,第二次去执行同样的sql时就不会去请求数据库了,因为SqlSession会缓存第一次执行sql的结果,会执行MapperProxy的invoke()方法:

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }

            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
        
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        // 主要执行这里
        return mapperMethod.execute(this.sqlSession, args);
    }

        当执行某个方法时,会判断方法是@Select、@Insert等注解,以@Select为例,最终会调用

 public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                // 最终会调用到这里
                result = sqlSession.selectOne(this.command.getName(), param);
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        执行到result = sqlSession.selectOne(this.command.getName(), param);拿到了sql和方法的参数。

2. SqlSessionTemplate类的作用

        在MapperFactoryBean的父类SqlSessionDaoSupport中的SqlSession其实是SqlSessionTemplate类:

this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);

        我们用到的UserMapper是通过SqlSessionTemplate.getMapper()返回的代理对象,那这个SqlSessionTemplate是什么东东呢?

        先看它的构造函数:

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
    }

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
    }

        传入一个SqlSessionFactory ,最终通过JDK的动态代理生成一个SqlSession的代理对象,并赋值给sqlSessionProxy属性。

        再看它的getMapper()方法:

    public <T> T getMapper(Class<T> type) {
        return this.getConfiguration().getMapper(type, this);
    }

        参数传的是类型和this,去生成代理对象。mapper.selectById()调用的就是SqlSessionTemplate.selectOne()方法:

    public <T> T selectOne(String statement, Object parameter) {
        return this.sqlSessionProxy.selectOne(statement, parameter);
    }

        就是调用的sqlSessionProxy就是构造函数生成的代理对象的selectOne()方法。

        此时还没有真正去执行sql,执行sql是DefaultSqlSession对象,执行sql最终会进入SqlSessionInterceptor类,它是构造方法中生成动态代理导进去的,调用到它的invoke():

    private class SqlSessionInterceptor implements InvocationHandler {
        private SqlSessionInterceptor() {
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                // 执行sql就是在这
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }

        SqlSessionUtils.getSqlSession()方法就是从Session工厂中得到DefaultSqlSession:

        所属类:org.mybatis.spring.SqlSessionUtils

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
        // 通过Spring的事务管理器,从ThreadLocal中是否有SqlSession
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        SqlSession session = sessionHolder(executorType, holder);
        // 有则返回
        if (session != null) {
            return session;
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Creating a new SqlSession");
            }

            // 没有,则从DefaultSqlSessionFactory工厂类中创建一个新的DefaultSqlSession
            session = sessionFactory.openSession(executorType);
            registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
            return session;
        }
    }

        总之,调用流程就是SqlSessionTemplate.selectOne()---->SqlSessionProxy.selectOne()--->DefaultSqlSession.selectOne()。这儿抛出两个问题:

        SqlSessionTemplate的作用是什么?

       因为DefaultSqlSession是线程不安全的,没有锁和同步方法,当不同线程同时调用DefaultSqlSession时,DefaultSqlSession确是同一个,存在并发安全问题。

        SqlSessionTemplate就是用来解决线程安全这个问题,但该类里面的方法却没有加锁,所以只要保证每个线程有单独的DefaultSqlSession,一个线程执行sql时是同一个DefaultSqlSession就可以了,如何做的?

        通过ThreadLocal,每个线程执行之前,先看ThreadLoca中是否有DefaultSqlSession,有直接拿来用,没有则创建。

        上面SqlSessionUtils.getSqlSession()方法中的TransactionSynchronizationManager.getResource(),就是Spring的事务,底层就是ThreadLocal:

	private static Object doGetResource(Object actualKey) {
		// resources是ThreadLocal包装的Map,用来缓存资源的,比如缓存当前线程中由某个DataSource所创建的数据库连接
		Map<Object, Object> map = resources.get();
		if (map == null) {
			return null;
		}

		// 获取DataSource对象所对应的数据库连接对象
		Object value = map.get(actualKey);
		// Transparently remove ResourceHolder that was marked as void...
		if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
			map.remove(actualKey);
			// Remove entire ThreadLocal if empty...
			if (map.isEmpty()) {
				resources.remove();
			}
			value = null;
		}
		return value;
	}

        检查ThreadLocal中是否有SqlSession,返回的是SqlSession的包装类SqlSessionHolder,有则从SqlSessionHolder中取出,没有,则从DefultSqlSession工厂类中创建一个新的:

SqlSessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

        然后执行registerSessionHolder()方法,把DefultSqlSession包装成SqlSessionHolder,存到ThreadLocal。

 private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            Environment environment = sessionFactory.getConfiguration().getEnvironment();
            if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
                }

                SqlSessionHolder holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
                // 通过事务管理器,存到ThreadLocal
                TransactionSynchronizationManager.bindResource(sessionFactory, holder);
                TransactionSynchronizationManager.registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
                holder.setSynchronizedWithTransaction(true);
                holder.requested();

3. Mybatis一级缓存失效问题

        为什么Spring和Mybatis整合之后一级缓存会失效?

        在上面的源码中,注意有个条件TransactionSynchronizationManager.isSynchronizationActive(),是否开启了Spring事务,如果为true才去执行后面的逻辑。

        不符合带来的结果就是,一个线程去执行不同的sql时,发现ThreadLocal中没有SqlSession,于是每次都去创建,导致一级缓存失效,因为一级缓存的运行机制就是同一个SqlSession执行同一个sql时,返回之前缓存的结果。

        如何解决?就是方法上加@Transactional注解,就会使用Spring的事务管理器建立的数据库连接,Mybatis如何拿到事务管理器的连接呢?就是prepareStatement()方法:

        所属类:org.apache.ibatis.executor.SimpleExecutor

    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        // 获取数据库连接
        Connection connection = this.getConnection(statementLog);
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
    }

         进而调用到org.apache.ibatis.executor.BaseExecutor.getConnection()方法:

    protected Connection getConnection(Log statementLog) throws SQLException {
        Connection connection = this.transaction.getConnection();
        return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
    }

        这个Transaction是Mybatis的事务管理接口,包括事务的提交、回滚,及数据库连接的获取。

        会先在DefaultSqlSessionFactory.openSessionFromDataSource()方法中通过TransactionFactory创建的,进而创建了Executor执行器。

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        try {
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            Executor executor = this.configuration.newExecutor(tx, execType);
            return new DefaultSqlSession(this.configuration, executor, autoCommit);

      ----省略无关代码---- 
    }

        Spring整合Mybatis后得到的是SpringManagedTransaction类,它继承自Transaction接口:

public class SpringManagedTransaction implements Transaction {
    private static final Log LOGGER = LogFactory.getLog(SpringManagedTransaction.class);
    private final DataSource dataSource;
    private Connection connection;
    private boolean isConnectionTransactional;
    private boolean autoCommit;

    public SpringManagedTransaction(DataSource dataSource) {
        Assert.notNull(dataSource, "No DataSource specified");
        this.dataSource = dataSource;
    }

    public Connection getConnection() throws SQLException {
        if (this.connection == null) {
            this.openConnection();
        }
        return this.connection;
    }

    private void openConnection() throws SQLException {
        this.connection = DataSourceUtils.getConnection(this.dataSource);
        this.autoCommit = this.connection.getAutoCommit();
        this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
    }

    public void commit() throws SQLException {
        if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
            this.connection.commit();
        }
    }

    public void rollback() throws SQLException {
        if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
            this.connection.rollback();
        }
    }

    public void close() throws SQLException {
        DataSourceUtils.releaseConnection(this.connection, this.dataSource);
    }

    public Integer getTimeout() throws SQLException {
        ConnectionHolder holder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.dataSource);
        return holder != null && holder.hasTimeout() ? holder.getTimeToLiveInSeconds() : null;
    }
}

        openConnection()中的DataSourceUtils.getConnection(this.dataSource)就可以拿到由Spring事务管理器创建的数据库连接。

        如果方法没有开启事务,那么在执行sql时候,每个sql有自己的SqlSession对象来执行。

        如果开启了Spring事务,就是多个sql属于同一个事务,那应该用一个SqlSession来执行多个sql。所以,在没有开启Spring事务的时候,SqlSession的一级缓存并不是失效了,而是存在的生命周期太短了(执行完一个sql后就被销毁了,下一个sql执行时又是一个新的SqlSession)。 

        Spring为什么要这样设计?解决线程安全问题,一般情况下不建议使用Mybatis的一级缓存,如果使用,就会涉及到事务的隔离级别,假如设置的隔离级别是读未提交,那么同一条sql查到的结果可能是一样的(从缓存中拿的),把隔离界别忽略了。那数据库的隔离级别重要还是Mybatis的一级缓存重要?肯定是数据库的隔离级别重要。

        如果非要使用Mybatis的一级缓存,提供另外一种方法,用@Bean创建一个SqlSession的Bean:

@ComponentScan("com.baec")
@BaecMapperScan("com.baec.mapper")
public class AppConfig {

    @Autowired
    public SqlSessionFactory sqlSessionFactory() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }

    @Bean
    public SqlSession sqlSession() throws IOException {
        return sqlSessionFactory().openSession();
    }

        这样,在Service中就可以直接使用了:

@Component
public class UserService {

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private SqlSession sqlSession;

	public void test() {
		sqlSession.selectOne("cn.kieasar.mybatis.mapper.UserMapper.selectById");
		sqlSession.selectOne("cn.kieasar.mybatis.mapper.UserMapper.selectById");
		sqlSession.selectOne("cn.kieasar.mybatis.mapper.UserMapper.selectById");
    }
}

        这样一来,这三个Sql执行使用的SqlSession 就是同一个了,而且是同一个线程。

        如果加了@MapperScan注解,会扫描、注入BeanDefinition,Mapper的代理对象Bean,即MapperFactoryBean<T>,MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport又继承了DaoSupport,而DaoSupport类实现了InitializingBean接口,所以各种Mapper创建时会执行afterPropertiesSet()方法:

public abstract class DaoSupport implements InitializingBean {

	/** Logger available to subclasses. */
	protected final Log logger = LogFactory.getLog(getClass());


	@Override
	public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
		// Let abstract subclasses check their configuration.
		checkDaoConfig();

		// Let concrete implementations initialize themselves.
		try {
			initDao();
		}
		catch (Exception ex) {
			throw new BeanInitializationException("Initialization of DAO failed", ex);
		}
	}

        初始化的时候首先会调用checkDaoConfig(),检查SqlSession是否为空,会调到这里:

public abstract class SqlSessionDaoSupport extends DaoSupport {
    private SqlSession sqlSession;
    private boolean externalSqlSession;

    public SqlSessionDaoSupport() {
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
            this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
    }

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
    }

    public SqlSession getSqlSession() {
        return this.sqlSession;
    }

    protected void checkDaoConfig() {
        Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
    }
}

        接下来,会进入到子类MapperFactoryBean.checkDaoConfig():

    protected void checkDaoConfig() {
        super.checkDaoConfig();
        Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
        Configuration configuration = this.getSqlSession().getConfiguration();
        if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
            try {
                // 把泛型Class<T>的mapperInterface添加到Mapper中
                configuration.addMapper(this.mapperInterface);
            } catch (Exception var6) {
                this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
                throw new IllegalArgumentException(var6);
            } finally {
                ErrorContext.instance().reset();
            }
        }

    }

        把泛型Class<T>的mapperInterface添加到Mapper中。

        另外,如果不想写@MapperScan注解,还有一种方式可以扫描注入Mybatis生成的代理对象Bean,整合Mybatis:

@ComponentScan("com.baec")
public class AppConfig {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        // 指定扫描路径
        configurer.setBasePackage("com.baec.mapper");
        return configurer;
    }

	@Bean
	public JdbcTemplate jdbcTemplate() {
		return new JdbcTemplate(dataSource());
	}

	@Bean
	public DataSource dataSource() {
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/tuling?characterEncoding=utf-8&amp;useSSL=false");
		dataSource.setUsername("root");
		dataSource.setPassword("Zhouyu123456***");
		return dataSource;
	}

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

}

        SqlSessionFactory类实现了InitializingBean和FactoryBean接口,它的getObject()方法调用了afterPropertiesSet()方法:

    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }
        return this.sqlSessionFactory;
    }
    public void afterPropertiesSet() throws Exception {
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

        buildSqlSessionFactory()方法中会构建出Mybatis的核心配置类Configuration,在这里面创建了Spring的事务管理器SpringManagedTransactionFactory工厂,通过该类可以拿到SpringManagedTransaction。

        MapperScannerConfigurer实现了BenDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry()方法中创建了扫描器,实现了扫描BeanDefiniiton的功能:

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }

        最后,我们总结一下Spring整合Mybatis底层源码执行流程:

  1. 通过@MapperScan导入了MapperScannerRegistrar类;
  2. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions()方法;
  3. registerBeanDefinitions()方法中定义了ClassPathMapperScanner对象,用来扫描Mapper;
  4. 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中不会扫描接口(因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会扫描@Component注解的类);
  5. 扫描接口并且得到对应的BeanDefinition;
  6. 把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType;
  7.  扫描完成后,Spring就会基于BeanDefinition去创建Bean,相当于每个Mapper对应一个FactoryBean;
  8. 在MapperFactoryBean中的getObject()方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean;
  9. SqlSession对象属于Mybatis,SqlSession对象需要SqlSessionFactory来产生;
  10. MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory(),一个setSqlSessionTemplate(),而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的Bean或者SqlSessionTemplate类型的Bean;
  11. 如果你定义的是一个SqlSessionFactory类型的Bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性;
  12. 而在SqlSessionTemplate类中就存在一个getMapper()方法,这个方法会产生一个Mapper接口代理对象;
  13. 当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程,执行流程看下图:

猜你喜欢

转载自blog.csdn.net/qq_40213907/article/details/121047234