Mybatis源码剖析之插件interceptor执行原理

预读

mybatis通过插件 对(Executor、StatementHandler、ParameterHandler、ResultSetHandler) 这四个 核心对象创建代理进行拦截

对mybatis来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的 动态代理实现的,换句话说,MyBatis中的四大对象都是代理对象

Mybatis核心对象介绍

MyBatis的主要的核心部件有以下几个:

  • Configuration 初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如,插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中

  • SqlSessionFactory SqlSession工厂

  • SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能

  • Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护

  • StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。

  • ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数,

  • ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

  • TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换

  • MappedStatement MappedStatement维护了一条节点的封装,

  • SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回

  • BoundSql 表示动态生成的SQL语句以及相应的参数信息

准备

/**
 * 自定义mybatis拦截器
 */
@Intercepts({
        // 拦截指定接口的指定方法
        @Signature(type= StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class}),
//        @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class StatementInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("对方法进行增强....");
        // 执行原方法
        Object result = invocation.proceed();
        return result;
    }

    @Override
    public Object plugin(Object target) {
        // 调用插件,创建目标类的代理对象
        System.out.println("为 "+target+" 创建代理对象");
        return Plugin.wrap(target,this);
    }

    // 为拦截器设置参数值
    @Override
    public void setProperties(Properties properties) {
    }
}

<configuration>

    
    <properties resource="jdbc.properties">properties>

    




    
    <typeAliases>
        
        
        
        <package name="com.xiaoxu.pojo"/>
    typeAliases>

    <plugins>
        <plugin interceptor="com.xiaoxu.interceptor.StatementInterceptor">plugin>
    plugins>


    
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC">transactionManager>
            
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        
        <package name="com.xiaoxu.mapper"/>
    mappers>
configuration>

Mybatis源码剖析之插件interceptor执行原理_第1张图片
Mybatis源码剖析之插件interceptor执行原理_第2张图片

interceptor执行原理剖析

1、mybatis在解析配置文件的时候,会创建拦截器,放入configuration中

/**
     * 解析 XML
     *
     * 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
     *
     * @param root 根节点
     */
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 解析  标签
            propertiesElement(root.evalNode("properties"));
            // 解析  标签
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            // 加载自定义的 VFS 实现类
            loadCustomVfs(settings);
            // 解析  标签
            typeAliasesElement(root.evalNode("typeAliases"));
            // 解析  标签
            pluginElement(root.evalNode("plugins"));
            // 解析  标签
            objectFactoryElement(root.evalNode("objectFactory"));
            // 解析  标签
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 解析  标签
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // 赋值  到 Configuration 属性
            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);
        }
    }

Mybatis源码剖析之插件interceptor执行原理_第3张图片

在这里插入图片描述

Configuration中的interceptorChain

/**
 * 拦截器链
 *
 * @author Clinton Begin
 */
public class InterceptorChain {

    /**
     * 拦截器数组
     */
    private final List<Interceptor> interceptors = new ArrayList<>();

    /**
     * 应用所有插件
     *
     * @param target 目标对象
     * @return 应用结果
     */
    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);
    }

}

2、在创建sqlSession的时候,会创建executor,从Configuration中获取拦截器链,遍历拦截器链interceptorChain,对executor进行代理

Mybatis源码剖析之插件interceptor执行原理_第4张图片
Mybatis源码剖析之插件interceptor执行原理_第5张图片

Mybatis源码剖析之插件interceptor执行原理_第6张图片
Mybatis源码剖析之插件interceptor执行原理_第7张图片

同理 StatementHandler,paramenterHandler,ResultHandler也会通过拦截器链的方式创建代理对象,这些handler默认是需要的时候创建,而不是一开始就创建

// 创建 ParameterHandler 对象
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        // 创建 ParameterHandler 对象
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        // 应用插件
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }

    // 创建 ResultSetHandler 对象
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
                                                ResultHandler resultHandler, BoundSql boundSql) {
        // 创建 DefaultResultSetHandler 对象
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        // 应用插件
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
    }

    // 创建 StatementHandler 对象
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 创建 RoutingStatementHandler 对象
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        // 应用插件
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }

3、获取代理对象,代理对象调用方法,默认通过executor执行,默认先cacheExecutor,然后 baseExecutor

Mybatis源码剖析之插件interceptor执行原理_第8张图片

二级缓存所在的cacheExecutor
Mybatis源码剖析之插件interceptor执行原理_第9张图片

一级缓存所在的baseExecutor
Mybatis源码剖析之插件interceptor执行原理_第10张图片

4、通过executor执行,默认会创建statmentHandler处理sql编译,参数设置,结果处理‘

Mybatis源码剖析之插件interceptor执行原理_第11张图片

预编译sql,由于是代理对象statementHandler,那么走invoke

Mybatis源码剖析之插件interceptor执行原理_第12张图片
Mybatis源码剖析之插件interceptor执行原理_第13张图片

由于我们指定了要对StatementHandler的prepare方法进行拦截调用,所以此时invoke方法中会判断是否 对该方法进行拦截。 如果拦截的方法是目标方法,那么走拦截器

Mybatis源码剖析之插件interceptor执行原理_第14张图片

拦截器增强方法

Mybatis源码剖析之插件interceptor执行原理_第15张图片

走完增强方法,走原方法

/**
 * 方法调用信息
 *
 * @author Clinton Begin
 */
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 getTarget() {
        return target;
    }

    public Method getMethod() {
        return method;
    }

    public Object[] getArgs() {
        return args;
    }

    /**
     * 调用方法
     *
     * @return 调用结果
     */
    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(target, args);
    }

}

总结

Mybatis源码剖析之插件interceptor执行原理_第16张图片

你可能感兴趣的:(Mybatis源码,Java源码,Mybatis,java,Mybatis源码)