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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

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

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

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

1 Intercetor

MyBatis 允許你在已映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis允許使用插件來(lái)攔截的方法調(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語(yǔ)法構(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初始化的時(shí)候創(chuàng)建。上面interceptorChain.pluginAll(executor)的含義是調(diào)用攔截器鏈里的每個(gè)攔截器依次對(duì)executor進(jìn)行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>

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

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

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

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

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

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

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

/*** @author Clinton Begin* 這個(gè)類是Mybatis攔截器的核心,大家可以看到該類繼承了InvocationHandler* 又是JDK動(dòng)態(tài)代理機(jī)制*/ public class Plugin implements InvocationHandler {//目標(biāo)對(duì)象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;}//一個(gè)靜態(tài)方法,對(duì)一個(gè)目標(biāo)對(duì)象進(jìn)行包裝,生成代理類。public static Object wrap(Object target, Interceptor interceptor) {//首先根據(jù)interceptor上面定義的注解 獲取需要攔截的信息Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);//目標(biāo)對(duì)象的ClassClass<?> type = target.getClass();//返回需要攔截的接口信息Class<?>[] interfaces = getAllInterfaces(type, signatureMap);//如果長(zhǎng)度為>0 則返回代理類 否則不做處理if (interfaces.length > 0) {return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}//代理對(duì)象每次調(diào)用的方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//通過(guò)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));}//不攔截 直接通過(guò)目標(biāo)對(duì)象調(diào)用方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}//根據(jù)攔截器接口(Interceptor)實(shí)現(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注解信息 是一個(gè)數(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)建一個(gè)并放入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ù)對(duì)象類型與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.代理鏈上的攔截

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

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

//代理對(duì)象每次調(diào)用的方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//通過(guò)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));}//不攔截 直接通過(guò)目標(biāo)對(duì)象調(diào)用方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);} }

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

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

傳遞給攔截器的是一個(gè)Invocation對(duì)象,如下

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類保存了代理對(duì)象的目標(biāo)類,執(zhí)行的目標(biāo)類方法以及傳遞給它的參數(shù)。

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

4.總結(jié)

總體來(lái)說(shuō)MyBatis攔截器還是很簡(jiǎn)單的,攔截器本身不需要太多的知識(shí)點(diǎn),但是學(xué)習(xí)攔截器需要對(duì)MyBatis中的各個(gè)接口很熟悉,因?yàn)閿r截器涉及到了各個(gè)接口的知識(shí)點(diǎn)。

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

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

    因此,在編寫插件時(shí)需注意以下幾個(gè)原則:

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

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

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