基于mybatis+spring
在我们写增删改查的时候,一般service依赖dao 写法是:
public class AService{
@Autowire
private BDao
//调用Bdo的方法
}
一般BDo是一个接口,这里就有两个问题 1.接口是怎么被实例化,注入进去的 2.接口是怎么调用方法的,这个接口的实现类是什么,有什么内容 走进spring源码
从一个注解开始 @MapperScan(basePackages = ‘com.xx.dao’)
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
.........
}
可以看到这个@MapperScan 注解其实是先当与实现了 @Improt 需要先了解下 @Import 的作用 详情见 tech.souyunku.com5f001f… 如果了解了@Import之后就知道,@MapperScan 这个注解的关键就是 MapperScannerRegistrar
MapperScannerRegistrar
MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//拿到@MapperScan注解,后面会通过这个注解拿到需要扫描的包
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//注册 MapperScannerConfigurer
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
//注册 MapperScannerConfigurer
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
//.... BeanDefinitionBuilder 对 属性的设置,忽略,不然篇幅太长了
//.... 主要是设置比如说 msqlSessionFactoryBeanName,basePackages 等属性
//注册MapperScannerConfigurer
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
MapperScannerConfigurer
MapperScannerConfigurer 是什么,为什么要先注册这个类
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
}
MapperScannerConfigurer 继承了 BeanDefinitionRegistryPostProcessor# 是一种bean工厂后置处理器,在初始化普通bean之前就会执行 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 所以往spring注册MapperScannerConfigurer这个类之后,spring还会从beandefinition的集合中获取未加载的BeanDefinitionRegistryPostProcessor然后进行实例化执行postProcessBeanDefinitionRegistry方法
MapperScannerConfigurer#postProcessBeanDefinitionRegistry
这个后置处理器做了什么?
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//.....
//ClassPathMapperScanner 顾名思义就是类路径扫描器,后面分析
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//..去掉扫描器各种属性填充
//开始进入扫描,basePackage这个就是我们在@MapperScan的包名
scanner.scan(
StringUtils.tokenizeToStringArray(this.,basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
ClassPathMapperScanner
可以看到ClassPathMapperScanner继承了spring的ClassPathBeanDefinitionScanner
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
}
//下面是spring的ClassPathBeanDefinitionScanner的方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
//开始扫描包 ,调用的是mybatis的 ClassPathMapperScanner
doScan(basePackages);
//........
//返回注册的bean数量
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
ClassPathMapperScanner的方法
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//调用spring方法扫描出包,解析成 BeanDefinitionHolder
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 {
//重点,开始对这些beanDefinition进行特殊处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//获取到 bean类名称
String beanClassName = definition.getBeanClassName();
//将bean类名称放到beanDefinition的构造方法里面,后面解析这个作用
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//设置beanDefinition的class 其实就是 MapperFactoryBean 一种FactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
//.... 设置各种属性值
if (!explicitFactoryUsed) {
//这蛮有意思的,设置自动装配的类型为 类型注入,假如有 setxx(xx xx)
//spring会去容器中找xx类,然后注入进去
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
//设置懒加载
definition.setLazyInit(lazyInitialization);
}
}
所以到这里,其实可以看出来,扫描包之后往spring容器注入的是Class为MapperFactoryBean 所以接着看 MapperFactoryBean
MapperFactoryBean
是什么: 一种FactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
//刚才有看到往这个构造方法塞一个bean类名称,就是塞到这里的,这里就是我们平常写的xxDao
//把这个类名称放到这里干嘛呢,做动态代理,具体往下走
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
先看下 MapperFactoryBean<T> extends SqlSessionDaoSupport
这个有什么用呢 SqlSessionDaoSupport
继承DaoSupport
,DaoSupport
又实现 InitializingBean
如果了解InitializingBean
这个类就会知道,如果实现了InitializingBean
就会在bean初始化的过程调用afterPropertiesSet
方法。看下 DaoSupport
的 afterPropertiesSet
做了什么
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
// Let abstract subclasses check their configuration.
//检查dao配置,看下具体实现
checkDaoConfig();
//..........
}
在MapperFactoryBean#checkDaoConfig
中有实现
protected void checkDaoConfig() {
//....
//获取mybatis 配置类
Configuration configuration = getSqlSession().getConfiguration();
//重点来了 往配置类添加mapper,这个mapper是最开始往BeanDefinition传来的
//真实的xxDao,看下这个做了什么事情
configuration.addMapper(this.mapperInterface);
}
public <T> void addMapper(Class<T> type) {
//将xxdao类放到 MapperProxyFactory 然后放到 knownMappers 这个缓存中
knownMappers.put(type, new MapperProxyFactory<>(type));
//往mapper注册器添加mapper,接着往下
mapperRegistry.addMapper(type);
}
public <T> void addMapper(Class<T> type) {
//去掉不影响主流程的.........
//解析xxxDao,主要是解析类
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
//主要逻辑,往下
parser.parse();
//..........
}
public void parse() {
//........去掉无关主流程代码
//获取xxxDao的所有方法
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
if (!method.isBridge()) {
//重要,往下走
parseStatement(method);
}
}
}
parsePendingMethods();
}
void parseStatement(Method method) {
//.....主要是根据方法名称去获取sql,然后将这些信息封装成
//MappedStatement 然后放到 configuration 中
assistant.addMappedStatement(...);
}
}
//最后是将 放到 configuration中的`mappedStatements`这个map
//后面如果执行方法的时候,直接从这个map就可以获得相应的sql等参数
configuration.addMappedStatement(statement);
所以 checkDaoConfig 这个方法一层套一层,最后是想把我们写的XXDao解析出来,以方法的维度,包含着方法名称,sql,出入参类型等等,然后放到configuration
的mappedStatements
接下来看更重要的方法。
FactoryBean
最重要的就是getObject
方法 往下看MapperFactoryBean
public T getObject() throws Exception {
//获取sqlsession,在spring环境中是 sqlSessionTemplate
//往下看 sqlSessionTemplate 的getMapper方法
return getSqlSession().getMapper(this.mapperInterface);
}
public <T> T getMapper(Class<T> type) {
//调用Configuration#getMapper,又开始一层套一层,往下看
return getConfiguration().getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//又是这个 mapperRegistry,往下走,注意这里的 sqlSession 还是 sqlSessionTemplate
return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//刚才在addMapper的时候就放进去 MapperProxyFactory 现在拿出来
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
//.....
try {
//可以看出来是返回代理类了,具体往下走
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public T newInstance(SqlSession sqlSession) {
// MapperProxy 真正的代理类
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
//使用 jdk 动态代理,就不进去看了
return newInstance(mapperProxy);
}
到这里为止算是将xxxDao以动态代理的形式交给spring容器管理
MapperProxy#invoke 做了什么
MapperProxy#invoke
是真正执行xxDao的方法,对xxDao方法进行代理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//...忽略对Object方法的执行
//从缓存中拿到 MapperMethod
//一般是new 一个 MapperMethod。MapperMethod里面的参数基本都是从
//Configuration 的 mappedStatements 获取,也就是addMappers的时候放进去
final MapperMethod mapperMethod = cachedMapperMethod(method);
//往下走看,这个方法做了什么
return mapperMethod.execute(sqlSession, args);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//...忽略各种判断是update,还是select,还是delete等,直接看seleOne
//因为流程都差不多
//往下走,这时候的 sqlSession 还是 SqlSessionTemplate
result = sqlSession.selectOne(command.getName(), param);
//......
return result;
}
public <T> T selectOne(String statement, Object parameter) {
//调用 sqlSessionProxy 的方法,一看就知道这个是sqlSession的代理类
//在SqlSessionTemplate 实例化方法进行代理的
//往下看代理类 SqlSessionInterceptor 的invoke
return this.sqlSessionProxy.selectOne(statement, parameter);
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取到真正干活的 sqlSession: DefaultSqlSession
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
//这个方法要注意不是我们xxxDao的方法,是上面那个selectOne方法
//执行对象是SqlSession,所以要去看 DefaultSqlSession#SelectOne
Object result = method.invoke(sqlSession, args);
//....
return result;
} catch (Throwable t) {
//.....
} finally {
if (sqlSession != null) {
//重要,所以每次mapper执行完方法,都会关闭session,这就导致了
//spring+mybatis mybatis的一级缓存失效
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
//调用 selecList往下看
List<T> list = this.selectList(statement, parameter);
//........
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从 configuration 获取 MappedStatement(包含着sql,出参入参,等等)
MappedStatement ms = configuration.getMappedStatement(statement);
//调用执行器,往下看
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//相当于获取sql
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//往下看
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//....
//往下看,顾名思义,从数据库获取数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
//.....
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//......
//往下走,模板方法,调用子类方法,SimpleExecutor
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
//....
return list;
}
快完了,做个标志 org.apache.ibatis.executor.SimpleExecutor#doQuery 下次接着写
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
未完待续………