MyBatis uses the chain of responsibility model, through the dynamic agent to organize multiple interceptors (plug-ins), through these interceptors can change the default behavior of MyBatis (such as SQL rewrite), because the plug-in will go deep into the core of MyBatis, so before writing their own plug-ins, it is best to understand the principle of it, To write a safe and efficient plugin.
Generation of proxy chains
MyBatis supports interception of executor, Statementhandler, Pameterhandler, and Resultsethandler, which means that the 4 objects will be proxied.
By looking at the source code of the configuration class, we can see that each time a proxy chain is generated for the target object.
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, Executortype executortype, Boolean autocommit) { Executortype = Executortype = = null? Defaultexecutortype:executortype; Executortype = Executortype = = null? ExecutorType.SIMPLE:executorType; Executor Executor; if (Executortype.batch = = executortype) { executor = new Batchexecutor (this, transaction); } else if (Executortyp E.reuse = = executortype) { executor = new Reuseexecutor (this, transaction); } else { executor = new Simpleexec Utor (this, transaction); } if (cacheenabled) { executor = new Cachingexecutor (executor, autocommit); } Executor = (executor) interceptorchain.pluginall (executor); return executor; }
Next, let's analyze the source code to understand the mybatis of the Interceptor implementation principle
For interceptors MyBatis provides us with a interceptor interface that allows us to define our own interceptors by implementing this interface. Let's first look at the definition of this interface:
1 package org.apache.ibatis.plugin; 2 3 import java.util.Properties; 4 5 public interface Interceptor {6 7 Object Intercept (invocation invocation) throws Throwable; 8 9 Object Plugin (object target); setproperties void (properties); 12 13}
We can see that there are altogether three methods defined in the interface, intercept, plugin, and setproperties. The plugin method is used by the interceptor to encapsulate the target object, which we can return to the target object itself, or it can return a proxy for it. When we return to the proxy, we can intercept the method to invoke the Intercept method, and of course we can call other methods, which will be explained later in this article. The SetProperties method is used to specify some properties in the MyBatis configuration file.
The most important thing to define your own interceptor is to implement the plugin method and intercept method, and in the plugin method we can decide whether to intercept and decide what target object to return. The Intercept method is the method to be executed when intercepting.
For the plugin method, in fact MyBatis has provided us with an implementation. There is a class called Plugin in MyBatis that has a static method wrap (object Target,interceptor Interceptor) that determines whether the object to be returned is the target object or the corresponding proxy. Here we first look at the source code of plugin:
1 package org.apache.ibatis.plugin; 2 3 Import Java.lang.reflect.InvocationHandler; 4 Import Java.lang.reflect.Method; 5 Import Java.lang.reflect.Proxy; 6 Import Java.util.HashMap; 7 Import Java.util.HashSet; 8 Import Java.util.Map; 9 Import Java.util.Set; Ten import Org.apache.ibatis.reflection.ExceptionUtil; 12 13//This class is the core of the MyBatis interceptor, and you can see that the class inherits the Invocationhandler 14//is also the JDK dynamic agent mechanism on the public class Plugin implements Invocationhand ler {16 17//target object 19//Interceptor Private Interceptor Interceptor; 21//Record the class and method to be intercepted 22 Private map<class<?> set<method>> Signaturemap; Plugin Private (Object target, Interceptor Interceptor, Map<class<?>, set<method>> Signaturemap) {this.target = target; this.interceptor = interceptor; this.signaturemap = Signaturemap; 28} 29 30//A static method that wraps a target object and generates a proxy class. public static object Wrap (object target, Interceptor Interceptor) {32//FirstObtain information that needs to be intercepted according to the annotations defined above interceptor map<class<?> set<method>> signaturemap = Getsignaturemap (interce Ptor); 34//target object Class class<?> type = Target.getclass (); 36//Return interface information that needs to be intercepted class<?>[] interfaces = getallinterfaces (type, signaturemap); 38//If the length is >0 then return the proxy class otherwise do not do the processing of the IF (Interfaces.length > 0) {return proxy.newproxyinstance (41 Type.getclassloader (), interfaces, new Plugin (Target, Interceptor, Signaturemap)); The return target; 46} 47 48//proxy object method for each invocation of the public object invoke (object proxy, Method method, object[] args) throws Throwable {50 try {51//The class that is defined by the method parameter goes to Signaturemap to query the collection of methods that need to be intercepted set<method> methods = Signaturemap.get (Metho D.getdeclaringclass ()); 53//Determine if you need to intercept the methods! = NULL && Methods.contains (method)) {return interceptor.inte Rcept (new invocation (target, method, args)); 56 } 57//Do not intercept directly through the target object call method Method.invoke (target, args); (Exception e) {exceptionutil.unwrapthrowable (e); 61} 62} 63 64//According to the Interceptor interface (Interce Ptor) Implement the annotations on the class to get relevant information. Map<class<?>, set<method>> Getsignaturemap (Interceptor Interceptor) {66//Get annotation information intercepts interceptsannotation = Interceptor.getclass (). Getannotation (intercepts.cl ); 68//Is empty throws an exception if (interceptsannotation = = null) {//Issue #251-throw new Pluginexception ("No @Intercep TS annotation is found in interceptor "+ Interceptor.getclass (). GetName ()); 71} 72//Get Signature annotation information signature[] sigs = Interceptsannotation.value (); Map<class<?>, set<method>> signaturemap = new Hashmap<class<?>, Set<Method>> () ; 75//Loop annotation information for (Signature Sig:sigs) {77//////////To query the collection of required interception methods in Signaturemap according to the type information defined by the Signature annotation. Et<method> MeThods = Signaturemap.get (Sig.type ()); 79//The first time is definitely null to create one and put Signaturemap if (methods = = null) {Bayi methods = new Hashset<method> ( ); Signaturemap.put (Sig.type (), methods); * * * * try {85///Find the method defined in Sig.type and add to the set of methods = Sig.type (). GetMethod (Sig.method (), Sig.args ()); Methods.add (method); (Nosuchmethodexception e) {pluginexception ("Could not Find method on" + Sig.type () + "named" + Sig.method () + ". Cause: "+ E, E); * * * * the SIGNATUREMAP return; 93} 94 95///Based on object type and Signaturemap get interface information. class<?>[] Getallinterfaces (class<?> type, MAP <class<?>, set<method>> signaturemap) {set<class<?>> interfaces = new HashSet<Cla Ss<?>> (); 98//Loop type interface information if the type exists with Signaturemap, add to set to go to the same time (type! = null) {for (class<?> C:typ E.getinterFaces ()) {101 if (Signaturemap.containskey (c)) {102 interfaces.add (c); 103}104}105 Type = Type.getsuperclass (); 106}107//Convert to an array returns 108 return Interfaces.toarray (new class<?>[interfaces.size ()]); 109}110 111}Plugin Source Code Analysis
The following is the definition of two annotation classes source code
1 package org.apache.ibatis.plugin; 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import Java.lang.annotation.RetentionPolicy; 6 Import Java.lang.annotation.Target; 7 8 @Retention (retentionpolicy.runtime) 9 @Target (elementtype.type) public @interface intercepts { one Signature[] Value (); 12}
1 package org.apache.ibatis.plugin; 2 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import Java.lang.annotation.RetentionPolicy; 6 Import Java.lang.annotation.Target; 7 8 @Retention (retentionpolicy.runtime) 9 @Target (elementtype.type) public @interface Signature {one Class <?> type (); String method (); class<?>[] args (); 16}
Http://www.cnblogs.com/daxin/p/3541922.html
Mybatis Interceptor Interceptor Principle Source analysis