日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

mybatis源码阅读(八) ---Interceptor了解一下

發(fā)布時間:2023/12/3 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis源码阅读(八) ---Interceptor了解一下 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自??mybatis源碼閱讀(八) ---Interceptor了解一下?

1 Intercetor

MyBatis 允許你在已映射語句執(zhí)行過程中的某一點進行攔截調(diào)用。默認(rèn)情況下,MyBatis允許使用插件來攔截的方法調(diào)用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 攔截執(zhí)行器的方法
ParameterHandler (getParameterObject, setParameters) 攔截參數(shù)的處理
ResultSetHandler (handleResultSets, handleOutputParameters) 攔截結(jié)果集的處理
StatementHandler (prepare, parameterize, batch, update, query) 攔截Sql語法構(gòu)建的處理

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler; }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; }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; }public Executor newExecutor(Transaction transaction) {return newExecutor(transaction, defaultExecutorType); }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; }

InterceptorChain里保存了所有的攔截器,它在mybatis初始化的時候創(chuàng)建。上面interceptorChain.pluginAll(executor)的含義是調(diào)用攔截器鏈里的每個攔截器依次對executor進行plugin(插入攔截)代碼如下

public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<Interceptor>();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}public void addInterceptor(Interceptor interceptor) {interceptors.add(interceptor);}public List<Interceptor> getInterceptors() {return Collections.unmodifiableList(interceptors);}}

Interceptor 結(jié)構(gòu)

public interface Interceptor {Object intercept(Invocation invocation) throws Throwable;Object plugin(Object target);void setProperties(Properties properties);}

2.自定義攔截器

官方源碼例子

