ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

mybatis源码配置文件解析之四:解析plugins标签

2020-06-18 18:52:13  阅读:262  来源: 互联网

标签:配置文件 plugin 标签 源码 mybatis interceptor 解析 properties


在前边的博客在分析了mybatis解析typeAliases标签,《mybatis源码配置文件解析之三:解析typeAliases标签》。下面来看解析plugins标签的过程。

一、概述

在mybatis的核心配置文件(mybatis-config.xml)文件中,有关plugins的配置如下,

<!-- 拦截器 -->
    <plugins>
        <plugin interceptor="cn.com.mybatis.plugins.MyInterceptor" />
    </plugins>

在mybatis的plugins叫做插件,其实也可以理解为拦截器。在plugins标签中配置plugin子标签,plugin子标签可以配置多个,多个子标签是有顺序的。除了上面的配置方式在每个plugin下还可以配置property子标签,

<!-- 拦截器 -->
    <plugins>
        <plugin interceptor="cn.com.mybatis.plugins.MyInterceptor" />
        <plugin interceptor="cn.com.mybatis.plugins.MyInterceptor2">
        <property name="name" value="mybatis"/>
        <property name="age" value="10"/>
        </plugin>
    </plugins>

上面的配置在第二个拦截器中新增了两个属性name和age,这两个属性会被解析为Properties对象。

在上面可以看到一个重要的配置是plugin标签中的interceptor属性,该属性配置的是一个拦截器类,对应该类有什么要求那,一个普通的类就可以吗,答案是就是一个普通的类,但必须满足下面的几个条件,才可以被mybatis作为插件使用,

  • 实现mybatis中的Interceptor接口,并重写其中的三个方法;
  • 在类上声明@Intercepts注解,该注解标明该拦截器拦截的方法,一个例子如下,
@Intercepts(@Signature(type=Executor.class,method="query",args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))

拦截的是实现了Executor接口的query方法,后面的args配置的该方法的入参。在mybatis中默认拦截以下接口的方法,

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

Executor(执行器)、ParameterHandler(参数处理器)、ResultSetHandler(结果集处理器)、StatementHandler(SQL语法构建器)在mybatis中是四个顶级接口,贯穿了mybatis执行sql的全过程,从这里可以看出mybatis的拦截器的强大。

下面看下我的拦截器MyInterceptor的源码,

package cn.com.mybatis.plugins;

import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

