前言
本文从源码层面介绍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不熟悉的话,看起来还是比较费解的。
最后再梳理下整体流程:
- 提供MapperScan配置扫描路径,并且依赖其注解@Import来注册BeanDefinition到spring的beanDefinitionMap容器中
- 把我们mapper接口对应的bean包装为MapperFactoryBean,即工厂bean
- MapperFactoryBean继承了DaoSupport类,DaoSupport实现了InitializingBean,所以会调用afterPropertiesSet,通过DaoSupport的afterPropertiesSet的模板模式方法checkDaoConfig调用到MapperFactoryBean的checkDaoConfig,然后在mybatis自己的Configuration对象中注册了mapper信息
- 注入时调用的是MapperFactoryBean的getObject,从而到mybatis的Configuration中根据接口获取了实例MapperProxyFactory,并且调用newInstance获取了一个真正的实例对象MapperProxy
- MapperProxy是一个动态代理类,所以调用时也就是调用了MapperProxy的invoke方法,然后在invoke中调用mybatis自己的execute进行增删改查
以上就是tk mybatis整合到springboot时的一些流程