@Intercepts({@Signature(type = Executor.class, method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class ExamplePlugin implements Interceptor {private Properties properties;@Overridepublic Object intercept(Invocation invocation) throws Throwable {return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {this.properties = properties;}public Properties getProperties() {return properties;}}

mybatis-config.xml配置

<plugins><plugin interceptor="org.lpf.interceptor.ExamplePlugin"></plugin> </plugins>

每一個攔截器都必須實現(xiàn)上面的三個方法,其中:

Object intercept(Invocation invocation)是實現(xiàn)攔截邏輯的地方,內(nèi)部要通過invocation.proceed()顯式地推進責(zé)任鏈前進,也就是調(diào)用下一個攔截器攔截目標(biāo)方法。

Object plugin(Object target)就是用當(dāng)前這個攔截器生成對目標(biāo)target的代理,實際是通過Plugin.wrap(target,this)來完成的,把目標(biāo)target和攔截器this傳給了包裝函數(shù)。

setProperties(Properties properties)用于設(shè)置額外的參數(shù),參數(shù)配置在攔截器的Properties節(jié)點里。

注解里描述的是指定攔截方法的簽名 [type,method,args] (即對哪種對象的哪種方法進行攔截),它在攔截前用于決斷。

定義自己的Interceptor最重要的是要實現(xiàn)plugin方法和intercept方法,在plugin方法中我們可以決定是否要進行攔截進而決定要返回一個什么樣的目標(biāo)對象。而intercept方法就是要進行攔截的時候要執(zhí)行的方法。

對于plugin方法而言,其實Mybatis已經(jīng)為我們提供了一個實現(xiàn)。Mybatis中有一個叫做Plugin的類,里面有一個靜態(tài)方法wrap(Object target,Interceptor interceptor),通過該方法可以決定要返回的對象是目標(biāo)對象還是對應(yīng)的代理。這里我們先來看一下Plugin的源碼:

/*** @author Clinton Begin* 這個類是Mybatis攔截器的核心,大家可以看到該類繼承了InvocationHandler* 又是JDK動態(tài)代理機制*/ public class Plugin implements InvocationHandler {//目標(biāo)對象private final Object target;// 攔截器private final Interceptor interceptor;//記錄需要被攔截的類與方法 提高性能private final Map<Class<?>, Set<Method>> signatureMap;private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {this.target = target;this.interceptor = interceptor;this.signatureMap = signatureMap;}//一個靜態(tài)方法,對一個目標(biāo)對象進行包裝,生成代理類。public static Object wrap(Object target, Interceptor interceptor) {//首先根據(jù)interceptor上面定義的注解 獲取需要攔截的信息Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);//目標(biāo)對象的ClassClass<?> type = target.getClass();//返回需要攔截的接口信息Class<?>[] interfaces = getAllInterfaces(type, signatureMap);//如果長度為>0 則返回代理類 否則不做處理if (interfaces.length > 0) {return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}//代理對象每次調(diào)用的方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//通過method參數(shù)定義的類 去signatureMap當(dāng)中查詢需要攔截的方法集合Set<Method> methods = signatureMap.get(method.getDeclaringClass());//判斷是否需要攔截if (methods != null && methods.contains(method)) {//執(zhí)行攔截器的攔截方法return interceptor.intercept(new Invocation(target, method, args));}//不攔截 直接通過目標(biāo)對象調(diào)用方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}//根據(jù)攔截器接口(Interceptor)實現(xiàn)類上面的注解獲取相關(guān)信息private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {//獲取注解信息@InterceptsIntercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);// issue #251if (interceptsAnnotation == null) {//為空則拋出異常throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); }//獲得Signature注解信息 是一個數(shù)組Signature[] sigs = interceptsAnnotation.value();Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();//循環(huán)注解信息for (Signature sig : sigs) {//根據(jù)Signature注解定義的type信息去signatureMap當(dāng)中查詢需要攔截方法的集合Set<Method> methods = signatureMap.get(sig.type());if (methods == null) { //第一次肯定為null 就創(chuàng)建一個并放入signatureMapmethods = new HashSet<Method>();signatureMap.put(sig.type(), methods);}try {//找到sig.type當(dāng)中定義的方法 并加入到集合Method method = sig.type().getMethod(sig.method(), sig.args());methods.add(method);} catch (NoSuchMethodException e) {throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);}}return signatureMap;}//根據(jù)對象類型與signatureMap獲取接口信息private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {Set<Class<?>> interfaces = new HashSet<Class<?>>();//循環(huán)type類型的接口信息 如果該類型存在與signatureMap當(dāng)中則加入到set當(dāng)中去while (type != null) {for (Class<?> c : type.getInterfaces()) {if (signatureMap.containsKey(c)) {interfaces.add(c);}}type = type.getSuperclass();}//轉(zhuǎn)換為數(shù)組返回return interfaces.toArray(new Class<?>[interfaces.size()]);}}

3.代理鏈上的攔截

我們再次結(jié)合(Executor)interceptorChain.pluginAll(executor)這個語句來看,這個語句內(nèi)部對executor執(zhí)行了多次plugin,第一次plugin后通過Plugin.wrap方法生成了第一個代理類,姑且就叫executorProxy1,這個代理類的target屬性是該executor對象。第二次plugin后通過Plugin.wrap方法生成了第二個代理類,姑且叫executorProxy2,這個代理類的target屬性是executorProxy1...這樣通過每個代理類的target屬性就構(gòu)成了一個代理鏈(從最后一個executorProxyN往前查找,通過target屬性可以找到最原始的executor類)

當(dāng)Configuration調(diào)用newExecutor方法的時候,由于Executor接口的update(MappedStatement ms, Object parameter)方法被攔截器被截獲。因此最終返回的是一個代理類Plugin,而不是Executor。這樣調(diào)用方法的時候,如果是個代理類,那么會執(zhí)行:

//代理對象每次調(diào)用的方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//通過method參數(shù)定義的類 去signatureMap當(dāng)中查詢需要攔截的方法集合Set<Method> methods = signatureMap.get(method.getDeclaringClass());//判斷是否需要攔截if (methods != null && methods.contains(method)) {//執(zhí)行攔截器的攔截方法return interceptor.intercept(new Invocation(target, method, args));}//不攔截 直接通過目標(biāo)對象調(diào)用方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);} }

那么會執(zhí)行Interceptor接口的interceptor方法 如下

public Object intercept(Invocation invocation) throws Throwable {return invocation.proceed(); }

傳遞給攔截器的是一個Invocation對象,如下

public class Invocation {private final Object target;private final Method method;private final Object[] args;public Invocation(Object target, Method method, Object[] args) {this.target = target;this.method = method;this.args = args;}public Object getTarget() {return target;}public Method getMethod() {return method;}public Object[] getArgs() {return args;}public Object proceed() throws InvocationTargetException, IllegalAccessException {return method.invoke(target, args);}

可以看到,Invocation類保存了代理對象的目標(biāo)類,執(zhí)行的目標(biāo)類方法以及傳遞給它的參數(shù)。

在每個攔截器的intercept方法內(nèi),最后一個語句一定是return invocation.proceed()(不這么做的話攔截器鏈就斷了,你的mybatis基本上就不能正常工作了)。invocation.proceed()只是簡單的調(diào)用了下target的對應(yīng)方法,如果target還是個代理,就又回到了上面的Plugin.invoke方法了。這樣就形成了攔截器的調(diào)用鏈推進。

4.總結(jié)

總體來說MyBatis攔截器還是很簡單的,攔截器本身不需要太多的知識點,但是學(xué)習(xí)攔截器需要對MyBatis中的各個接口很熟悉,因為攔截器涉及到了各個接口的知識點。

我們假設(shè)在MyBatis配置了一個插件,在運行時會發(fā)生什么?

  • 所有可能被攔截的處理類都會生成一個代理
  • 處理類代理在執(zhí)行對應(yīng)方法時,判斷要不要執(zhí)行插件中的攔截方法
  • 執(zhí)行插接中的攔截方法后,推進目標(biāo)的執(zhí)行
  • 如果有N個插件,就有N個代理,每個代理都要執(zhí)行上面的邏輯。這里面的層層代理要多次生成動態(tài)代理,是比較影響性能的。雖然能指定插件攔截的位置,但這個是在執(zhí)行方法時動態(tài)判斷,初始化的時候就是簡單的把插件包裝到了所有可以攔截的地方。

    因此,在編寫插件時需注意以下幾個原則:

  • 不編寫不必要的插件;
  • 實現(xiàn)plugin方法時判斷一下目標(biāo)類型,是本插件要攔截的對象才執(zhí)行Plugin.wrap方法,否者直接返回目標(biāo)本省,這樣可以減少目標(biāo)被代理的次數(shù)。
  • 總結(jié)

    以上是生活随笔為你收集整理的mybatis源码阅读(八) ---Interceptor了解一下的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。