文章目录
- 1.背景
- 2.常规在没有插件的情况下【MyBatis使用存在的问题】
- 3.MyBatis插件源码
- 4.MyBatis拦截的四大对象
- 4.1.Executor
- 4.1.1.方法:【update, query, flushStatements, commit, rollback, getTransaction, close, isClosed】
- 4.1.2.封装代理Executor的Code位置
- 4.1.3.使用Executor的Code位置
- 4.1.3.1.【SecondLevelDemo.testNotCommit】
- 4.1.3.2.【DefaultSqlSession.getMapper】
- 4.1.3.3.【Configuration.getMapper】
- 4.1.3.4.【MapperRegistry.getMapper】
- 4.1.3.5.【MapperProxyFactory.newInstance】
- 4.1.3.6.【SecondLevelDemo.testNotCommit】
- 4.1.3.7.【MapperProxy.invoke】
- 4.1.3.8.【MapperMethod.execute】
- 4.1.3.9.【DefaultSqlSession.selectOne】
- 4.1.3.10.【DefaultSqlSession.selectList】
- 4.1.3.11.【Plugin.invoke】
- 4.1.3.12.【Example01PageInterceptor.intercept】
- 4.2.StatementHandler
- 4.2.1.方法:【prepare, parameterize, batch, update, query】
- 4.2.2.封装StatementHandler代理的code位置
- 4.2.2.1.【SecondLevelDemo.testNotCommit】
- 4.2.2.2.【MapperProxy.invoke】
- 4.2.2.3.【MapperMethod.execute】
- 4.2.2.4.【DefaultSqlSession.selectOne】
- 4.2.2.5.【DefaultSqlSession.selectList】
- 4.2.2.7.【BaseExecutor.query】
- 4.2.2.8.【BaseExecutor.queryFromDatabase】
- 4.2.2.9.【SimpleExecutor.doQuery】
- 4.2.2.10.【Configuration.newStatementHandler】
- 4.2.3.使用SimpleHandler的Code位置
- 4.3.ParameterHandler
- 4.4.ResultSetHandler
1.背景
1.1.我们怎么去理解【插件】这个单词
1.大家应该都有笔记本电脑,不同的笔记电脑上或多或少的有一些插槽(例如下图);
我们都知道,这些插槽实际上是辅助或增强我们电脑的功能的;
比如:USB接口:我们可以使用USB接口连接鼠标,使得我们通过鼠标去控制屏幕的操作;
如果我们电脑自带的硬盘磁盘空间不足了,我们也可以通过使用USB接口,外接一个硬盘,满足硬盘不足的问题
网线接口:我们电脑本身可以使用无线网进行上网,同时我们也可以通过这个网线接口进行有线上网;
2.这些插口,我们都认为是插件;
1.2.总结
1.从上面的案例介绍,我们知道,插件其实本质上是增强了对象的功能,是不是有点动态代理的作用?
2.常规在没有插件的情况下【MyBatis使用存在的问题】
2.1.入参SQL组装无法自动组装问题
1.比如我们希望调用到MySql进行执行SQL的时候,可能会使用limit这个脚本,我们希望下面的start和end根据我们传递的值去动态的产生执行SQL;
select * from emp where id limit start,end;
默认情况下,mybatis是不支持的;
2.这个时候,我们就可以使用插件,在SQL执行之前,去对SQL进行处理,产生新的SQL,再使用新的SQL执行
2.2.在做数据库操作前后想做其他操作等等
1.如果我们希望在进行数据库操作的过程中,希望做一下其他的操作,这个过程可以是数据库操作前,也可以是数据库操作之后;
3.MyBatis插件源码
3.1.MyBatis插件本质【代理】
1.mybatis插件的本质实际是代理,下面我们开看一下插件类Plugin类;
3.2.Plugin
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.plugin;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.reflection.ExceptionUtil;
/**
* 参考 http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins
*
* 四大拦截对象:
* @see org.apache.ibatis.executor.Executor
* @see org.apache.ibatis.executor.statement.StatementHandler
* @see org.apache.ibatis.executor.parameter.ParameterHandler
* @see org.apache.ibatis.executor.resultset.ResultSetHandler
*
* @author Clinton Begin
*/
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//jdk 动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
/*
拦截:
这个地方通过 methods中是否包含改方法,判断是否需要拦截
*/
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
/**
*
* @param interceptor
* @return
*/
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
/**
* 这里Signature 就是Annotation注解中的内容
*/
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
/**
* signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>())
* 相当于:
* Object key= signatureMap.get(sig.type());
* if (key==null){
* key=new HashSet<>();
* signatureMap.put(sig.type(),key);
* }
*/
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
}
3.2.1.构造方法:【构造一个对象,后面invoke直接使用对象里内容】
3.2.1.1.【code】
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
3.2.1.2.【使用位置】
3.2.2.对被代理对象target进行包装,返回一个代理的对象Plugin【本质上jdk动态代理】
3.2.2.1.【code】
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
//jdk 动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
3.2.2.2.【使用位置】
Example01PageInterceptor为例
3.2.3.代理对象的调用:【拦截器的处理】【Invoke->intercept()方法】
3.2.3.1【code】
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
/*
拦截:
这个地方通过 methods中是否包含改方法,判断是否需要拦截
*/
if (methods != null && methods.contains(method)) {
//调用到拦截器中
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
3.2.3.2【使用位置】
1.通过上面wrap方法的封装,我们知道,代理对象是一个plguin的对象,所以一旦target指定的方法一旦被调用,便会先进入plugin.invoke方法
进而进入到intercept()方法
4.MyBatis拦截的四大对象
https://mybatis.org/mybatis-3/zh/configuration.html#plugins
4.1.Executor
4.1.1.方法:【update, query, flushStatements, commit, rollback, getTransaction, close, isClosed】
4.1.2.封装代理Executor的Code位置
4.1.2.1.【SecondLevelDemo.testNotCommit】
4.1.2.2.【DefaultSqlSessionFactory.openSession】
4.1.2.3.【DefaultSqlSessionFactory.openSessionFromDataSource】
4.1.2.4.【Configuration.newExecutor】
4.1.3.使用Executor的Code位置
4.1.3.1.【SecondLevelDemo.testNotCommit】
4.1.3.2.【DefaultSqlSession.getMapper】
4.1.3.3.【Configuration.getMapper】
4.1.3.4.【MapperRegistry.getMapper】
4.1.3.5.【MapperProxyFactory.newInstance】
最终返回的一个代理对象MapperProxy
4.1.3.6.【SecondLevelDemo.testNotCommit】
4.1.3.7.【MapperProxy.invoke】
4.1.3.8.【MapperMethod.execute】
4.1.3.9.【DefaultSqlSession.selectOne】
4.1.3.10.【DefaultSqlSession.selectList】
4.1.3.11.【Plugin.invoke】
4.1.3.12.【Example01PageInterceptor.intercept】
4.2.StatementHandler
4.2.1.方法:【prepare, parameterize, batch, update, query】
4.2.2.封装StatementHandler代理的code位置
4.2.2.1.【SecondLevelDemo.testNotCommit】
4.2.2.2.【MapperProxy.invoke】
4.2.2.3.【MapperMethod.execute】
4.2.2.4.【DefaultSqlSession.selectOne】
4.2.2.5.【DefaultSqlSession.selectList】
4.2.2.7.【BaseExecutor.query】
4.2.2.8.【BaseExecutor.queryFromDatabase】
我们先看最普通的SimpleExecutor
4.2.2.9.【SimpleExecutor.doQuery】
4.2.2.10.【Configuration.newStatementHandler】
4.2.3.使用SimpleHandler的Code位置
其实我们只要找handler.方法即可,当然还有其他的,我们这里不再赘述,标记