Mybatis源码分析之(六)mybatis拦截器(Interceptor)的实现原理
文章目錄
- 前言
- InterceptorChain保存所有的Interceptor
- 創(chuàng)建四大對(duì)象都走Configuration
- InterceptorChain增強(qiáng)對(duì)象方法
- Plugin封裝動(dòng)態(tài)代理,讓你使用Mybatis攔截器更簡(jiǎn)單
- Invocation,讓我們能在攔截器中使用動(dòng)態(tài)代理類中的invoke方法中的對(duì)象
- 調(diào)用時(shí)序圖
- 小結(jié)
前言
mybatis攔截器是一個(gè)非常有用的功能,當(dāng)你想實(shí)現(xiàn)自動(dòng)分頁(yè),自動(dòng)記錄執(zhí)行的sql等功能時(shí),若在service層,每次調(diào)用時(shí),都寫(xiě)代碼的話,會(huì)非常麻煩,而使用mybatis攔截器,就可以非常輕松的實(shí)現(xiàn)了。
Executor , ResultSetHandler,StatementHandler,ParameterHandler,這是Mybatis中的四大對(duì)象,也是攔截器的切入點(diǎn)。我們可以基于這四大對(duì)象的方法進(jìn)行增強(qiáng)。解釋一下,因?yàn)檫@四個(gè)都是接口,我們可以利用動(dòng)態(tài)代理進(jìn)行方法的增強(qiáng)。
動(dòng)態(tài)代理,這是底層的原理,若只用了動(dòng)態(tài)代理,那么你肯定要自己寫(xiě)代理類,在使用的時(shí)候?qū)嵗?#xff0c;然后再替換Mybatis里面的原有對(duì)象,對(duì)不?但是實(shí)際你并不需要這么做,那是因?yàn)閙ybatis一開(kāi)始就為你設(shè)計(jì)好了讓你如何簡(jiǎn)單快速的添加攔截器,讓你在添加攔截器的時(shí)候只用關(guān)注業(yè)務(wù)邏輯,而不需要管類之間的關(guān)系。
那么Mybatis究竟是怎么做到這一點(diǎn)的呢?接下來(lái)就讓LZ帶大家來(lái)看看Mybatis究竟是如何實(shí)現(xiàn)的吧。
我們實(shí)現(xiàn)mybatis攔截器的步驟,首先創(chuàng)建Interceptor的實(shí)現(xiàn)類,然后我們要在mybatis.xml中配置plugins,這就是我們?yōu)镸ybatis添加攔截器的步驟。
InterceptorChain保存所有的Interceptor
我們來(lái)到Configuration類中,看到里面他有個(gè)屬性叫InterceptorChain,里面是用來(lái)存放我們的所有攔截器,針對(duì)四大對(duì)象的攔截器全部在里面。
//這里就是解析并把plugin加入到interceptorChain中private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}}//加入到interceptorChainpublic void addInterceptor(Interceptor interceptor) {interceptorChain.addInterceptor(interceptor);}創(chuàng)建四大對(duì)象都走Configuration
然后,Mybatis在創(chuàng)建四大對(duì)象的時(shí)候都是走的Configuration類中的方法
//創(chuàng)建ParameterHandler對(duì)象public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);//都有interceptorChain.pluginAll()parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;} //創(chuàng)建ResultSetHandler對(duì)象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);//都有interceptorChain.pluginAll()resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);return resultSetHandler;} //創(chuàng)建StatementHandler對(duì)象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);//都有interceptorChain.pluginAll()statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;} //創(chuàng)建Executor對(duì)象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);}//都有interceptorChain.pluginAll()executor = (Executor) interceptorChain.pluginAll(executor);return executor;}觀察上面的四個(gè)函數(shù),他們都有一個(gè)共有的點(diǎn),調(diào)用了interceptorChain.pluginAll()方法,這也是Mybatis實(shí)現(xiàn)攔截器功能的中點(diǎn),這個(gè)pluginAll(),使攔截器有了一個(gè)特性,那就是邏輯可以向下傳遞,是責(zé)任鏈模式。
InterceptorChain增強(qiáng)對(duì)象方法
public class InterceptorChain {private final List<Interceptor> interceptors = new ArrayList<Interceptor>();public Object pluginAll(Object target) {//通過(guò)for循環(huán)遍歷interceptors,將所有的interceptor都加進(jìn)去。//一層包一層,直到所有的interceptor都包裝好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);}}這里進(jìn)去的對(duì)象,和出來(lái)的對(duì)象已經(jīng)不是同一個(gè)了。進(jìn)去的基礎(chǔ)四大對(duì)象,出來(lái)的是增強(qiáng)版四大對(duì)象。
這里其實(shí)用到了責(zé)任鏈模式,每個(gè)Interceptor 都有自己要服務(wù)的對(duì)象,只有當(dāng)請(qǐng)求的方法和Interceptor要服務(wù)的對(duì)象匹配時(shí),它才會(huì)執(zhí)行,你攔截器里的方法。
接下來(lái)LZ帶大家來(lái)看看Mybatis是怎么比對(duì)兩個(gè)對(duì)象是否匹配的。
Plugin封裝動(dòng)態(tài)代理,讓你使用Mybatis攔截器更簡(jiǎn)單
我們?nèi)lugin類中
//看到這個(gè)類,大家有沒(méi)有覺(jué)得很熟悉,沒(méi)錯(cuò),實(shí)現(xiàn)了InvocationHandler ,使用動(dòng)態(tài)代理 //這個(gè)就是Mybatis實(shí)現(xiàn)攔截器功能的底層。 public class Plugin implements InvocationHandler {private Object target;private Interceptor interceptor;private 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;} //我們不需要手動(dòng)的實(shí)例化動(dòng)態(tài)代理對(duì)象,是因?yàn)閣rap為我們做了這件事public static Object wrap(Object target, Interceptor interceptor) {//這句也蠻重要的,是將我們寫(xiě)的攔截器類的注解轉(zhuǎn)換成了map,key為我們的類對(duì)象,value是方法//為了之后判斷是否需要執(zhí)行攔截器的方法//這句就不進(jìn)去仔細(xì)分析了,因?yàn)楹芎?jiǎn)單,就是解析注解而已Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();Class<?>[] interfaces = getAllInterfaces(type, signatureMap);//這里實(shí)例了動(dòng)態(tài)代理對(duì)象 return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;} }public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {//獲得方法Set<Method> methods = (Set)this.signatureMap.get(method.getDeclaringClass());//如果方法不為空,說(shuō)明這個(gè)對(duì)象確實(shí)有拓展攔截器,之后看method是不是有這個(gè)方法,有的話才說(shuō)明這個(gè)方法確實(shí)被拓展了,之后執(zhí)行interceptor.intercept()即調(diào)用攔截器的方法return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);} catch (Exception var5) {throw ExceptionUtil.unwrapThrowable(var5);}}Invocation,讓我們能在攔截器中使用動(dòng)態(tài)代理類中的invoke方法中的對(duì)象
//通過(guò)這個(gè)對(duì)象,把代理類中的invoke方法中的對(duì)象和我們攔截器類相連。 //我們?cè)跀r截器類中的intercept(Invocation invocation)中的invocation就是這個(gè)類型 //所以我們就能在攔截器類中獲取到代理類中的各個(gè)對(duì)象啦 public class Invocation {private Object target;private Method method;private 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);}}調(diào)用時(shí)序圖
小結(jié)
總結(jié)一下,其實(shí)底層用到的還是動(dòng)態(tài)代理,但是Mybatis通過(guò)封裝,讓我們開(kāi)發(fā)攔截器更加簡(jiǎn)單。通過(guò)InterceptorChain,使得攔截器能將邏輯向下傳遞,然后通過(guò)Invocation,讓攔截器類能使用到動(dòng)態(tài)代理類invoke中的對(duì)象。
總結(jié)
以上是生活随笔為你收集整理的Mybatis源码分析之(六)mybatis拦截器(Interceptor)的实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python精要(72)-函数参数列表副
- 下一篇: python精要(73)-函数传递任意参