@Intercepts(@Signature(type=Executor.class,method="query",args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class MyInterceptor implements Interceptor{

    /**
     * incation 封装了拦截的类,方法,方法参数
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // TODO Auto-generated method stub
        //执行被拦截的类中的方法
        
        
        Object o=invocation.proceed();
        return o;
    }

    /**
     * 返回一个JDK代理对象
     */
    @Override
    public Object plugin(Object target) {
        // TODO Auto-generated method stub
        return Plugin.wrap(target, this);
    }
/**
 * 允许在配置文件中配置一些属性即
 * <plugin interceptor="">
 * <property name ="" value =""/>
 * </plugin>
 */
    @Override
    public void setProperties(Properties properties) {
        // TODO Auto-generated method stub
        
    }

}

 

二、详述

上面,了解了plugis标签的基本配置及使用,下面看mybatis是如何解析的。

在XMLConfigBuilder类中的parseConfiguration方法

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      //解析properties标签    
      propertiesElement(root.evalNode("properties"));
      //解析settings标签,1、把<setting>标签解析为Properties对象
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      /*2、对<settings>标签中的<setting>标签中的内容进行解析,这里解析的是<setting name="vfsImpl" value=",">
      * VFS是mybatis中用来表示虚拟文件系统的一个抽象类,用来查找指定路径下的资源。上面的key为vfsImpl的value可以是VFS的具体实现,必须
      * 是权限类名,多个使用逗号隔开,如果存在则设置到configuration中的vfsImpl属性中,如果存在多个,则设置到configuration中的仅是最后一个
      * */
      loadCustomVfs(settings);
      //解析别名标签,例<typeAlias alias="user" type="cn.com.bean.User"/>
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析插件标签
      pluginElement(root.evalNode("plugins"));
      //解析objectFactory标签,此标签的作用是mybatis每次创建结果对象的新实例时都会使用ObjectFactory,如果不设置
      //则默认使用DefaultObjectFactory来创建,设置之后使用设置的
      objectFactoryElement(root.evalNode("objectFactory"));
      //解析objectWrapperFactory标签
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //解析reflectorFactory标签
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      //解析environments标签
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析<mappers>标签
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

从上面的代码中,我们找到下面这行解析plugins标签的代码,

 //解析插件标签
 pluginElement(root.evalNode("plugins"));

上面折行代码即在解析配置文件中的plugis标签,其定义如下

/**
 * 解析<plugin>标签
 * @param parent
 * @throws Exception
 */
  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
//1、获得interceptor属性 String interceptor = child.getStringAttribute("interceptor");
//2、把plugin标签下的property标签的内容解析为Properties对象 Properties properties = child.getChildrenAsProperties(); //3、使用配置的interceptor生成一个实例,配置的interceptor需要实现interceptor接口 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); //4、执行interceptor的setProperties方法,把配置的properties标签中的值设置到setProperties中 interceptorInstance.setProperties(properties);
//5、添加interceptor configuration.addInterceptor(interceptorInstance); } } }

1、解析标签

从上面的代码中可以看到在循环plugin标签,取得interceptor配置的类的全限类名,然后获得plugin标签下的property标签的内容,调用getChildrenAsProperties方法,

public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    for (XNode child : getChildren()) {
      String name = child.getStringAttribute("name");
      String value = child.getStringAttribute("value");
      if (name != null && value != null) {
        properties.setProperty(name, value);
      }
    }
    return properties;
  }

解析完property标签后,下面会生成interceptor实例。

2、生成interceptor实例

上面解析完plugin标签的内容后会生成interceptor实例,且调用其setProperty方法,

//使用配置的interceptor生成一个实例,配置的interceptor需要实现interceptor接口
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //执行interceptor的setProperties方法,把配置的properties标签中的值设置到setProperties中
        interceptorInstance.setProperties(properties);

 从上面的代码可以看到把配置的interceptor全限类名解析为一个Class对象,然后调用其默认的构造方法获得一个实例,然后调用了其setProperties方法,即把在标签中配置的property属性值注入到interceptor实例中,在我的例子中即调用下面的方法,

/**
 * 允许在配置文件中配置一些属性即
 * <plugin interceptor="">
 * <property name ="" value =""/>
 * </plugin>
 */
    @Override
    public void setProperties(Properties properties) {
        // TODO Auto-generated method stub
        this.properties=properties;
    }

这样就可以把标签中的内容转化到interceptor实例中的属性。

 3、添加到configuration中

经过上面的两个步骤的分析,拦截器已经从配置文件中的配置转化为了一个实例,下面就是要放入核心配置类configuration中,

configuration.addInterceptor(interceptorInstance);

看addInterceptor方法,该方法是configuration类中的方法,

public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
  }

调用了interceptorChain的addInterceptor方法,interceptorChain使用下面的方式进行初始化,

//拦截器链
  protected final InterceptorChain interceptorChain = new InterceptorChain();

其addInterceptor方法如下,

可以看到最终放到了interceptors中,这是一个ArrayList。

最终完成了解析plugins标签并把拦截器实例放到configuration中的过程。

三、总结

本文分析了mybatis中plugins标签的解析过程,该标签的解析过程相对简单,重点是拦截器背后的原理,是如何起到拦截作用,待下次分析。

 

有不正之处,欢迎指正,感谢!

标签:配置文件,plugin,标签,源码,mybatis,interceptor,解析,properties
来源: https://www.cnblogs.com/teach/p/13158937.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有