请结合上一边  >>>  Mybatis 插件原理 <<<  进行查看

Mybatis 中自己定义了一个 自己的插件类接口 InnerInterceptor

 其内部实现了一些现成的插件,

如 : PaginationInnerInterceptor 、租户插件: TenantLineInnerInterceptor (行及的,居于字段进行数据隔了)、乐观锁插件: OptimisticLockerInnerInterceptor  等等 …

 

 

配置使用,使用过程中,将插添加(注册)到 服务中即可,注入到 IOC

  * MP 新的配置插件配置
     * 官网说明:@see { https://baomidou.com/pages/2976a3/#mybatisplusinterceptor }
     *
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        /**
         * 乐观锁
         */
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        /**
         * 分页插件
         */
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        /**
         * 防止全表更新与删除
         */
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        /**
         * 数据权限返回插件
         */
        interceptor.addInnerInterceptor(new DataScopeInterceptor());
        /**
         * 表别名处理插件(自定义的)
         */
        interceptor.addInnerInterceptor(new AliasInterceptor());
        /**
         * 年月时间处理插件(自定义的)Java类 YearMonth ( yyyy-MM )与数据库 date ( yyyy-MM-dd ) 映射处理
         */
        interceptor.addInnerInterceptor(new YearMonthInterceptor());

        log.info("########### Mybatis-Plus 插件装配完毕 ###########");

        return interceptor;
    }

 

插件调用原理:(直接看源码)

@Intercepts(
        {
                @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
                @Signature(type = StatementHandler.class, method = "getBoundSql", args = {}),
                @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)
public class MybatisPlusInterceptor implements Interceptor {

    // 配置中的所有的 MP 插件都注册到此集合中,
    @Setter
    private List<InnerInterceptor> interceptors = new ArrayList<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // b被代理的目标对象
        Object target = invocation.getTarget();
        // 代理执行的参数
        Object[] args = invocation.getArgs();
        // 此插件同时监听了 StatementHandler 和 Executor 所以需要进行代理对象判断
        if (target instanceof Executor) {
            // 执行的是 Executor 的逻辑
            final Executor executor = (Executor) target;
            Object parameter = args[1];
            // Executor 的 update 函数中只有两个参数,若参数length ==2 ,则断定为 update 逻辑
            boolean isUpdate = args.length == 2;
            // Executor 的三个方法(update、query、query) 首个参数都是 MappedStatement 类型
            MappedStatement ms = (MappedStatement) args[0];

            // 执行的是 SELECT 语句
            if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {
                // Executor 的两个 query 方法中,第三个参数可以确定的 RowBounds 类
                RowBounds rowBounds = (RowBounds) args[2];

                // Executor 的两个 query 方法中,第四个参数可以确定的 ResultHandler 类
                ResultHandler resultHandler = (ResultHandler) args[3];
                BoundSql boundSql;
                if (args.length == 4) {
                    // Executor 的 4个参数的那个 query 函数
                    boundSql = ms.getBoundSql(parameter);
                } else {
                    // Executor 的 6个参数的那个 query,函数几乎不可能走进这里面,除非使用Executor的代理对象调用query[args[6]]
                    boundSql = (BoundSql) args[5];
                }

                // MP 自行封装的 InnerInterceptor 类集合,进行遍历调用插件逻辑
                for (InnerInterceptor query : interceptors) {
                    /**
                     * 插件中进行判断是否需要执行 Executor.query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)
                     * 默认值为 true 为不执行
                     */
                    if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
                        // 返回空的
                        return Collections.emptyList();
                    }
                    // 执行查询的前置操作,可以对 SQL 进行一些修改
                    query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                }
                // 进行一些缓存的操作逻辑
                CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
                return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            } else if (isUpdate) {
                // 执行的是 Executor 中的 update 方法,遍历执行 MP 中定义的注册的插件
                for (InnerInterceptor update : interceptors) {
                    /**
                     * 判断是否执行 Executor.update(MappedStatement, Object)
                     * 如果不执行update操作,则影响行数的值为 -1
                     */
                    if (!update.willDoUpdate(executor, ms, parameter)) {
                        // 如果不执行update操作,则影响行数的值为 -1
                        return -1;
                    }
                    // 需要执行 Executor.update(MappedStatement, Object)
                    update.beforeUpdate(executor, ms, parameter);
                }
            }
        } else {
            // 执行的是 StatementHandler 的逻辑
            final StatementHandler sh = (StatementHandler) target;
            // 目前只有StatementHandler.getBoundSql方法args才为null
            if (null == args) {
                /**
                 * 遍历执行 MP 封装的插件,没有参数,则执行的是 StatementHandler.getBoundSql() 函数
                 * 遍历执行 MP 中定义的注册的插件
                 */
                for (InnerInterceptor innerInterceptor : interceptors) {
                    innerInterceptor.beforeGetBoundSql(sh);
                }
            } else {
                /**
                 * 有参数,则执行的是 StatementHandler.prepare(Connection connection, Integer transactionTimeout)
                 */
                Connection connections = (Connection) args[0];
                // prepare 方法中两个参数的类型都是可以确定的
                Integer transactionTimeout = (Integer) args[1];
                // 遍历执行 MP 中定义的注册的插件
                for (InnerInterceptor innerInterceptor : interceptors) {
                    /**
                     * StatementHandler.prepare(Connection, Integer) 操作前置处理
                     * 可以对 SQL 进行一些修改
                     */
                    innerInterceptor.beforePrepare(sh, connections, transactionTimeout);
                }
            }
        }
        // 调用代理执行目标方法,流程回归到 Mybatis 中
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor || target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    public void addInnerInterceptor(InnerInterceptor innerInterceptor) {
        this.interceptors.add(innerInterceptor);
    }

    public List<InnerInterceptor> getInterceptors() {
        return Collections.unmodifiableList(interceptors);
    }

    /**
     * 使用内部规则,拿分页插件举个栗子:
     * <p>
     * - key: "@page" ,value: "com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"
     * - key: "page:limit" ,value: "100"
     * <p>
     * 解读1: key 以 "@" 开头定义了这是一个需要组装的 `InnerInterceptor`, 以 "page" 结尾表示别名
     * value 是 `InnerInterceptor` 的具体的 class 全名
     * 解读2: key 以上面定义的 "别名 + ':'" 开头指这个 `value` 是定义的该 `InnerInterceptor` 属性需要设置的值
     * <p>
     * 如果这个 `InnerInterceptor` 不需要配置属性也要加别名
     */
    @Override
    public void setProperties(Properties properties) {
        PropertyMapper pm = PropertyMapper.newInstance(properties);
        Map<String, Properties> group = pm.group(StringPool.AT);
        group.forEach((k, v) -> {
            InnerInterceptor innerInterceptor = ClassUtils.newInstance(k);
            innerInterceptor.setProperties(v);
            addInnerInterceptor(innerInterceptor);
        });
    }
}

 

原文地址:http://www.cnblogs.com/Alay/p/16837717.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性