<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Mybatis攔截器並不是每個物件裡面的方法都可以被攔截的。Mybatis攔截器只能攔截Executor、StatementHandler、ParameterHandler、ResultSetHandler四個類裡面的方法,這四個物件在建立的時候才會建立代理。
用途:實際工作中,可以使用Mybatis攔截器來做一些SQL許可權校驗、資料過濾、資料加密脫敏、SQL執行時間效能監控和告警等。
以在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的值是一個簽名陣列,簽名中包含要攔截的 類,方法和引數。
代理物件指的是:可以被攔截的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; }
從物件的建立過程中可以看出 代理 物件的建立時通過 InterceptorChain.pluginAll() 方法建立的。
檢視 攔截器鏈 InterceptorChain 發現,其中的攔截器的新增是在 Configuration 中。因為攔截器被宣告為Bean了,所以在MyBatis初始化的時候,會掃描所有攔截器,新增到 InterceptorChain 中。
從上一步得知代理物件的建立是呼叫 Interceptor.pugin() 方法,然後呼叫 Plugin.wrap() 方法
Interceptor @Override public Object plugin(Object o) { return Plugin.wrap(o, this); }
Plugin實現了 InvocationHandler 介面
在 Plugin.wrap() 方法中會獲取當前攔截器的介面,生成動態代理。
在動態代理中當代理物件呼叫方法時,會將方法的呼叫委託給 InvocationHandler,也就是 Plugin,
如下圖所示;
在該方法中 獲取攔截器簽名中的方法,如果包含當前方法,則呼叫攔截方法,否則執行原方法的呼叫。
攔截器的順序設定使用 Spring 中的 org.springframework.core.annotation.Order 註解設定。
order值大的攔截器先執行,order值大的在interceptors中越靠後,最後生成代理,所以先執行。
到此這篇關於MyBatis攔截器的實現原理的文章就介紹到這了,更多相關MyBatis攔截器 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45