Mybatis的Interceptor接口

30 篇文章 0 订阅
6 篇文章 0 订阅
本文详细介绍了Mybatis的基本流程,从配置文件解析、四大对象(StatementHandler、ParameterHandler等)的创建过程,以及插件机制如何影响对象操作。重点展示了如何使用Interceptor拦截并增强四大对象的功能。
摘要由CSDN通过智能技术生成

Mybatis基本流程理解(详情点击)

引入

首先那一段最简单的mybatis调用的代码块来说明

{
	SqlSession session = null;
	String resource = "configuration.xml";
	// 使用io流读取配置
	InputStream inputStream;
	inputStream = Resources.getResourceAsStream(resource);
	//这里是解析配置文件
	sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	// 得到了一个会话,有了这个会话,你就可以对数据进行增,删,改,查的操作
	session = sqlSessionFactory.openSession();
}

XMLConfigBuilder部分方法

  private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
	  //你的接口实现类似在这一步就解析了
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
  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);
      }
    }
  }


四大对象(StatementHandler,ParameterHandler,ResultHandler,Executor)创建过程:

只拿Parameterhandler示例

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


在这里插入图片描述

总结(顺序如下图):

在这里插入图片描述

四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler),每个对象不是直接返回的,而是调用interceptorChain.pluginAll(),获取到所有的Interceptor,调用interceptor.plugin(target),返回target包装后的对象

插件机制:可以使用插件为目标对象创建一个代理对象,代理对象会拦截每一个对象的执行。

应用Interceptor插件

书写Interceptor实现类+注解@Intercepts

/**
 * @作者 five-five
 * @创建时间 2021/1/13
 */
//说明:我们使用插件拦截,都是拦截四大对象,其中@Intercepts注解是数组,所以可以写多个拦截的值
//type: 四大对象的那个(传入Class)
//method: 方法名
//args: 方法的参数
@Intercepts({
        @Signature(type = Executor.class, method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type=StatementHandler.class,method="parameterize",args={Statement.class})
})
public class MyTestExecutePlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();//目标方法
        System.out.println("目标对象:" + target);
        //这个方法是mybatis提供的,得到元数据,使用getValue()可以拿到getter和setter的值
        MetaObject metaObject = SystemMetaObject.forObject(target);
        metaObject.getValue("boundSql.sql");
        metaObject.getValue("configuration.environment");
        Object proceed = invocation.proceed();//拦截器放行
        return proceed;//返回的就是查询到的对象
    }

    @Override
    public Object plugin(Object target) {
        //target:核心对象
        //StatementHandler  ParameterHandler  ResultSetHandler   Executor
        //将拦截器中定义的增强功能和某一个核心对象合并起来,称为最终的核心对象
        Object wrap = Plugin.wrap(target, this);
        System.out.println(wrap);
        return wrap;
    }

    @Override
    public void setProperties(Properties properties) {
        // 属性值
        System.out.println(properties);
    }
}


mybatisconf.xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 开启懒加载 -->
    	<settings>
            <setting name="lazyLoadingEnabled" value="true"/>
            <setting name="aggressiveLazyLoading" value="false" />
            <setting name="jdbcTypeForNull" value="NULL" /><!-- 不设置这个参数的话有一些数据库不能识别null,如oracle数据库 -->
            <setting name="logImpl" value="log4j" />
        </settings>
<!--    <typeAliases>-->
<!--        <package name="com.edu.bean"/>-->
<!--    </typeAliases>-->
    <!-- 配置分页插件 5之后的版本:com.github.pagehelper.PageInterceptor -->
    	<plugins>
            <!--自定义的一个分页插件-->
<!--            <plugin interceptor="com.edu.mybatisIntercept.MyPagePlugin"></plugin>-->
            <!--旨在测试-->
            <plugin interceptor="com.edu.mybatisIntercept.MyTestExecutePlugin"></plugin>
            <!--分页插件-->
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
                <property name="reasonable" value="true"/>
            </plugin>
        </plugins>
</configuration> 

其中metadate元数据是最重要的,下面来看看这个元数据在Mybatis中有什么样的方法

MetaObject对象

public class MetaObject {

  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    if (object == null) {
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
  }

  public ObjectFactory getObjectFactory() {
    return objectFactory;
  }

  public ObjectWrapperFactory getObjectWrapperFactory() {
    return objectWrapperFactory;
  }

  public ReflectorFactory getReflectorFactory() {
    return reflectorFactory;
  }

  public Object getOriginalObject() {
    return originalObject;
  }

  public String findProperty(String propName, boolean useCamelCaseMapping) {
    return objectWrapper.findProperty(propName, useCamelCaseMapping);
  }

  public String[] getGetterNames() {
    return objectWrapper.getGetterNames();
  }

  public String[] getSetterNames() {
    return objectWrapper.getSetterNames();
  }

  public Class<?> getSetterType(String name) {
    return objectWrapper.getSetterType(name);
  }

  public Class<?> getGetterType(String name) {
    return objectWrapper.getGetterType(name);
  }

  public boolean hasSetter(String name) {
    return objectWrapper.hasSetter(name);
  }

  public boolean hasGetter(String name) {
    return objectWrapper.hasGetter(name);
  }

  public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return null;
      } else {
        return metaValue.getValue(prop.getChildren());
      }
    } else {
      return objectWrapper.get(prop);
    }
  }

  public void setValue(String name, Object value) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        if (value == null) {
          // don't instantiate child path if value is null
          return;
        } else {
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      metaValue.setValue(prop.getChildren(), value);
    } else {
      objectWrapper.set(prop, value);
    }
  }

  public MetaObject metaObjectForProperty(String name) {
    Object value = getValue(name);
    return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
  }

  public ObjectWrapper getObjectWrapper() {
    return objectWrapper;
  }

  public boolean isCollection() {
    return objectWrapper.isCollection();
  }

  public void add(Object element) {
    objectWrapper.add(element);
  }

  public <E> void addAll(List<E> list) {
    objectWrapper.addAll(list);
  }

}

看源码可以知道,这个MetaObject有点类似于一个反射工具类,其实现也是利用的反射获取实例的 getter 和 setter 方法进行赋值。而我们用的最多的就是getValue()和setValue();下面我们来看看有哪些属性,我们是可以获得的。

在这里插入图片描述

目前我只成功了拿boundSql属性

BoundSql对象的属性:

private final String sql;//sql语句
private final List<ParameterMapping> parameterMappings;//参数映射
private final Object parameterObject;//参数
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;//源参数

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

five-five

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值