中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MyBatis攔截器的實現原理是什么及怎么使用

發布時間:2022-08-23 11:23:14 來源:億速云 閱讀:224 作者:iii 欄目:開發技術

本篇內容介紹了“MyBatis攔截器的實現原理是什么及怎么使用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    前言

    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}),})
    public class SqlValidateMybatisInterceptor extends PRSMybatisInterceptor {
     
        @Override
        protected Object before(Invocation invocation) throws Throwable {
            String sql="";
            Statement statement=(Statement) invocation.getArgs()[0];
            if(Proxy.isProxyClass(statement.getClass())){
                MetaObject metaObject= SystemMetaObject.forObject(statement);
                Object h=metaObject.getValue("h");
                if(h instanceof StatementLogger){
                    RoutingStatementHandler rsh=(RoutingStatementHandler) invocation.getTarget();
                    sql=rsh.getBoundSql().getSql();
                }else {
                    PreparedStatementLogger psl=(PreparedStatementLogger) h;
                    sql=psl.getPreparedStatement().toString();
                }
            }else{
                sql=statement.toString();
            }
            if(containsDelete(sql)&&!containsWhere(sql)){
                throw new SQLException("不能刪除整張表,sql:"+sql);
            }
            return null;
        }
     
        private boolean containsDelete(String sql){
            return sql.contains("delete")||sql.contains("DELETE");
        }
     
        private boolean containsWhere(String sql){
            return sql.contains("where")||sql.contains("WHERE");
        }
    }
    public class PRSMybatisInterceptor implements Interceptor {
     
        Boolean needBreak=false;
     
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            Object result= before(invocation);
            if(needBreak){
                return result;
            }
            result= invocation.proceed();
            result=after(result,invocation);
            return result;
        }
     
        protected Object before(Invocation invocation) throws Throwable{
            return null;
        }
        protected Object after(Object result,Invocation invocation) throws Throwable{
            return result;
        }
        @Override
        public Object plugin(Object o) {
            return Plugin.wrap(o, this);
        }
        @Override
        public void setProperties(Properties properties) {
        }
    }

    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

    private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        ...
        final Executor executor = configuration.newExecutor(tx, execType);
        return new DefaultSqlSession(configuration, executor, autoCommit);
    }
    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
      executorType = executorType == null ? defaultExecutorType : executorType;
      executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
      Executor executor;
      if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
      } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
      } else {
        executor = new SimpleExecutor(this, transaction);
      }
      if (cacheEnabled) {
        executor = new CachingExecutor(executor);
      }
      executor = (Executor) interceptorChain.pluginAll(executor);
      return executor;
    }

    Executor中要用StatementHandler執行sql語句,StatementHandler是調用configuration.newStatementHandler()方法創建的。

    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
     
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
      StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
      statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
      return statementHandler;
    }

    StatementHandler依賴 parameterHandler 和 resultSetHandler,在構造 StatementHandler 時會調用一下方法創建這兩個 handler。

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
      ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
      parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
      return parameterHandler;
    }
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
        ResultHandler resultHandler, BoundSql boundSql) {
      ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
      resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
      return resultSetHandler;
    }

    3.代理對象的創建

    3.1 攔截器的獲取

    從對象的創建過程中可以看出 代理 對象的創建時通過 InterceptorChain.pluginAll() 方法創建的。

    MyBatis攔截器的實現原理是什么及怎么使用

    查看 攔截器鏈 InterceptorChain 發現,其中的攔截器的添加是在 Configuration 中。因為攔截器被聲明為Bean了,所以在MyBatis初始化的時候,會掃描所有攔截器,添加到 InterceptorChain 中。

    3.2 代理對象的創建

    從上一步得知代理對象的創建是調用 Interceptor.pugin() 方法,然后調用 Plugin.wrap() 方法

    Interceptor
    @Override
    public Object plugin(Object o) {
        return Plugin.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攔截器的實現原理是什么及怎么使用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    株洲县| 青铜峡市| 霍邱县| 高雄县| 五常市| 招远市| 浦江县| 泰顺县| 双桥区| 怀远县| 三台县| 舟山市| 惠东县| 沾益县| 洛隆县| 阜新市| 永修县| 岳阳市| 清远市| 株洲市| 壤塘县| 射洪县| 屏东县| 宜城市| 盐亭县| 宁波市| 临沂市| 无锡市| 南和县| 福泉市| 湘乡市| 隆化县| 加查县| 江阴市| 和政县| 宜丰县| 苏州市| 中方县| 米脂县| 泰和县| 望城县|