Principe du plug-in MyBatis --- plug-in de pagination PageHelper

Instructions

1. Ajouter une dépendance Maven

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.0.0</version>
</dependency>

2. Configurez les paramètres du plug-in PageHelper dans Mybatis-Config.xml

<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- 4.0.0以后版本可以不设置该参数 ,可以自动识别
            <property name="dialect" value="mysql"/>  -->
            <!-- 该参数默认为false -->
            <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
            <!-- 和startPage中的pageNum效果一样-->
            <property name="offsetAsPageNum" value="true"/>
            <!-- 该参数默认为false -->
            <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
            <property name="rowBoundsWithCount" value="true"/>
            <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
            <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
            <property name="pageSizeZero" value="true"/>
            <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
            <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
            <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
            <property name="reasonable" value="true"/>
            <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
            <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
            <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,orderBy,不配置映射的用默认值 -->
            <!-- 不理解该含义的前提下,不要随便复制该配置 -->
            <property name="params" value="pageNum=start;pageSize=limit;"/>
            <!-- 支持通过Mapper接口参数来传递分页参数 -->
            <property name="supportMethodsArguments" value="true"/>
            <!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
            <property name="returnPageInfo" value="check"/>
        </plugin>
    </plugins>

Remarque: la configuration des plugins est après les paramètres et avant les environnements, sinon le fichier de configuration ne peut pas être analysé

3. Code

		PageHelper.startPage(pn, 10); //pageNumber, pageSize,第几页,每页几条
        List<Employee> emps = employeeService.getAll();
        PageInfo page = new PageInfo(emps, 10);
        return Msg.success().add("pageInfo", page);

principe

Comme on peut le voir dans le fichier de configuration, la classe principale du plugin de pagination pageHelper est com.github.pagehelper.PageInterceptor

package com.github.pagehelper;

import com.github.pagehelper.cache.Cache;
import com.github.pagehelper.cache.CacheFactory;
import com.github.pagehelper.util.MSUtils;
import com.github.pagehelper.util.StringUtil;
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.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * Mybatis - 通用分页拦截器<br/>
 * 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
 *
 * @author liuzh/abel533/isea533
 * @version 5.0.0
 */
@SuppressWarnings({
    
    "rawtypes", "unchecked"})
@Intercepts(
    {
    
    
        @Signature(type = Executor.class, method = "query", args = {
    
    MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {
    
    MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
    }
)
public class PageInterceptor implements Interceptor {
    
    
    //缓存count查询的ms
    protected Cache<CacheKey, MappedStatement> msCountMap = null;
    private Dialect dialect;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";
    private Field additionalParametersField;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
    
        ...
    }
    @Override
    public Object plugin(Object target) {
    
    
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    
    
       ...
    }

}

Réalise principalement l'interface org.apache.ibatis.plugin.Interceptor. Il existe principalement trois méthodes

  • intercepter où le contenu d'interception est exécuté, intercepter l'exécution de la méthode cible de l'objet cible
  • Le plugin décide s'il faut déclencher la méthode intercept () et encapsule l'objet cible. L'empaquetage consiste à créer un objet proxy pour l'objet cible
  • setProperties transmet les paramètres de propriété de la configuration xml à l'intercepteur personnalisé. Définissez l'attribut de propriété lorsque le plugin est enregistré
package org.apache.ibatis.plugin;

import java.util.Properties;

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

  Object intercept(Invocation invocation) throws Throwable;
  
  default Object plugin(Object target) {
    
    
    return Plugin.wrap(target, this);
  }
  
  default void setProperties(Properties properties) {
    
    
  }
}

Il faut également faire attention à l'annotation @Intercepts de PageInterceptor, qui spécifie l'objet et la méthode interceptés. Par défaut, MyBatis permet aux plug-ins d'intercepter les appels de méthode, notamment:

  • Executor (mise à jour, requête, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (préparer, paramétrer, traiter par lots, mettre à jour, interroger)
@Intercepts(
    {
    
    
        @Signature(type = Executor.class, method = "query", args = {
    
    MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {
    
    MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
    }
)

Après avoir compris les classes de base de PageHelper, voyons comment cela fonctionne.

1. Charger

Tout d'abord, lorsque myBatis génère la fabrique de sessions sqlSessionFactory, le nœud du plugin dans le fichier de configuration mybatis-config.xml est chargé

XMLConfigBuilder.java

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).getDeclaredConstructor().newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

Lisez tous les nœuds d'intercepteur dans une boucle et forcez-les dans des objets Interceptor. Farcis dans un conteneur InterceptorChain pour la gestion

InterceptorChain.java

public class InterceptorChain {
    
    
  private final List<Interceptor> interceptors = new ArrayList<>();
  public void addInterceptor(Interceptor interceptor) {
    
    
    interceptors.add(interceptor);
  }

Le nom de la classe montre que mybaits gère tous les plug-ins en utilisant le modèle de conception de la chaîne de responsabilité
. La pagination du code montre qu'il n'a pas modifié le code d'origine. Comment change-t-il et améliore-t-il le comportement de l'objet? De plus, si j'ai plusieurs plug-ins qui interceptent la même classe, comment les intercepte-t-il couche par couche?
Devinez, modifiez et améliorez l'objet de manière audacieuse grâce au modèle proxy; implémentez l'interception couche par couche via le modèle de chaîne de responsabilité.
Maintenant, nous entrons le code pour voir s'il est le même que nous l'avons deviné.

2. Intercepter

Dans l'annotation @Intercepts, vous pouvez voir que l'objet intercepté par le plug-in de pagination est l'exécuteur sous Execut, et l'exécuteur de mybatis est créé en créant la session sqlSession

Configuration.java

  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 {
    
    
      // 默认 SimpleExecutor
      executor = new SimpleExecutor(this, transaction);
    }
    // 二级缓存开关,settings 中的 cacheEnabled 默认是 true
    if (cacheEnabled) {
    
    
      executor = new CachingExecutor(executor);
    }
    // 植入插件的逻辑,至此,四大对象已经全部拦截完毕
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

InterceptorChain.java

public Object pluginAll(Object target) {
    
    
    for (Interceptor interceptor : interceptors) {
    
    
      target = interceptor.plugin(target);
    }
    return target;
  }

Comme deviné, tous les plugins sont gérés en utilisant le modèle de chaîne de responsabilité.
Selon la supposition, la méthode interceptor.plugin (cible) est utilisée pour créer des objets proxy, cliquer et jeter un œil

Interceptor.java

default Object plugin(Object target) {
    
    
    return Plugin.wrap(target, this);
  }

Plugin.java

 public static Object wrap(Object target, Interceptor interceptor) {
    
    
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
    
    
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

En effet, comme deviné, une méthode wrap est encapsulée via Plugin pour générer une classe proxy dynamique jdk.
Dans la méthode Proxy.newProxyInstance, il existe trois paramètres:

  • Chargeur ClassLoader
  • Interfaces de classe <?> [] Toutes les informations d'interface de la classe proxy
  • InvocationHandler h est une classe de traitement qui implémente la méthode invoke dans l'interface InvocationHandler.

Donc, la prochaine chose sur laquelle nous devons nous concentrer est la méthode invoke () dans la classe Plugin.

 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);
    }
  }

De cette manière, nous revenons à la méthode initiale de l'intercepteur intercept (). Ici, il encapsule une classe d'invocation, qui peut également être comprise comme l'encapsulation de la classe proxy.

public class Invocation {
    
    

  private final Object target;
  private final Method method;
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    
    
    this.target = target;
    this.method = method;
    this.args = args;
  }
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    
    
  	//被代理类本来的方法
    return method.invoke(target, args);
  }
}

En d'autres termes, il suffit d'exécuter la méthode procède (), ce qui équivaut à exécuter la méthode d'origine de la classe proxy. De cette façon, lorsque nous écrivons nous-mêmes la classe de plug-in, nous pouvons appeler la méthode proc () une fois le traitement terminé.

Pour résumer

Objets clés du plug-in Mybatis:

  • Interface d'intercepteur: intercepteur personnalisé (classe d'implémentation)
  • InterceptorChain: un conteneur pour stocker les plug-ins
  • Plugin: objet h, fournit une méthode pour créer une classe proxy
  • Invocation: encapsulation de la classe proxy

Organigramme de travail du plug-in:

Insérez la description de l'image ici
Lorsqu'il y a plusieurs plugins:
Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/xzw12138/article/details/106281035
conseillé
Classement