Mybatis苞⽶⾖源码分析⼆-⽅法执⾏
执⾏具体过程(集成到spring)
1. 到扫包类ClassPathMapperScanner,和以往的扫包形式⼀样,扫描包下所有类, 并获得BeanDefinition
2. 基于BeanDefinition,通过设置definition.setBeanClass,然后在spring 容器中通过getBean的⽅式获取Mapper对象(此时是基础对象下⾯要继续织⼊插件)
3. Mapper对象只有简单持有sqlSession来做数据库操作的能⼒, ⽽Mybatis提供了插件的功能, 就需要已实例化的Mapper对象进⾏再次代理, 将插件能⼒⽤⽅法拦截的⽅
式编织到进去(插件要编织代码具体执⾏位置依据实际情况)
Mybatis知识: 重重代理之后最终操作数据库执⾏链 Executor -> StatementHandler -> ute()-> ResultHander.handleResultSets(statement)
//spring-mybatis扫包注解实例化ClassPathMapperScanner 并设置所需属性值, 属spring 范畴,忽略掉,直接关注ClassPathMapperScanner
@MapperScan("dian.audit.dao.mapper")
public class MybatisPlusConfig {
忽略.......
}
查看最关⼼的doScan与processBeanDefinitions⽅法
ClassPathMapperScanner
@Override
public Set<BeanDefinitionHolder> basePackages) {
//使⽤spring⾃带扫包⽅式先得到所有定义类
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + String(basePackages) + "' package. Please check your configuration.");
} else {
// 具体Bean处理⽅法接着往下看
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
//实际是对定义类的⼀系列设置
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
忽略若⼲⾏......
/
/definition.setBeanClass 很眼熟的代码, 设置FactoryBean 后续spring容器通过FactoryBean的getObject()⽅法得到具体代理对象
//getObject的实现在 MapperFactoryBean中实现, 接下来所有的东西都是围绕MapperFactoryBean的getObject来实现的
definition.setBeanClass(Class());
忽略若⼲⾏.......
}
实例化Mapper对象
MapperFactoryBean
@Override
public T getObject() throws Exception {
//这⾥使⽤spring-mybatis集成使⽤的是getSqlSession()=SqlSessionTemplate
return getSqlSession().getMapper(this.mapperInterface);
}
委托SqlSessionTemplate来实例化Mapper
SqlSessionTemplate
@Override
public <T> T getMapper(Class<T> type) {
//使⽤mybatis configuration来实例化Mapper
return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
/
/获取sqlSessionFactory中的configuration
//在上⼀节MybatisPlusAutoConfiguration初始化过程中 sqlSessionFactory的configuration设置为苞⽶⾖重写类
return Configuration();
}
继续调⽤⽅法getMapper
MybatisConfiguration
@Override
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
Mapper(type, sqlSession);
}
MybatisMapperRegistry
@Override
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) (type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
}
try {
//在这⾥会通过代理实例化⼀个Mapper对象最终使⽤MapperProxy实例化代理对象
wInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
直接看invoke⽅法,Mapper每个⽅法都会被此⽅法代理执⾏, jdk代理⽅式不过多解释
MapperProxy
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.DeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//此处为执⾏⽅法类这个⽅法很重要, 后⾯查询时, 对于是否查询条⽬边界, 就在cachedMapperMethod⽅法中设置, 判断Mapper⽅法参数是否含有有RowBounds⼦类(举例:Page extends RowBounds)
final MapperMethod mapperMethod = cachedMapperMethod(method);
ute(sqlSession, args);
}
到这⾥ Mapper代理实例化⽅式和⽅法执⾏过程基本结束, 接下来看Mapper⽅法的具体执⾏, 接上⾯最后⼀⾏ ute(sqlSession, args) 以查询列表为例查看源码
MapperMethod
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = vertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = actRowBounds(args);
//通过看前⾯代码可只此处sqlSession的实现为Spring的 SqlSessionTemplate, 但其实SqlSessionTemplate并没有实现Sqlsession的功能,⽽是委托给Mybatis⾃带的DefaultSqlSession来完成操作
result = sqlSession.<E>Name(), param, rowBounds);
} else {
//通过看前⾯代码可只此处sqlSession的实现为Spring的 SqlSessionTemplate, 但其实SqlSessionTemplate并没有实现Sqlsession的功能,⽽是委托给Mybatis⾃带的DefaultSqlSession来完成操作
实例化bean的三种方式result = sqlSession.<E>Name(), param);
}
忽略.......
return result;
}
实际执⾏者为DefaultSqlSession, 接着往下看selectList
DefaultSqlSession
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//在上⼀节解析XML的时候已经将所有的statement设置到configuration 此处直接取⽤
MappedStatement ms = MappedStatement(statement);
//executor 有两个实现类BaseExecutor 和 cachedExecutor(⼆级缓存), 为了⽅便代码追踪, 不开⼆级缓存,使⽤BaseExecutor来执⾏
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();
}
}
我们更关注的是从数据库取数据的执⾏过程, 直接跳到queryFromDatabase⽅法
BaseExecutor
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
忽略缓存代码....
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
}
忽略缓存代码....
return list;
}
//下⾯这段代码为此篇⽂章最为重点的代码
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
//啥也没⼲不需要关⼼
flushStatements();
//获取苞⽶⾖Configuration
Configuration configuration = ms.getConfiguration();
//这⾥是mybatis插件核⼼所在⾮常重要直接进⼊⽅法
StatementHandler handler = wStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = StatementLog());
stmt = handler.prepare(connection, Timeout());
handler.parameterize(stmt);
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
实例化具体Statement执⾏器
Configuration
//实例化Statement执⾏器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //创建RoutingStatementHandler对象⽤于执⾏Statement, 其实从名字可以看出RoutingStatementHandler 并不做真正的处理, ⽽是将处理过程交给其他基础StatementHandler实现类
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//这⾥就是Mybatis的精髓所在了将创建的RoutingStatementHandler对象再次代理, 添加插件执⾏功能, 进⼊interceptorChain.pluginAll 看具体如何代理的
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
通过jdk代理⽅式植⼊插件调⽤链
InterceptorChain
//将所有的插件interceptors 通过代理⽅式植⼊到⽬标对象中
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
// 从下⾯这段代码可以简单看出,要么给target通过反射⽅式设置属性,要么通过jdk重新代理当前target, 这⾥根据⾃⼰需要实现(也即插件)
// 下⾯从苞⽶⾖分页插件(PaginationInterceptor)为例⼦来看怎么实现
target = interceptor.plugin(target);
}
return target;
}
分页插件织⼊
PaginationInterceptor
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
//具体包装交给Plugin⼯具类来做的直接看⼯具类wrap⽅法
return Plugin.wrap(target, this);
}
return target;
}
mybatis插件执⾏包装
Plugin
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = Class();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//这段代码表明代理⽅法实现在Plugin 直接进Plugin查看invoke⽅法
wProxyInstance(
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//获取当前类所有需要拦截的⽅法
Set<Method> methods = (DeclaringClass());
//若⽅法需要拦截则前置执⾏⽅法
if (methods != null && ains(method)) {
//具体的⽅法执⾏Invocation在intercept中执⾏, 当然这取决于是否真的需要执⾏
return interceptor.intercept(new Invocation(target, method, args));
}
//若⽅法不需要拦截则直接执⾏⽅法
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论