MyBatis插件原理----从
本文分析一下MyBatis的插件实现原理,在此之前,如果对MyBatis插件不是很熟悉的朋友,可参看此文MyBatis7:MyBatis插件及示例----打印每条SQL语句及其执行时间,本文我以一个例子说明了MyBatis插件是什么以及如何实现。由于MyBatis的插件已经深入到了MyBatis底层代码,因此要更好地使用插件,必须对插件实现原理及MyBatis底层代码有所熟悉才行,本文分析一下MyBatis的插件实现原理。
首先,我们从插件
1 private void pluginElement(XNode parent) throws Exception { 2 if (parent != null) { 3 for (XNode child : parent.getChildren()) { 4 String interceptor = child.getStringAttribute("interceptor"); 5 Properties properties = child.getChildrenAsProperties(); 6 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); 7 interceptorInstance.setProperties(properties); 8 configuration.addInterceptor(interceptorInstance); 9 }10 }11 }
这里拿
再拿
最后,通过第8行的代码将拦截器设置到Configuration中,源码实现为:
<span style="color: #008080"> 1</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> addInterceptor(Interceptor interceptor) {</span><span style="color: #008080"> 2</span> <span style="color: #000000"> interceptorChain.addInterceptor(interceptor);</span><span style="color: #008080"> 3</span> <span style="color: #000000">}</span><span style="color: #008080"><br></span>
InterceptorChain是一个拦截器链,存储了所有定义的拦截器以及相关的几个操作的方法:
1 public class InterceptorChain { 2 3 private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); 4 5 public Object pluginAll(Object target) { 6 for (Interceptor interceptor : interceptors) { 7 target = interceptor.plugin(target); 8 } 9 return target;10 }11 12 public void addInterceptor(Interceptor interceptor) {13 interceptors.add(interceptor);14 }15 16 public List<Interceptor> getInterceptors() {17 return Collections.unmodifiableList(interceptors);18 }19 20 }
分别有添加拦截器、为目标对象添加所有拦截器、获取当前所有拦截器三个方法。
MyBatis插件原理----pluginAll方法添加插件
上面我们在InterceptorChain中看到了一个pluginAll方法,pluginAll方法为目标对象生成代理,之后目标对象调用方法的时候走的不是原方法而是代理方法,这个在后面会说明。
MyBatis官网文档有说明,在以下四个代码执行点上允许使用插件:
为之生成插件的时机(换句话说就是pluginAll方法调用的时机)是Executor、ParameterHandler、ResultSetHandler、StatementHandler四个接口实现类生成的时候,每个接口实现类在MyBatis中生成的时机是不一样的,这个就不看它们是在什么时候生成的了,每个开发工具我相信都有快捷键可以看到pluginAll方法调用的地方,我使用的Eclipse就是Ctrl+Alt+H。
再看pluginAll方法:
1 public Object pluginAll(Object target) {2 for (Interceptor interceptor : interceptors) {3 target = interceptor.plugin(target);4 }5 return target;6 }
这里值得注意的是:
形参Object target,这个是Executor、ParameterHandler、ResultSetHandler、StatementHandler接口的实现类,换句话说,plugin方法是要为Executor、ParameterHandler、ResultSetHandler、StatementHandler的实现类生成代理,从而在调用这几个类的方法的时候,其实调用的是InvocationHandler的invoke方法
这里的target是通过for循环不断赋值的,也就是说如果有多个拦截器,那么如果我用P表示代理,生成第一次代理为P(target),生成第二次代理为P(P(target)),生成第三次代理为P(P(P(target))),不断嵌套下去,这就得到一个重要的结论:
... 中后定义的实际其拦截器方法先被执行 ,因为根据这段代码来看,后定义的代理实际后生成,包装了先生成的代理,自然其代理方法也先执行
plugin方法中调用MyBatis提供的现成的生成代理的方法Plugin.wrap(Object target, Interceptor interceptor),接着我们看下wrap方法的源码实现。
MyBatis插件原理----Plugin的wrap方法的实现
Plugin的wrap方法实现为:
1 public static Object wrap(Object target, Interceptor interceptor) { 2 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); 3 Class<?> type = target.getClass(); 4 Class<?>[] interfaces = getAllInterfaces(type, signatureMap); 5 if (interfaces.length > 0) { 6 return Proxy.newProxyInstance( 7 type.getClassLoader(), 8 interfaces, 9 new Plugin(target, interceptor, signatureMap));10 }11 return target;12 }
首先看一下第2行的代码,获取Interceptor上定义的所有方法签名:
1 private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) { 2 Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); 3 // issue #251 4 if (interceptsAnnotation == null) { 5 throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); 6 } 7 Signature[] sigs = interceptsAnnotation.value(); 8 Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>(); 9 for (Signature sig : sigs) {10 Set<Method> methods = signatureMap.get(sig.type());11 if (methods == null) {12 methods = new HashSet<Method>();13 signatureMap.put(sig.type(), methods);14 }15 try {16 Method method = sig.type().getMethod(sig.method(), sig.args());17 methods.add(method);18 } catch (NoSuchMethodException e) {19 throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);20 }21 }22 return signatureMap;23 }
看到先拿@Intercepts注解,如果没有定义@Intercepts注解,抛出异常,这意味着使用MyBatis的插件,必须使用注解方式。
接着拿到@Intercepts注解下的所有@Signature注解,获取其type属性(表示具体某个接口),再根据method与args两个属性去type下找方法签名一致的方法Method(如果没有方法签名一致的就抛出异常,此签名的方法在该接口下找不到),能找到的话key=type,value=Set
回过头继续看wrap方法,在拿到方法签名映射后,调用getAllInterfaces方法,传入的是Target的Class对象以及之前获取到的方法签名映射:
1 private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { 2 Set<Class<?>> interfaces = new HashSet<Class<?>>(); 3 while (type != null) { 4 for (Class<?> c : type.getInterfaces()) { 5 if (signatureMap.containsKey(c)) { 6 interfaces.add(c); 7 } 8 } 9 type = type.getSuperclass();10 }11 return interfaces.toArray(new Class<?>[interfaces.size()]);12 }
这里获取Target的所有接口,如果方法签名映射中有这个接口,那么添加到interfaces中,这是一个Set,最终将Set转换为数组返回。
wrap方法的最后一步:
1 if (interfaces.length > 0) {2 return Proxy.newProxyInstance(3 type.getClassLoader(),4 interfaces,5 new Plugin(target, interceptor, signatureMap));6 }7 return target;
如果当前传入的Target的接口中有@Intercepts注解中定义的接口,那么为之生成代理,否则原Target返回。
这段理论可能大家会看得有点云里雾里,我这里举个例子:
= StatementHandler., method = "query", args = {Statement., ResultHandler.= StatementHandler., method = "update", args = {Statement. org.apache.ibatis.executor.statement.StatementHandler=[ org.apache.ibatis.executor.statement.StatementHandler.update(java.sql. Statement) java.sql.SQLException, java.util.List org.apache.ibatis.executor.statement.StatementHandler.query(java.sql.Statement,org.apache. ibatis.session.ResultHandler) java.sql.SQLException]} 一个Class对应一个Set,Class为StatementHandler.class,Set为StataementHandler中的两个方法 如果我new的是StatementHandler接口的实现类,那么可以为之生成代理,因为signatureMap中的key有StatementHandler这个接口 如果我new的是Executor接口的实现类,那么直接会把Executor接口的实现类原样返回,因为signatureMap中的key并没有Executor这个接口
相信这么解释大家应该会明白一点。注意这里生不生成代理,只和接口在不在@Intercepts中定义过有关,和方法签名无关,具体某个方法走拦截器,在invoke方法中,马上来看一下。
MyBatis插件原理----Plugin的invoke方法
首先看一下Plugin方法的方法定义:
1 public class Plugin implements InvocationHandler { 2 3 private Object target; 4 private Interceptor interceptor; 5 private Map<Class<?>, Set<Method>> signatureMap; 6 7 private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { 8 this.target = target; 9 this.interceptor = interceptor;10 this.signatureMap = signatureMap;11 }12 ...13 }
看到Plugin是InvocationHandler接口的实现类,换句话说,为目标接口生成代理之后,最终执行的都是Plugin的invoke方法,看一下invoke方法的实现:
1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2 try { 3 Set<Method> methods = signatureMap.get(method.getDeclaringClass()); 4 if (methods != null && methods.contains(method)) { 5 return interceptor.intercept(new Invocation(target, method, args)); 6 } 7 return method.invoke(target, args); 8 } catch (Exception e) { 9 throw ExceptionUtil.unwrapThrowable(e);10 }11 }
在这里,将method对应的Class拿出来,获取该Class中有哪些方法签名,换句话说就是Executor、ParameterHandler、ResultSetHandler、StatementHandler,在@Intercepts注解中定义了要拦截哪些方法签名。
如果当前调用的方法的方法签名在方法签名集合中,即满足第4行的判断,那么调用拦截器的intercept方法,否则方法原样调用,不会执行拦截器。
Atas ialah kandungan terperinci 【MyBatis源码分析】插件实现原理. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Artikel ini membincangkan menggunakan Maven dan Gradle untuk Pengurusan Projek Java, membina automasi, dan resolusi pergantungan, membandingkan pendekatan dan strategi pengoptimuman mereka.

