Mybatis之Interceptor原理分析

Mybatis之Interceptor原理分析

Interceptor介绍

Mybatis提供了一种插件plugin来实现拦截器功能,Interceptor是Mybatis的一个拦截是接口,通过实现Interceptor可以实现对sql的增强。例如加解密数据,分页等功能
Executor,ResultSetHandler,StatementHandler,ParameterHandler,这是Mybatis中的四大对象,也是拦截器的切入点。我们可以基于这四大对象的方法进行增强
MyBatis 允许使用插件来拦截的方法调用包括:

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

总体概括为:

  1. 拦截执行器的方法
  2. 拦截参数的处理
  3. 拦截结果集的处理
  4. 拦截sql语法构建的处理

Interceptor具体使用

Interceptor在工程中具体使用流程,下面已添加解密的dbKey为例进行分析
在执行任何sql语句是,都会在参数中添加dbKey这个参数

1.新增实现类

这个拦截器拦截StatementHandler接口的prepare方法,所有执行prepare方法都会被拦截

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MybatisStmtInterceptor implements Interceptor {
    private String dbKey = null;
    @Override
    public Object intercept(Invocation invocation)
            throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        // 所有sql执行语句的参数中都增加一个dbKey参数
        boundSql.setAdditionalParameter("dbKey", dbKey);
        Object result = invocation.proceed();
        return result;
    }
    @Override
    public void setProperties(Properties properties) {
        dbKey = properties.getProperty("dbKey");
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
}

这三个方法的执行分别是setProperties,plugin,intercept
其中,setProperties是在spring启动加载plugins成为bean时调用的,可以从中获取Properties中获取Interceptor配置的对应属性供拦截器使用(譬如在上述代码中只能获取dbKey,是因为配置文件中该Interceptor只配置了dbKey)

2.配置文件配置拦截器

在Spring的配置文件中能够添加

    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configuration">
            <bean class="org.apache.ibatis.session.Configuration">
                <property name="mapUnderscoreToCamelCase" value="true"/>
            </bean>
        </property>
        <!-- ORM对象存放的包地址 -->
        <property name="typeAliasesPackage"
                  value="com.alliance.openhde.dao.bean"/>
        <!-- sqlmap配置文件地址 -->
        <property name="mapperLocations"
                  value="classpath:mybatis/mapping/*.xml"/>
        <property name="plugins">
            <array>
                <bean class="com.commons.plugin.MybatisStmtInterceptor">
                    <property name="properties">
                        <value>dbKey=${db_key}</value>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

这两部配置好后,拦截器即配置成功,以后在工程中就不用关心dbKey的问题,所有的sql参数都会自动默认添加dbKey这个参数。

源码分析

1.读取配置文件plugins加载到内存中


在工程启动时会通过SqlSessionFactoryBuilder.builder方法进行加载配置文件,解析xml文件,关键代码
如下
解析plugins标签

XMLConfigBuilder.java
 private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

通过反射生成Interceptor对象,把Interceptor对象添加到InterceptorChain中的Interceptor数组中

XMLConfigBuilder.java
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
        Iterator var2 = parent.getChildren().iterator();
        while(var2.hasNext()) {
            XNode child = (XNode)var2.next();
            String interceptor = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance();
            interceptorInstance.setProperties(properties);
            this.configuration.addInterceptor(interceptorInstance);
        }
    }
}
Configuration.java
public void addInterceptor(Interceptor interceptor) {
    this.interceptorChain.addInterceptor(interceptor);
}
InterceptorChain.java
private final List<Interceptor> interceptors = new ArrayList();
public void addInterceptor(Interceptor interceptor) {
    this.interceptors.add(interceptor);
}

可以看出Interceptor 实例化后最终添加到InterceptorChain类中的Interceptor的list中供后续代码使用

2.执行sql的命令时从内存中获取拦截器,执行拦截操作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值