MyBatis拦截器的实现原理是什么及怎么使用
导读:本文共4598.5字符,通常情况下阅读需要15分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要:希望大家仔细阅读,能够学有所成!前言Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler四个类里面的方法,这四个对象在创建的时候才会创建代理。用途:实际工作中,可以使用Mybatis拦截器来做一些SQL权限校验、数据过滤、数据加密脱敏、SQL执行... ...
目录
(为您整理了一些要点),点击可以直达。希望大家仔细阅读,能够学有所成!
前言
Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler四个类里面的方法,这四个对象在创建的时候才会创建代理。
用途:实际工作中,可以使用Mybatis拦截器来做一些SQL权限校验、数据过滤、数据加密脱敏、SQL执行时间性能监控和告警等。
1.使用方法
以在Spring中创建StatementHandler.update()方法的拦截器为例:
@Component
@Order(1)
@Intercepts({@Signature(type=StatementHandler.class,method="update",args={Statement.class}),})
publicclassSqlValidateMybatisInterceptorextendsPRSMybatisInterceptor{@Override
protectedObjectbefore(Invocationinvocation)throwsThrowable{
Stringsql="";
Statementstatement=(Statement)invocation.getArgs()[0];
if(Proxy.isProxyClass(statement.getClass())){
MetaObjectmetaObject=SystemMetaObject.forObject(statement);
Objecth=metaObject.getValue("h");
if(hinstanceofStatementLogger){
RoutingStatementHandlerrsh=(RoutingStatementHandler)invocation.getTarget();
sql=rsh.getBoundSql().getSql();
}else{
PreparedStatementLoggerpsl=(PreparedStatementLogger)h;
sql=psl.getPreparedStatement().toString();
}
}else{
sql=statement.toString();
}
if(containsDelete(sql)&&!containsWhere(sql)){
thrownewSQLException("不能删除整张表,sql:"+sql);
}
returnnull;
}privatebooleancontainsDelete(Stringsql){
returnsql.contains("delete")||sql.contains("DELETE");
}privatebooleancontainsWhere(Stringsql){
returnsql.contains("where")||sql.contains("WHERE");
}
}
publicclassPRSMybatisInterceptorimplementsInterceptor{BooleanneedBreak=false;
@Override
publicObjectintercept(Invocationinvocation)throwsThrowable{
Objectresult=before(invocation);
if(needBreak){
returnresult;
}
result=invocation.proceed();
result=after(result,invocation);
returnresult;
}protectedObjectbefore(Invocationinvocation)throwsThrowable{
returnnull;
}
protectedObjectafter(Objectresult,Invocationinvocation)throwsThrowable{
returnresult;
}
@Override
publicObjectplugin(Objecto){
returnPlugin.wrap(o,this);
}
@Override
publicvoidsetProperties(Propertiesproperties){
}
}
1. 自定义拦截器 实现 org.apache.ibatis.plugin.Interceptor 接口与其中的方法。在plugin方法中需要返回return Plugin.wrap(o, this)。在intercept方法中可以实现拦截的业务逻辑,改方法的 参数 Invocation中有原始调用的 对象,方法和参数,可以对其任意处理。
2. 在自定义的拦截器上添加需要拦截的对象和方法,通过注解 org.apache.ibatis.plugin.Intercepts 添加。如示例代码所示:
Intercepts的值是一个签名数组,签名中包含要拦截的 类,方法和参数。
2.MyBatis对象的创建
代理对象指的是:可以被拦截的4个类的实例。
代理对象创建时需要解析拦截器,从而利用JDK动态代理将拦截器的逻辑织入原始对象。
DefaultSqlSession中依赖Executor,如果新建的时候会创建executor
privateSqlSessionopenSessionFromConnection(ExecutorTypeexecType,Connectionconnection){
...
finalExecutorexecutor=configuration.newExecutor(tx,execType);
returnnewDefaultSqlSession(configuration,executor,autoCommit);
}
publicExecutornewExecutor(Transactiontransaction,ExecutorTypeexecutorType){
executorType=executorType==null?defaultExecutorType:executorType;
executorType=executorType==null?ExecutorType.SIMPLE:executorType;
Executorexecutor;
if(ExecutorType.BATCH==executorType){
executor=newBatchExecutor(this,transaction);
}elseif(ExecutorType.REUSE==executorType){
executor=newReuseExecutor(this,transaction);
}else{
executor=newSimpleExecutor(this,transaction);
}
if(cacheEnabled){
executor=newCachingExecutor(executor);
}
executor=(Executor)interceptorChain.pluginAll(executor);
returnexecutor;
}
Executor中要用StatementHandler执行sql语句,StatementHandler是调用configuration.newStatementHandler()方法创建的。
StatementHandlerhandler=configuration.newStatementHandler(wrapper,ms,parameterObject,rowBounds,resultHandler,boundSql);publicStatementHandlernewStatementHandler(Executorexecutor,MappedStatementmappedStatement,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql){
StatementHandlerstatementHandler=newRoutingStatementHandler(executor,mappedStatement,parameterObject,rowBounds,resultHandler,boundSql);
statementHandler=(StatementHandler)interceptorChain.pluginAll(statementHandler);
returnstatementHandler;
}
StatementHandler依赖 parameterHandler和resultSetHandler,在构造 StatementHandler 时会调用一下方法创建这两个 handler。
this.parameterHandler=configuration.newParameterHandler(mappedStatement,parameterObject,boundSql);
publicParameterHandlernewParameterHandler(MappedStatementmappedStatement,ObjectparameterObject,BoundSqlboundSql){
ParameterHandlerparameterHandler=mappedStatement.getLang().createParameterHandler(mappedStatement,parameterObject,boundSql);
parameterHandler=(ParameterHandler)interceptorChain.pluginAll(parameterHandler);
returnparameterHandler;
}
this.resultSetHandler=configuration.newResultSetHandler(executor,mappedStatement,rowBounds,parameterHandler,resultHandler,boundSql);
publicResultSetHandlernewResultSetHandler(Executorexecutor,MappedStatementmappedStatement,RowBoundsrowBounds,ParameterHandlerparameterHandler,
ResultHandlerresultHandler,BoundSqlboundSql){
ResultSetHandlerresultSetHandler=newDefaultResultSetHandler(executor,mappedStatement,parameterHandler,resultHandler,boundSql,rowBounds);
resultSetHandler=(ResultSetHandler)interceptorChain.pluginAll(resultSetHandler);
returnresultSetHandler;
}
3.代理对象的创建
3.1 拦截器的获取
从对象的创建过程中可以看出 代理 对象的创建时通过 InterceptorChain.pluginAll() 方法创建的。
查看 拦截器链InterceptorChain 发现,其中的拦截器的添加是在 Configuration 中。因为拦截器被声明为Bean了,所以在MyBatis初始化的时候,会扫描所有拦截器,添加到 InterceptorChain 中。
3.2 代理对象的创建
从上一步得知代理对象的创建是调用 Interceptor.pugin() 方法,然后调用 Plugin.wrap() 方法
Interceptor
@Override
publicObjectplugin(Objecto){
returnPlugin.wrap(o,this);
}
Plugin实现了 InvocationHandler 接口
在 Plugin.wrap() 方法中会获取当前拦截器的接口,生成动态代理。
4. 拦截器的执行过程
在动态代理中当代理对象调用方法时,会将方法的调用委托给 InvocationHandler,也就是 Plugin,
如下图所示;
在该方法中获取拦截器签名中的方法,如果包含当前方法,则调用拦截方法,否则执行原方法的调用。
5. 拦截器的执行顺序
拦截器的顺序配置使用 Spring 中的org.springframework.core.annotation.Order 注解配置。
order值大的拦截器先执行,order值大的在interceptors中越靠后,最后生成代理,所以先执行。
MyBatis拦截器的实现原理是什么及怎么使用的详细内容,希望对您有所帮助,信息来源于网络。