mybatis拦截器
阅读数:124 评论数:0
跳转到新版页面分类
python/Java
正文
一、概述
1、mybatis执行流程
(1)Configuration
mybatis所有的配置信息都维持在Configuration对象中,比如:插件、映射器、DataSource等。
(2)SqlSession
作为mybatis工作的主要顶层API,表示和数据库交互会话
(3)Executor
mybatis执行器,是mybatis调度的核心,负责SQL语句的生成和查询缓存的维护。
(4)StatementHandler
封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换成List集合。
(5)ParameterHandler
负责对用户传递的参数转换成JDBC Statement所需要的参数。
(6)ResultSetHandler
负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。
(7)TypeHandler
负责Java数据类型和jdbc数据类型之间的映射和转换。
(8)MappedStatement
一条SQL对应一个Statement.
2、mybatis拦截器
又称mybatis插件,采用责任链模式+动态代理模式实现拦截的功能,通过这些插件可以改变mybatis的默认行为(诸如SQL重写之类的),可以实现在mybatis的整个运行流程中的某些指定位置进行拦截,mybatis支持对Executor、ParameterHandler、ResultSetHandler和StatementHandler接口进行拦截。
//对执行器类型进行拦截,括号内表示是可以拦截的方法
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
//参数处理时进行拦截
ParameterHandler (getParameterObject, setParameters)
//处理结果集,封装Java对象时进行拦截
ResultSetHandler (handleResultSets, handleOutputParameters)
//编译statement时进行拦截
StatementHandler (prepare, parameterize, batch, update, query)
(1)Executor接口及其方法
可以在这里实现自定义二级缓存。
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement var1, Object var2) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
boolean isCached(MappedStatement var1, CacheKey var2);
void clearLocalCache();
void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
Transaction getTransaction();
void close(boolean var1);
boolean isClosed();
void setExecutorWrapper(Executor var1);
}
(2)ResultSetHandler
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement var1) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
void handleOutputParameters(CallableStatement var1) throws SQLException;
}
(3)StatementHandler
public interface StatementHandler {
Statement prepare(Connection var1, Integer var2) throws SQLException;
void parameterize(Statement var1) throws SQLException;
void batch(Statement var1) throws SQLException;
int update(Statement var1) throws SQLException;
<E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;
<E> Cursor<E> queryCursor(Statement var1) throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
3、mybatis拦截器链
InterceptorChain对象是拦截器执行链对象,里面维护了Mybatis配置的所有拦截器(Interceptor)对象。启动mybatis时,初始化配置文件的时候就把所有的拦截器添加到拦截器链中。
SqlSessionFactoryBean初始化配置文件时添加拦截器。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
public void afterPropertiesSet() throws Exception {
this.sqlSessionFactory = this.buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
// 省略
//查看是否注入拦截器,有的话添加到Interceptor集合里面
if (!ObjectUtils.isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach((plugin) -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> {
return "Registered plugin: '" + plugin + "'";
});
});
}
//省略
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
同样在使用配置文件时,XMLConfigBuilder解析mybatis全局配置文件的pluginElement方法时也会将拦截器添加到拦截器链对象中。
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
InterceptorChain是Configuration的内部属性。
public class InterceptorChain {
//所有插件对象
private final List<Interceptor> interceptors = new ArrayList();
public InterceptorChain() {
}
public Object pluginAll(Object target) {
Interceptor interceptor;
//循环调用每个拦截器的plugin方法,返回的对象调用下一个拦截器的plugin
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
this.interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(this.interceptors);
}
}
拦截器链中的pluginAll方法,就是执行所有拦截器的plugin方法。拦截器的plugin方法,可以在自定义拦截器的时候自我实现,但是Mybatis已经为我们提供了一个Plugin的类实现,通过静态方法wrap(Object target,Interceptor interceptor)可以决定要返回的对象是目标对象还是对应的代理。
//实现InvocationHandler接口,重写invoke方法,代理对象执行时会调用invoke方法
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
//提供warp方法,目标实例target(这个target就是之前所说的MyBatis拦截器可以拦截的类,Executor,ParameterHandler,ResultSetHandler,StatementHandler)和它的父类们
public static Object wrap(Object target, Interceptor interceptor) {
//获取插件的Intercepts注解拦截器类名和方法信息
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
//获取要改变行为的类(ParameterHandler|ResultSetHandler|StatementHandler|Executor)
Class<?> type = target.getClass();
//获取signatureMap中含有target实现的接口数组
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
//根据是否有4个接口类型决定返回代理对象还是原目标对象
return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
}
//如果当前执行的方法是定义好的需要拦截的方法,则把目标对象、要执行的方法以及方法参数封装成一个Invocation对象,
//再把封装好的Invocation作为参数传递给当前拦截器的intercept方法。如果不需要拦截,则直接调用当前的方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//获取拦截器中配置的所有方法
Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());
//当拦截器中方法包含当前执行的方法,则调用拦截器的intercept方法,否则执行原方法
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
二、@Interceptor接口
mybatis中使用拦截器非常简单,只要实现Interceptor接口并通过@Intercepts和@Signature注解指定要拦截的接口和方法即可。
public interface Interceptor {
//要进行拦截的时候要执行的方法。
Object intercept(Invocation invocation) throws Throwable;
//用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,根据是否要进行拦截决定要返回一个什么样的目标对象,官方提供了示例:return Plugin.wrap(target, this)返回一个代理对象
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
//在Mybatis进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置。
void setProperties(Properties properties);
}
1、@Intercepts和@Signature注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Intercepts {
Signature[] value();
}
它的值是一个@Signature数组,表示当前的对象是一个Interceptor,而@Signature则表明要拦截的接口、方法以及对应的参数类型。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
Class<?> type();
String method();
Class<?>[] args();
}