说明:以下分析基于spring-framework-5.0.x,mybatis-spring-1.3.2,mybatis-3.4.6相关源码可自行去github下载或者maven依赖然后利用类似ideal工具自动关联源码功能。
我们知道spring对bean的管理,我们可以通过多种方式将bean添加进spring中进行管理其中包括:
- 一般注解@Conponent、@Controller、@Service
- @Import形式有三种:第一种用法:@Import({ 要导入的容器中的组件 } ):容器会自动注册这个组件,id默认是全类,第二种用法:ImportSelector:返回需要导入的组件的全类名数组,springboot底层用的特别多第三种用法:ImportBeanDefinitionRegistrar(如Mybatis和springboot整合时):手动注册bean到容器。
- 当然也可以通过BeanDefinitionRegistryPostProcessor(BeanFactoryPostProcessor子类)进行bean注册。通过它的重写方法参数可以拿到register进而进行bean的注册或者
- 使用beanRegister或者bean工厂
一:bean交由spring管理
mybatis通过@MapperScan标签完成对接口mapper的注入,我们依次点击@MapperScan->@Import(MapperScannerRegistrar.class)->MapperScannerRegistrar implements ImportBeanDefinitionRegistrar通过重写registerBeanDefinitions这个方法完成bean的注入。部分相关代码如下:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
...for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
...scanner.doScan(StringUtils.toStringArray(basePackages));
}
@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;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
...
// the mapper interface is the original class of the bean but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
}
二:MapperFactoryBean:MapperFactoryBean->SqlSessionDaoSupport->DaoSupport->InitializingBean(在bean实例化前执行checkDaoConfig方法)
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}//把接口类型传入factoryBean便于代理对象的产生
@Override
protected void checkDaoConfig() {
...
configuration.addMapper(this.mapperInterface)->mapperRegistry.addMapper(type)->parser.parse()(完成一些注解解析及sql生成等)
...
}
}
@Override
public T getObject() throws Exception {
//getSqlSession()->SqlSessionTemplate 在MapperFactoryBean实例化过程中,会首先根据mybatis-spring-boot-autoconfigure中META-INF/spring.factories中的内容获取到MybatisAutoConfiguration这个配置类类名。在后续的bean创建过 程中,MybatisAutoConfiguration类的对象会被创建成一个继承了FactoryBean的代理对象放到bean工厂中 MybatisAutoConfiguration中SqlSessionTemplate sqlSessionFactory两个类的的生成是@Bean注解来生成
return getSqlSession().getMapper(this.mapperInterface);->>>
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
return method.invoke(this, args);
...
return mapperMethod.execute(sqlSession, args);
}
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
...
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
...
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
...
result = sqlSession.<E>selectList(command.getName(), param);
...
}
//sqlSession为SqlSessionTemplate
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.<E> selectList(statement, parameter);//执行invoke方法
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
...
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);//mybatis结合spring时一级缓存失效原因
}
}
}
}