1. 程式人生 > >一起學習Mybatis----外掛

一起學習Mybatis----外掛

外掛:在Mybatis的四大物件排程的時候插入我們的程式碼去執行一些特殊的需求以滿足特殊場景的需求。

在Mybatis中使用外掛,我們需要實現Interceptor介面。

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.util.Properties;

/**
 * @author Clinton Begin
 */
public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  Object plugin(Object target);

  void setProperties(Properties properties);

}

     Intercept方法:它將直接覆蓋你所攔截物件原有的方法,因此它是外掛的核心方法。Intercept裡面有個引數 Invocation物件,通過它可以反射排程原來物件的方。
     plugin方法: target是被攔截物件,它的作用是給被攔截物件生成一個代理物件,並返回它。為了方便 My Batis使用 org.apache ibatis plugin. Plugin中的wap靜態( (stati方法提供生成代理物件,我們往往使用 plugin方法便可以生成一個代理物件了。當然也可以自定義。
     setProperties方法:允許在 plugin元素中配置所需引數,方法在外掛初始化的時候就被呼叫了一次,然後把外掛物件存入到配置中,以便後面再取出。

外掛的初始化是在Mybatis初始化的時候完成的。儲存在Configuration中。

外掛初始化完成。

     外掛用的是責任鏈模式。責任鏈是一個物件在mybatis中可能是四大物件中的一個,在多個角色中傳遞,處在傳遞鏈上的任何角色都由機會處理它。

    Mybatis的責任鏈是由interceptorChain去定義的。

如 建立執行器的時候:

    plugin方法是生成代理物件的方法,當它取出外掛的時候是從 Configuration物件中去取出的。從第一個物件(四大物件中的一個)開始,將物件傳遞給了 plugin方法,然後返回一個代理;如果存在第二個外掛,那麼我們就拿到第一個代理物件,傳遞給 plugin方法再返回第一個代理物件的代理…1此類推,有多少個攔截器就生成多少個代理物件

。這樣一個外掛都可以攔截到真實的物件了。這就好比每一個外掛都可以一層層處理被攔截的物件。MyBatis的四大物件也是這樣處理的。

代理物件可以使用java自帶的jdk動態代理。

在編寫自定義外掛的時候,先需要了解一個工具類方便獲取響應的資料資訊。MetaObject 元資料物件。

例如:

獲取物件的屬性值。

外掛的開發需要三步驟:

    1.需要確定攔截的簽名

    2.實現攔截方法

    3.配置和執行監測

簽名:

   1.確定需要攔截的物件
         首先要根據功能來確定你需要攔截什麼物件
          Executor是執行SQL的全過程,包括組裝引數,組裝結果集返回和執行SQL過程,都可以攔截,較為廣泛。
          StatementHandler是執行SQL的過程,我們可以重寫執行SQL的過程。這是我最常用的攔截物件。
          ParameterHandler,很明顯它主要是攔截執行SQL的引數組裝,可以重寫組裝引數規則。
           Resultsethandler用於攔截執行結果的組裝,你可以重寫組裝結果的規則。
    如果我們清楚需要攔截的是 StatementHandler物件,應該在預編譯SQL之前,修改SQL使結果返回數量被限制。

  2.確定攔截該物件那個方法和引數。

   如:  

public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  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;

  List<BatchResult> flushStatements() throws SQLException;

攔截執行器Executor中的query方法。

比如PageInterceptor類 分頁外掛

@Intercepts(
        {
                @Signature(type = Executor.class,//確定要攔截的物件
                        method = "query",//確定要攔截的方法
                        args = {MappedStatement.class, Object.class})//攔截方法的引數
        }
)
public class MyInterceptor implements Interceptor {
 接著需要實現攔截方法。
package com.xxx.service;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Properties;

/**
 * @program: maobc-small_routine
 * @description:
 * @author: z.hw
 * @create: 2018-12-08 16:05
 **/
@Intercepts(
        {
                @Signature(type = Executor.class,//確定要攔截的物件
                        method = "query",//確定要攔截的方法
                        args = {MappedStatement.class, Object.class})//攔截方法的引數
        }
)
public class MyInterceptor implements Interceptor {

    Properties properties=null;
    
    /**
     * 代替攔截物件方法的內容
     * @param invocation 責任鏈物件
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        System.out.println("before.. ");
        //如果當前代理的是一個非代理物件,那麼它就回呼叫真實攔截物件的方法,如果不是
        //它會排程下個外掛代理物件的 invoke方法
        Object proceed = invocation.proceed();
        System.err.println("after.. ");

        return proceed;
    }

    /**
     * 生成物件的代理,這裡常用 MyBatis提供的 Plugin類的wrap方法
     * @param target 被代理的物件
     * @return
     */
    @Override
    public Object plugin(Object target) {
       //使用 MyBatis提供的 Plugin類生成代理物件
        System.err.println("呼叫生成代理物件..");
        Object wrap = Plugin.wrap(target, this);
        return wrap;
    }

    /**
     * 獲取外掛配置的屬性,我們在Mybatis的配置檔案中的配置
     *
     * @param properties 配置的引數
     */
    @Override
    public void setProperties(Properties properties) {

        System.out.println(properties.get("dbType"));
        this.properties=properties;

    }
}

配置:

<plugins>
   <plugin interceptor="">
           <property name="dbType" value="mysql" />
    </plugin>
</plugins>
   可以參看PageInterceptor原始碼。