Artikel ini membincangkan membuat dan menggunakan perpustakaan Java tersuai (fail balang) dengan pengurusan versi dan pergantungan yang betul, menggunakan alat seperti Maven dan Gradle.

Artikel ini membincangkan pelaksanaan caching pelbagai peringkat di Java menggunakan kafein dan cache jambu untuk meningkatkan prestasi aplikasi. Ia meliputi persediaan, integrasi, dan faedah prestasi, bersama -sama dengan Pengurusan Dasar Konfigurasi dan Pengusiran PRA Terbaik

Artikel ini membincangkan menggunakan JPA untuk pemetaan objek-relasi dengan ciri-ciri canggih seperti caching dan pemuatan malas. Ia meliputi persediaan, pemetaan entiti, dan amalan terbaik untuk mengoptimumkan prestasi sambil menonjolkan potensi perangkap. [159 aksara]

Kelas kelas Java melibatkan pemuatan, menghubungkan, dan memulakan kelas menggunakan sistem hierarki dengan bootstrap, lanjutan, dan pemuat kelas aplikasi. Model delegasi induk memastikan kelas teras dimuatkan dahulu, yang mempengaruhi LOA kelas tersuai


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

VSCode Windows 64-bit Muat Turun
Editor IDE percuma dan berkuasa yang dilancarkan oleh Microsoft

Dreamweaver CS6
Alat pembangunan web visual

Versi Mac WebStorm
Alat pembangunan JavaScript yang berguna

Pelayar Peperiksaan Selamat
Pelayar Peperiksaan Selamat ialah persekitaran pelayar selamat untuk mengambil peperiksaan dalam talian dengan selamat. Perisian ini menukar mana-mana komputer menjadi stesen kerja yang selamat. Ia mengawal akses kepada mana-mana utiliti dan menghalang pelajar daripada menggunakan sumber yang tidak dibenarkan.

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa