`
king_tt
  • 浏览: 2233340 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Mybatis插件原理分析(一)

 
阅读更多

我们首先介绍一下Mybatis插件相关的几个类,并对源码进行了简单的分析。

Mybatis插件相关的接口或类有:Intercept、InterceptChain、Plugin和Invocation,这几个接口或类实现了整个Mybatis插件流程。

Interceptor:一个接口,是实现自己功能需要实现的接口

源码如下:

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

  //在此方法中实现自己需要的功能,最后执行invocation.proceed()方法,实际就是调用method.invoke(target, args)方法,调用代理类
  Object intercept(Invocation invocation) throws Throwable;

  //这个方法是将target生成代理类
  Object plugin(Object target);
  //在xml中注册Intercept是配置一些属性
  void setProperties(Properties properties);

}

InterceptorChain:有一个List<Interceptor> interceptors变量,来保存所有Interceptor的实现类

源码如下:

/**
 * @author Clinton Begin
 */
public class InterceptorChain {

  //插件拦截器链
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  //把target变成代理类,这样在运行target方法之前需要运行Plugin的invoke方法
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}


Invocation:一个比较简单的类,主要的功能就是根据构造函数类执行代理类

源码如下:

/**
 * @author Clinton Begin
 */
public class Invocation {

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

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget() {
    return target;
  }

  public Method getMethod() {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }
  //其实mybatis的Interceptor最终还是调用的method.invoke方法
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }

}


Plugin:Mybatis插件的核心类,它实现了代理接口InvocationHandler,是一个代理类

源码详解如下:

/**
 * @author Clinton Begin
 */
//这个类是Mybatis拦截器的核心,大家可以看到该类继承了InvocationHandler
//又是JDK动态代理机制
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) {
	//首先根据interceptor上面定义的注解 获取需要拦截的信息
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
	//如果长度为>0 则返回代理类 否则不做处理
    if (interfaces.length > 0) {
	  //创建JDK动态代理对象
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  //在执行Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类的方法时会调用这个方法
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
	  //通过method参数定义的类 去signatureMap当中查询需要拦截的方法集合
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
	  //判断是否是需要拦截的方法,如果需要拦截的话就执行实现的Interceptor的intercept方法,执行完之后还是会执行method.invoke方法,不过是放到interceptor实现类中去实现了
      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);
    }
  }
  //根据拦截器接口(Interceptor)实现类上面的注解获取相关信息
  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
	//获取注解信息
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
	//为空则抛出异常
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
	//获得Signature注解信息
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
	//循环注解信息
    for (Signature sig : sigs) {
	  //根据Signature注解定义的type信息去signatureMap当中查询需要拦截方法的集合
      Set<Method> methods = signatureMap.get(sig.type());
	  //第一次肯定为null 就创建一个并放入signatureMap
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
		//找到sig.type当中定义的方法 并加入到集合
        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;
  }
  //根据对象类型与signatureMap获取接口信息
  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
	//循环type类型的接口信息 如果该类型存在与signatureMap当中则加入到set当中去
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
	//转换为数组返回
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }

}


对Mybatis插件有一定了解的人应该知道,插件拦截的类是Executor、ParameterHandler、ResultSetHandler和StatementHandler的实现类,为什么会这样呢?在Configuration类中看一下代码就明白了,可以看到在初始化Executor、ParameterHandler、ResultSetHandler和StatementHandler都会调用interceptorChain.pluginAll()这个函数,其实这样之后各个接口的实现类就被代理类生成为目标类了。

 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) {
    return newExecutor(transaction, defaultExecutorType);
  }

  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 {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }


分享到:
评论

相关推荐

    mybatis插件直接可用

    而"Idea MyBatis插件"则是针对IntelliJ IDEA这个强大的Java集成开发环境设计的一款增强工具,它可以提供更高效、便捷的MyBatis开发体验。 该插件的直接可用性意味着用户无需复杂设置,只需按照指定路径将其放置在...

    02-02-03-MyBatis插件原理及Spring集成1

    总的来说,理解 MyBatis 插件原理和自定义插件的编写,可以帮助我们更好地扩展 MyBatis 的功能,实现更灵活的数据库操作。同时,掌握 Spring 集成 MyBatis 的方法,可以确保事务管理和资源管理的正确性,提升系统的...

    Mybatis系列教程Mybatis插件共8页.pdf.z

    【标题】"Mybatis系列教程Mybatis插件共8页.pdf.z" 提供的信息表明,这是一个关于Mybatis框架的教程,特别关注Mybatis插件的使用。Mybatis是一款流行的Java持久层框架,它允许开发者直接编写SQL语句,提供灵活的数据...

    一图读懂mybatis插件plugin原理

    本篇文章将通过解析“一图读懂mybatis插件plugin原理”来深入理解MyBatis Plugin的工作机制。 首先,我们需要了解MyBatis Plugin的基本概念。Plugin是MyBatis中用于拦截SqlSession方法的组件,它基于Java的动态代理...

    电子书-深入浅出Mybatis技术原理与实战

    10. **插件机制**:探讨Mybatis的插件扩展,如PageHelper分页插件的实现原理,以及自定义插件的编写方法。 11. **Mybatis与Spring整合**:讲解如何在Spring框架中集成Mybatis,包括使用SqlSessionFactoryBean、事务...

    深入浅出MyBatis技术原理与实战

    此外,还将涉及MyBatis的插件机制,如PageHelper分页插件,以及如何使用MyBatis进行关联查询和一对一、一对多、多对多的关系映射。 在实际开发中,MyBatis不仅适用于传统的单表操作,还能处理复杂的业务场景,如...

    MyBatis 插件.zip

    为了实现这个功能,开发者可能需要创建一个 MyBatis 插件,该插件解析 MyBatis 的 XML 映射文件,提取出 SQL 语句和参数,然后将这些信息传递给 IDE,使其能够进行智能分析和提示。 对于标签中的 "mybatis 插件",...

    深入浅出MyBatis技术原理与实战(高清带书签)

    本书还会讲解MyBatis的插件机制,开发者可以通过自定义插件拦截执行的SQL语句,进行日志记录、性能分析等操作。此外,MyBatis的缓存机制也是其高效性能的关键,分为一级缓存和二级缓存,可以有效减少对数据库的访问...

    深入浅出 mybatis技术原理与实践pdf

    《深入浅出 MyBatis 技术原理与实践》是一本专为 Java 开发者深入理解 MyBatis 框架而编写的书籍。MyBatis 是一款深受开发者喜爱的持久层框架,它允许程序员将 SQL 语句与 Java 代码紧密集成,从而简化了数据访问层...

    深入浅出MyBatis技术原理与实战.pdf

    通过《深入浅出MyBatis技术原理与实战》这本书,读者可以系统学习MyBatis的各个方面,包括基础概念、核心组件、映射机制、事务管理、缓存策略,以及在实际项目中的应用技巧,从而成为一名熟练掌握MyBatis的开发者。

    杨开振的深入浅出myBatis技术原理与实战的pdf版本.rar

    《深入浅出myBatis技术原理与实战》是由著名IT专家杨开振撰写的一本关于Mybatis技术的专著,这本书旨在帮助初中级开发者全面理解并掌握Mybatis框架的运用和核心原理。Mybatis作为一款广泛应用的Java持久层框架,以其...

    全面学习Mybatis插件之Mybatis-Plus_Java框架视频教程

    本课程全面讲解了Mybatis-Plus框架的使用,从快速入门到原理分析再到插件的应用。每一个知识点都有案例进行演示学习,最终通过学习你将全面掌握MP的使用,从而使Mybatis的的开发更加的高效,达到事半功倍的效果。 ...

    springMVC,spring,mybatis所有插件

    MyBatis插件系统允许开发者自定义拦截器,对MyBatis的执行流程进行拦截,实现如日志记录、性能分析、动态SQL等功能。在描述中提到的插件,可能是用于增强MyBatis开发体验的工具,例如代码生成器、SQL调试助手等。将...

    深入浅出MyBatis技术原理与实战(带目录)

    通过自定义插件,开发者可以扩展MyBatis的功能,例如日志记录、性能分析等。书中会讲解如何编写和注册插件,以及插件工作原理。 实战部分,书中的实例涵盖了增删改查操作、复杂查询、关联查询、批量操作等多种常见...

    free mybatis plugins

    6. **最佳实践**:使用MyBatis插件时,应遵循最佳实践,如保持Mapper接口和XML配置的一致性,合理利用缓存,避免复杂的嵌套查询,以及定期清理和更新插件,确保其兼容性和稳定性。 总之,"Free MyBatis Plugins" ...

    Mybatis分库分表扩展插件

    二、Mybatis分库分表插件原理 Mybatis的插件机制基于拦截器(Interceptor)设计模式,允许在执行SQL之前或之后插入自定义逻辑。分库分表插件主要工作在SQL路由阶段,根据特定的规则(如哈希、范围等)对原始SQL进行...

    mybatis分页插件源码

    3. **MyBatis分页插件原理** MyBatis分页插件的工作原理是动态插入SQL,它会在执行查询语句之前,根据数据库类型(如MySQL或Oracle)动态生成合适的分页SQL。例如,对于MySQL,它会将原SQL语句转换为带有LIMIT和...

    mybatis自定义插件源码

    首先,了解 MyBatis 插件的工作原理。MyBatis 的插件基于 Java 的动态代理机制,插件是对 MyBatis 中某些接口(如 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler)的方法进行拦截。当这些接口...

    mybatis源码分析视频

    分析MyBatis源码有助于开发者更深入地理解框架的工作原理,提升问题解决能力,优化性能,以及在遇到异常时能够快速定位问题所在。 3. **MyBatis核心组件** - **SqlSessionFactoryBuilder**:用于创建...

Global site tag (gtag.js) - Google Analytics