前言
开心一刻
中韩两学生辩论.
中: 端午节是属于谁的?
韩: 韩国人!
中: 汉字是谁发明的?
韩: 韩国人!
中: 中医是属于谁的?
韩: 韩国人!
中: 那中国人到底发明过什么?
韩: 韩国人!
前情回顾
Mybatis 源码解析 - mapper 代理对象的生成, 你有想过吗, 我们讲到了 mybatis 操作数据库的流程: 先创建 SqlSessionFactory, 然后创建 SqlSession, 然后再创建获取 mapper 代理对象, 最后利用 mapper 代理对象完成数据库的操作; Mapper 代理对象的创建, 利用的是 JDK 的动态代理, InvocationHandler 是 MapperProxy, 后续 Mapper 代理对象方法的执行都会先经过 MapperProxy 的 invoke 方法.
但是, 此时 SqlSessionFactory 的创建, SqlSession 的创建以及 mapper 代理对象的获取都是我们手动操作的, 实际应用中, mybatis 往往也不会单独使用, 绝大多数都是集成在 spring 中, 也就是说我们无需手动去管理 mybatis 相关对象的生命周期, 全部都由 spring 容器统一管理, 那么 spring 是什么时候在哪创建的 mybatis 的相关对象的呢? 尤其是 mapper 代理对象 MapperProxy 的创建
Springboot 集成 mybatis
当 springboot(其实还是 spring)集成 mybatis 后, mybatis 的对象是交给 spring 容器管理的, 只会实例化一次, 然后伴随着 spring 容器一直存在, 直到 spring 容器销毁
自动配置: MybatisAutoConfiguration
Mybatis 的自动配置类: MybatisAutoConfiguration, 至于如何加载此类, 可参考: spring-boot-2.0.3 不一样系列之源码篇 - springboot 源码一, 绝对有值得你看的地方
MybatisAutoConfiguration 会被当做配置类被 spring 解析, 我们来看看 spring 容器会从此配置类中解析到什么
创建了 SqlSessionFactory 实例(实际类型: DefaultSqlSessionFactory), 并注册到了 spring 容器; 此时我们应该还注意到
@Import({ AutoConfiguredMapperScannerRegistrar.class })
AutoConfiguredMapperScannerRegistrar 继承了 ImportBeanDefinitionRegistrar(注意看类注释, 有兴许的可以更深入的研究下), 那么它的 registerBeanDefinitions 也会被调用
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- logger.debug("Searching for mappers annotated with @Mapper");
- ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
- try {
- if (this.resourceLoader != null) {
- scanner.setResourceLoader(this.resourceLoader);
- }
- // 获取启动类所在的包, 如: com.lee.shiro, 会作为扫描开始的 base package, 一般只会有一个, 但支持多个
- List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
- if (logger.isDebugEnabled()) {
- for (String pkg : packages) {
- logger.debug("Using auto-configuration base package'{}'", pkg);
- }
- }
- scanner.setAnnotationClass(Mapper.class); // 设置扫谁, Mapper 注解是被扫描对象
- scanner.registerFilters();
- scanner.doScan(StringUtils.toStringArray(packages)); // 扫描所有 mapper, 进行 bean 定义处理
- } catch (IllegalStateException ex) {
- logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
- }
- }
- View Code
以我们启动类所在的包 (com.lee.shiro) 为基包, 扫描所有的 mapper, 然后修改所有 mapper 在 spring 容器中的 bean 定义, 将 mapper 的 beanClass 全部指向了 MapperFactoryBean
mapper 代理对象的创建: MapperFactoryBean
MapperFactoryBean 继承 SqlSessionDaoSupport,SqlSessionDaoSupport 有两个方法用来设置 SqlSession
- public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
- if (!this.externalSqlSession) {
- this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
- }
- }
- public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
- this.sqlSession = sqlSessionTemplate;
- this.externalSqlSession = true;
- }
- View Code
可以看到 SqlSession 的实际类型是: SqlSessionTemplate,SqlSessionTemplate 在 MybatisAutoConfiguration 以 @Bean 方式创建的
Spring 在创建 Service 实例: UserServiceImpl 的时候, 发现依赖 mapper(可能还有其他的实例依赖 mapper), 那么就会去 spring 容器获取 mapper 实例, 没有则进行创建, 然后注入进来(依赖注入); 具体创建过程如下
- if (mbd.isSingleton()) {
- sharedInstance = getSingleton(beanName, () -> {
- try {
- // 创建 mapper 对象, beanName:com.lee.shiro.mapper.UserMapper, 创建出来的实例实际上是 MapperFactoryBean 类型
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- });
- // 获取给定 bean 实例的对象, 如果是 FactoryBean, 则获取 bean 实例本身或其创建的对象
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- View Code
因为 Spring 在 mapper 扫描的时候, 将所有 mapper bean 定义中的 beanClass 设置成了 MapperFactoryBean(继承了 FactoryBean), 所以通过 createBean 方法创建的 mapper 实例实际上是 MapperFactoryBean 对象, 然后通过
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- protected Object getObjectForBeanInstance(
- Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
- // Don't let calling code try to dereference the factory if the bean isn't a factory.
- // isFactoryDereference 方法判断 name 中是否有 & 字符
- if (BeanFactoryUtils.isFactoryDereference(name)) {
- if (beanInstance instanceof NullBean) {
- return beanInstance;
- }
- if (!(beanInstance instanceof FactoryBean)) {
- throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
- }
- }
- // Now we have the bean instance, which may be a normal bean or a FactoryBean.
- // If it's a FactoryBean, we use it to create a bean instance, unless the
- // caller actually wants a reference to the factory.
- // 此时的 beanInstance 可能是一个普通 bean, 也可能是一个 FactoryBean
- // 如果是一个 FactoryBean, 那么就用它创建想要的 bean 实例
- // 此 if 表示, 如果 beanInstance 是普通 bean, 或者本来就想要 FactoryBean 实例, 则直接返回 beanInstance
- if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
- return beanInstance;
- }
- Object object = null;
- if (mbd == null) {
- object = getCachedObjectForFactoryBean(beanName);
- }
- // 此时表明 beanInstance 是一个 FactoryBean, 并且不是想要 FactoryBean 实例
- if (object == null) {
- // Return bean instance from factory.
- FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
- // Caches object obtained from FactoryBean if it is a singleton.
- if (mbd == null && containsBeanDefinition(beanName)) {
- mbd = getMergedLocalBeanDefinition(beanName);
- }
- boolean synthetic = (mbd != null && mbd.isSynthetic());
- // 通过 FactoryBean 实例创建我们想要的实例
- object = getObjectFromFactoryBean(factory, beanName, !synthetic);
- }
- return object;
- }
- View Code
获取真正想要的 bean 实例, 如果 beanInstance 是普通 bean, 或者本来就想要 FactoryBean 实例 (beanName 中有 &), 那么直接返回 beanInstance, 否则用 FactoryBean 实例来创建我们想要的实例对象. 说回来就是会调用 MapperFactoryBean 的 getObject() 方法来获取 Mapper 的代理对象
后续流程就可以参考: Mybatis 源码解析 - mapper 代理对象的生成, 你有想过吗
至此, 前情回顾中的问题也就清晰了
总结
1, 自动配置的过程中, spring 会扫描所有的 mapper, 并将所有 mapper bean 定义中的 beanClass 指向 MapperFactoryBean;
2, 创建 mapper 实例的时候, 根据 bean 定义创建的实例实际上是 MapperFactoryBean 实例, 然后再利用 MapperFactoryBean 获取 mapper 实例(调用 MapperFactoryBean 的 getObject 方法, mybatis 会利用 jdk 的动态代理创建 mapper 代理对象);
3, 对比 Mybatis 源码解析 - mapper 代理对象的生成, 你有想过吗, 其实就是将我们手动创建的过程通过自动配置, 将创建过程交给了 spring;
4, 关于 spring 的 FactoryBean, 有时间再重新开一篇给大家讲讲
来源: https://www.cnblogs.com/youzhibing/p/10486307.html