默認狀況下,Mybatis容許使用插件來攔截的類及方法有:java
只需實現Interceptor接口,並指定要攔截的類、方法。
很重要的是實現的plugin方法,約定了如何包裝生成這個代理類。本類其實相似於代理的invocationhandler。通常Plugin.wrap(target,this)就好了緩存
在intercept方法中必定要調用Invocation的proceed方法並將返回值返回。mybatis
@Intercepts({ @Signature( type=Executor.class,method="update",args={ MappedStatement.class,Object.class }) }) public class ExamplePlugin implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { //自定義實現 do somethings return invocation.proceed(); } public Object plugin(Object target){ return Plugin.wrap(target,this) } public void setProperties(Properties properties){ //傳入配置項 String size = properties.getProperty("size"); } } <!-- mybatis-config.xml --> <plugins> <plugin interceptor="org.mybatis.example.ExamplePlugin"> <!-- 這裏的配置項就傳入setProperties方法中 --> <property name="size" value="100"> </plugin> </plugins>
若是瞭解Mybatis的攔截器實現原理,能夠在之後的工做中也可以使用該方法實現本身的攔截器app
//攔截器接口,供外部實現,實現該接口就定義了一個插件 public interface Interceptor { //攔截方法,能夠將自定義邏輯寫在該方法中 Object intercept(Invocation invocation) throws Throwable; //包裝成插件,通常Plugin.wrap(target,this)就好了 Object plugin(Object target); //傳入自定義配置參數 void setProperties(Properties properties); } 攔截器上定義的註解 @Intercepts:攔截器註解,包括一個或多個@Signature,攔截的目標類信息 @Signature:攔截的目標類信息,包括type、method、args,一個@Intercepts中可包含多個@Signature public class Invocation { private Object target;//目標對象 private Method method;//調用方法 private Object[] args;//方法形參列表 //省略get和set方法 //執行調用,基於動態代理,在Interceptor的intercept方法中必定要調用該方法 public Object proceed() throws InvocationTargetException, IllegalAccessException { return method.invoke(target, args); } }
在配置文件中定義的過濾器,都保存在Configuration類的interceptorChain中,這個類保存了mybatis的全部配置,interceptorChain類中保存中全部Interceptor集合組成的攔截器鏈。ide
//XMLConfigBuilder類中解析mybatis-config.xml 核心方法parseConfiguration(XNode root) pluginElement(root.evalNode("plugins"));//插件配置項 private void pluginElement(XNode parent) throws Exception { if (parent != null) { //遍歷 plugins的子節點plugin for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor");//獲取interceptor屬性值 Properties properties = child.getChildrenAsProperties();//獲取plugin屬性值 //建立攔截器實例,這裏interceptor值也能夠是typeAlias註冊的簡名 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); //設置屬性項 interceptorInstance.setProperties(properties); //添加到interceptorChain中 configuration.addInterceptor(interceptorInstance); } } } //Configuration類,添加攔截器 public void addInterceptor(Interceptor interceptor) { interceptorChain.addInterceptor(interceptor); }
//SQL語句處理器 public interface StatementHandler { //預備工做 Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException; //參數處理 void parameterize(Statement statement) throws SQLException; //批量處理 void batch(Statement statement) throws SQLException; //更新處理 int update(Statement statement) throws SQLException; //查詢處理 <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; } //返回集處理器 public interface ResultSetHandler { //處理返回結果 <E> List<E> handleResultSets(Statement stmt) throws SQLException; //處理輸出參數 void handleOutputParameters(CallableStatement cs) throws SQLException; } //參數處理器 public interface ParameterHandler { Object getParameterObject(); void setParameters(PreparedStatement ps) throws SQLException; } //執行器 public interface Executor { //更新 int update(MappedStatement ms, Object parameter) throws SQLException; //查詢(先查緩存) <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; //查詢 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; //查詢遊標 <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; //刷新Statement List<BatchResult> flushStatements() throws SQLException; //提交事務 void commit(boolean required) throws SQLException; //回滾事務 void rollback(boolean required) throws SQLException; //建立緩存key CacheKey createCacheKey(MappedStatement ms, Object parameterObject,RowBounds rowBounds, BoundSql boundSql); //是否存在key boolean isCached(MappedStatement ms, CacheKey key); //清除本地緩存 void clearLocalCache(); //延遲加載 void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType); //獲取事務 Transaction getTransaction(); //關閉鏈接 void close(boolean forceRollback); //是否關閉 boolean isClosed(); //設置Executor void setExecutorWrapper(Executor executor); }
關鍵在於Configuration 在建立代理對象時執行的: interceptorChain.pluginAll方法。
最終回到了Plugin.wrap方法,會調用getAllInterfaces方法對signatureMap進行按@Signature的type進行篩選。有則構建代理對象,無則直接返回target源對象!
ui
//建立相應Handler時會將全部攔截器經過動態代理方式返回代理Handler public class Configuration { //建立ParameterHandler(參數處理器) public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { // 根據指定Lang(默認RawLanguageDriver),建立ParameterHandler,將實際參數傳遞給JDBC語句 ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler( mappedStatement, parameterObject, boundSql); //返回代理實例 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } //建立ResultSetHandler(結果處理器) public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,ParameterHandler parameterHandler, ResultHandler resultHandler,BoundSql boundSql) { //默認使用DefaultResultSetHandler建立ResultSetHandler實例 ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); //返回代理實例 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } //建立StatementHandler(SQL語句處理器) public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //默認使用RoutingStatementHandler(路由做用) //建立指定StatementHandler實例(默認SimpleStatementHandler) StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); //返回代理實例 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } //建立Executor(執行器) public Executor newExecutor(Transaction transaction, ExecutorType executorType) { //獲取executorType,默認是SIMPLE 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; } }
Plugin源碼以下:this
//動態代理實現 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; } //包裝目標實例 public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); //目標類全部接口是否有signatureMap中定義的Class Class<?>[] interfaces = getAllInterfaces(type, signatureMap); //若是攔截器中有定義攔截目標類中的方法時,就返回代理實例 if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } //沒有就返回目標實例 return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass()); //該方法須要攔截 if (methods != null && methods.contains(method)) { return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } //獲取攔截器上的SignatureMap private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) { Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); if (interceptsAnnotation == null) { throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName()); } Signature[] sigs = interceptsAnnotation.value(); Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>(); for (Signature sig : sigs) { Set<Method> methods = signatureMap.get(sig.type());//重複定義的只生效一個 if (methods == null) { methods = new HashSet<Method>(); signatureMap.put(sig.type(), methods); } try { //獲取目標類中的指定方法 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; } private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) { Set<Class<?>> interfaces = new HashSet<Class<?>>(); while (type != null) {//獲取type上的全部接口 for (Class<?> c : type.getInterfaces()) { if (signatureMap.containsKey(c)) {//這裏不判斷Method,只判斷Class<?> interfaces.add(c); } } type = type.getSuperclass(); } return interfaces.toArray(new Class<?>[interfaces.size()]); } }
1.攔截器實現
Interceptor接口供插件實現,@Intercepts註解在插件實現上,表示這是一個插件類並配置將要攔截哪些類的哪些方法,@Signature定義將要攔截的方法信息,如類名、方法名、形參列表,Plugin類實現了InvocationHandler接口,是動態代理的具體實現,Invocation類包裝了攔截的目標實例,InterceptorChain保存全部攔截器。
2.如何實現攔截
建立目標實例,好比A a = new A();
Interceptor interceptor = new LogInterceptor();
將A b = (A)interceptor.plugin(a); 這裏b就是a的代理實例,在調用a中的save方法時,實際將調用interceptor的intercept方法,在該方法中必定要調用Invocation的proceed方法並將返回值返回。spa