Springboot集成tkMybatis源码分析

前言

本文从源码层面介绍springboot如何集成tk Mybatis,至于如何配置,本文不做介绍。

本文适用于使用过tk mybatis,但是不清楚其机制的同学

什么是tk Mybatis

tk Mybatis是对Mybatis封装的一个框架,有点类似于hibernate的jpa,其作用是可以简化增删改查的sql语句,以及可以支持不使用mapper.xml配置文件,并且在接口上也可以支持编写sql语句等功能

源码

我们直接从入口开始,tk Mybatis的扫描注解MapperScan,该注解使用时需要配置扫描的路径,参数为value

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

可以看到使用了@Import导入了一个MapperScannerRegistrar,@Import接口属于springboot的知识,可以理解为会加载该类,该类实现了ImportBeanDefinitionRegistrar接口,所有springboot启动时会调用他的registerBeanDefinitions方法

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    public static final Logger LOGGER = LoggerFactory.getLogger(MapperScannerRegistrar.class);

    private ResourceLoader resourceLoader;

    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 核心代码1
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        ......
		// 核心代码2
        List<String> basePackages = new ArrayList<String>();
        for (String pkg : annoAttrs.getStringArray("value")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        for (String pkg : annoAttrs.getStringArray("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }
        ......
        scanner.registerFilters();
        // 核心代码3
        scanner.doScan(StringUtils.toStringArray(basePackages));
    }
.....
}

这里不是核心的代码就删掉了,避免看的混乱,只分析核心代码,核心代码1和2即获取MapperScan配置的value值,拿到要扫描的包路径

核心代码3,即扫描该包路径,注册bean,核心代码3的源码

@Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    	// 核心代码1
        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 {
            // 核心代码2
            processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

核心代码1,调用super.doScan,super.doScan是spring的扫描方法,当前没有调用setAnnotationClass方法设置需要扫描的注解,所以就是扫描出该包路径下所有的类,最后会在spring的BeanDefinition的map容器中注册BeanDefinition的信息,key是bean的名称,value是封装的BeanDefinition

beanDefinitionMap 定义:
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);

核心代码2

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (GenericBeanDefinition) holder.getBeanDefinition();

            if (logger.isDebugEnabled()) {
                logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
                        + "' and '" + definition.getBeanClassName() + "' mapperInterface");
            }

            // the mapper interface is the original class of the bean
            // but, the actual class of the bean is MapperFactoryBean
            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
            // 核心代码1
            definition.setBeanClass(this.mapperFactoryBean.getClass());
            ......
        }
    }

这个方法是给BeanDefinitionHolder增加了很多额外的属性,在后面会用到,其中比较重要的是核心代码1处setBeanClass,设置的beanClass为class tk.mybatis.spring.mapper.MapperFactoryBean

然后扫描工作就完成了,在spring的beanDefinitionMap 容器中注册了所有bean的信息,这里存储的是所有bean的信息,不止mybatis的bean

然后就要涉及到spring中的bean的创建了,在bean创建时会调用AbstractBeanFactory的doGetBean,结果层层调用来到了AbstractAutowireCapableBeanFactory的getSingletonFactoryBeanForTypeCheck方法,这里可以打断点看看,调用链太长,此处无法分析

private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
		synchronized (getSingletonMutex()) {
			BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
			......
				if (instance == null) {
					// 核心代码1
					bw = createBeanInstance(beanName, mbd, null);
					instance = bw.getWrappedInstance();
				}
			}
			finally {
				// Finished partial creation of this bean.
				afterSingletonCreation(beanName);
			}

			FactoryBean<?> fb = getFactoryBean(beanName, instance);
			if (bw != null) {
				this.factoryBeanInstanceCache.put(beanName, bw);
			}
			return fb;
		}
	}

核心代码1,调用createBeanInstance获得了一个bw,createBeanInstance会根据我们之前的BeanDefinition的beanClass创建一个MapperFactoryBean实例,并且由bw包装,调用bw.getWrappedInstance()后获得了一个MapperFactoryBean实例,方法里面代码太长,这里就不贴了

然后来到了依赖注入,当我们注入我们的Mybatis接口时,会调用spring的AbstractAutowireCapableBeanFactory的doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			// 核心代码1
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}
		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			// 核心代码2
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
}

核心代码1,从spring的factoryBeanInstanceCache里面获取beanName对应的实例,这里可以获得一个BeanWrapper,BeanWrapper里面就是包装了我们需要的MapperFactoryBean实例

核心代码2

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// 核心代码3
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

核心代码3

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				// 核心代码1
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		......
	}

核心代码1,调用bean的afterPropertiesSet方法,MapperFactoryBean最终继承了DaoSupport类,DaoSupport实现了InitializingBean,所以会调用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,这里使用的是模板方法模式,所以会调用到MapperFactoryBean的checkDaoConfig

  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
      	// 核心代码
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

核心代码,调用了configuration.addMapper,这个属于Mybatis的核心代码了

public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }

继续跟

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 {
      	// 核心代码
        knownMappers.put(type, new MapperProxyFactory<>(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);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

核心代码,在knownMappers里面put了接口和MapperProxyFactory的对应关系,然后这里就结束了

依赖注入时注入的Mapper对象是通过MapperFactoryBean调用getObject得到的,所以我们查看getObject方法

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

然后调用SqlSessionTemplate的getMapper

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

然后调用到Configuration的getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	// 核心代码1
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) 
    knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 核心代码2
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

核心代码1,从knownMappers 获取我们之前put进去的对象,也就是MapperProxyFactory

核心代码2,调用MapperProxyFactory的newInstance,这里就可以看出来,这个newInstance和我们设计模式的动态代理非常像

查看MapperProxyFactory类,果然是使用了jdk的动态代理模式

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 mapperInterface;
  }

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

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

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

}

也就是调用我们的mapper接口时会使用动态代理,最终调用到MapperProxy的invoke方法。

 @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 核心代码
    return mapperMethod.execute(sqlSession, args);
  }

接下去的代码全部都是mybatis核心类库的代码,主要是调用mapperMethod.execute执行增删改查,我们就不细看了。

至此我们的整体流程是可以梳理清楚的

总结

从上文可以看出,tk mybatis里面整合spring时,用到了大量spring的扩展机制,以及BeanDefinition、FactoryBean等都属于spring的类库,如果对spring不熟悉的话,看起来还是比较费解的。

最后再梳理下整体流程:

  1. 提供MapperScan配置扫描路径,并且依赖其注解@Import来注册BeanDefinition到spring的beanDefinitionMap容器中
  2. 把我们mapper接口对应的bean包装为MapperFactoryBean,即工厂bean
  3. MapperFactoryBean继承了DaoSupport类,DaoSupport实现了InitializingBean,所以会调用afterPropertiesSet,通过DaoSupport的afterPropertiesSet的模板模式方法checkDaoConfig调用到MapperFactoryBean的checkDaoConfig,然后在mybatis自己的Configuration对象中注册了mapper信息
  4. 注入时调用的是MapperFactoryBean的getObject,从而到mybatis的Configuration中根据接口获取了实例MapperProxyFactory,并且调用newInstance获取了一个真正的实例对象MapperProxy
  5. MapperProxy是一个动态代理类,所以调用时也就是调用了MapperProxy的invoke方法,然后在invoke中调用mybatis自己的execute进行增删改查

以上就是tk mybatis整合到springboot时的一些流程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值