MyBatis拦截器的实现原理是什么及怎么使用(mybatis,开发技术)

时间:2024-05-09 09:33:41 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

希望大家仔细阅读,能够学有所成!

    前言

    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依赖 parameterHandlerresultSetHandler,在构造 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() 方法创建的。

    MyBatis拦截器的实现原理是什么及怎么使用

    查看 拦截器链InterceptorChain 发现,其中的拦截器的添加是在 Configuration 中。因为拦截器被声明为Bean了,所以在MyBatis初始化的时候,会扫描所有拦截器,添加到 InterceptorChain 中。

    3.2 代理对象的创建

    从上一步得知代理对象的创建是调用 Interceptor.pugin() 方法,然后调用 Plugin.wrap() 方法

    Interceptor
    @Override
    publicObjectplugin(Objecto){
    returnPlugin.wrap(o,this);
    }

    Plugin实现了 InvocationHandler 接口

    MyBatis拦截器的实现原理是什么及怎么使用

    Plugin.wrap() 方法中会获取当前拦截器的接口,生成动态代理。

    4. 拦截器的执行过程

    MyBatis拦截器的实现原理是什么及怎么使用

    在动态代理中当代理对象调用方法时,会将方法的调用委托给 InvocationHandler,也就是 Plugin,

    如下图所示;

    MyBatis拦截器的实现原理是什么及怎么使用

    在该方法中获取拦截器签名中的方法,如果包含当前方法,则调用拦截方法,否则执行原方法的调用。

    5. 拦截器的执行顺序

    拦截器的顺序配置使用 Spring 中的org.springframework.core.annotation.Order 注解配置。

    order值大的拦截器先执行,order值大的在interceptors中越靠后,最后生成代理,所以先执行。

    MyBatis拦截器的实现原理是什么及怎么使用

    MyBatis拦截器的实现原理是什么及怎么使用

    本文:MyBatis拦截器的实现原理是什么及怎么使用的详细内容,希望对您有所帮助,信息来源于网络。
    上一篇:深入理解令牌认证机制(token)下一篇:

    27 人围观 / 0 条评论 ↓快速评论↓

    (必须)

    (必须,保密)

    阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18