ICode9

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

mybatis源码学习之插件

2021-01-26 22:33:46  阅读:123  来源: 互联网

标签:插件 拦截器 Object 源码 mybatis 拦截 class


mybatis源码学习之插件

mybatis插件

mybatis为我们提供了插件,并提供了其为可扩展

可以增强我们使用的灵活性,可以结合项目需求对框架进行拓展

目前我们可以基于mybatis插件实现分页、分表、监控等功能

mybatis插件介绍

mybatis有四大组件

  • Executor(执行器,负责一些增删改查)
  • StatementHandler(sql语法构建器,负责sql预编译)
  • paramterHandler(参数处理器)
  • ResultSetHandler(结果集处理器)

mybatis对持久层的操作就是借助着四大核心对象,我们可以对着四大对象进行拦截加强处理

也就相当于四大组件的拦截器,增强功能的本质上是底层的动态代理实现的

也就是说mybatis的四大组件核心对象就是代理对象

mybatis允许我们拦截的方法如下:

  • Executor: update、query、commit、rollback等
  • StatementHandler: prepare、paramterize、batch、update、query等
  • paramterHandler: getParameterObject、setParamter
  • ResultSetHandler: handleResultSets、handleOutputParamters等

mybatis插件原理

在四⼤核心对象创建的时候
1、每个创建出来的对象不是直接返回的,⽽是interceptorChain.pluginAll(parameterHandler)处理后的
2、获取到所有的Interceptor (拦截器),调⽤ interceptor.plugin(target);返回 target 包装后的对象
3、插件机制,AOP (⾯向切⾯)我们的插件可以为四⼤对象创建出代理对象,代理对象就可以拦截到四⼤对象的每⼀个执⾏;

public ParameterHandler newParameterHandler(MappedStatement mappedStatement,
Object object, BoundSql sql, InterceptorChain interceptorChain){
    // 此处已经创建好了ParameterHandler对象
    ParameterHandler parameterHandler =
    mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
    // 此处interceptorChain.pluginAll(parameterHandler)处理 生成代理对象
    parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler);
    // 返回的也就是代理对象
    return parameterHandler;
}
public Object pluginAll(Object target) {
    // 获取我们的所有拦截器
    for (Interceptor interceptor : interceptors) {
        // 参数传入的target就是ParameterHandler的原生对象
        // plugin对原生对象进行处理  就是为当前的原生对象生成一个代理对象
        target = interceptor.plugin(target);
    }
    return target;
}

如果我们要做一个拦截器应该怎么做呢
假设我们要拦截Executor的query方法

首先要创建一个拦截器 这个拦截器要实现Interceptor接口

// 首先加入Intercepts注解代表这是一个拦截器
@Intercepts({
    // 这个拦截器具体拦截了什么
    @Signature(
        // 要拦截的类
        type = Executor.class,
        // 要拦截的方法名
        method = "query",
        // 因为会有方法重载 需要标识拦截方法的参数
        args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})
})
public class ExecutorPlugin implements Interceptor {
    // 具体拦截实现
}

然后需要将我们的ExecutorPlugin拦截器配置到核心配置文件中

<plugins>
    <plugin interceptor="com.hen84.plugin.ExecuPlugin">
    </plugin>
</plugins>

这样在我们启动时就可以加载到我们自定义的插件,并保存插件实例到拦截器链中,在我们执行sql时 Executor实例被创建后 mybatis就会为其生成动态代理类,这样在执行query方法前就会调用我们的拦截器里的加强逻辑

接下来我们手动演示一下

自定义插件

package com.hen84.plugin;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Properties;

@Intercepts({
        // 这个拦截器具体拦截了什么
        @Signature(
                // 要拦截的类
                type = Executor.class,
                // 要拦截的方法名
                method = "query",
                // 因为会有方法重载 需要标识拦截方法的参数
                args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class ExecutorPlugin implements Interceptor {

    // 具体的加强操作 只要被拦截的query方法执行时,就会先调用该方法
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("开始增强.........");
        return invocation.proceed();//意思就是让原方法执行
    }

    // 把当前拦截器放到拦截器链中
    public Object plugin(Object o) {
        System.out.println("把当前拦截器放到拦截器链中"+o);
        Object wrap = Plugin.wrap(o, this);
        return wrap;
    }

    // 配置文件的参数 
    public void setProperties(Properties properties) {
        System.out.println("properties" + properties);
    }
}
<plugins>
    <plugin interceptor="com.hen84.plugin.ExecutorPlugin">
        <property name="测试" value="123456"/>
    </plugin>
</plugins>

这样每当我们执行sql语句之前都会先执行我们的加强方法

pageHelper

pageHelper是mybatis为我们提供的分页插件,那么他怎样使用呢

导⼊PageHelper坐标

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>

在核心配置文件中配置PageHelper插件

<plugin interceptor="com.github.pagehelper.PageHelper">
    <!—指定⽅⾔ —>
    <property name="dialect" value="mysql"/>
</plugin>

附上pageHelper的部分代码

// 能够看到和我们的配置方法是一样的
// 也就是在每次执行Executor的query方法前都会调用intercept加强方法
@Intercepts({@Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class PageHelper implements Interceptor {
    
}

演示

InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 分页查询   startPage(要查询第几页,一页有多少条数据)
PageHelper.startPage(1,1);
List<User> users = userMapper1.selectList();

控制台打印

// PageHelper为我们做了什么事
// 1 先查询总条数
17:44:18,598 DEBUG selectList_PageHelper_Count:159 - ==>  Preparing: SELECT count(*) FROM t_user 
17:44:18,912 DEBUG selectList_PageHelper_Count:159 - ==> Parameters: 
17:44:19,127 DEBUG selectList_PageHelper_Count:159 - <==      Total: 1
// 根据我们PageHelper.startPage(1,1);直接改变我们的sql完成分页查询
17:44:19,129 DEBUG selectList_PageHelper:159 - ==>  Preparing: select user_id userId,user_name userName from t_user limit ?,? 
17:44:19,130 DEBUG selectList_PageHelper:159 - ==> Parameters: 0(Integer), 1(Integer)
17:44:19,135 DEBUG selectList_PageHelper:159 - <==      Total: 1

通用mapper插件

通⽤Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发⼈员不需要编写SQL,不需要
在DAO中增加⽅法,只要写好实体类,就能⽀持相应的增删改查⽅法

导入坐标

<dependency>
	<groupId>tk.mybatis</groupId>
	<artifactId>mapper</artifactId>
	<version>3.1.2</version>
</dependency>

在核心配置文件中完成配置

<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<!-- 通⽤Mapper接⼝,多个通⽤接⼝⽤逗号隔开 -->
    <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>

实例演示

// 指定sql表
@Table(name = "t_user")
public class User implements Serializable {

    @Id // 指定主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 指定自动增长
    private Long userId;

    @Column(name = "user_name")  //指定sql字段名称
    private String userName;

    @Column(name = "user_phone")  //指定sql字段名称
    private String userPhone;
    
    @Transient // 表明这个不是数据库的字段
    private String noSql;
}
// 实现我们指定的通用mapper接口
// mapper接口中有为我们写好的各种单表查询方法
public interface UserMapper extends Mapper<User> {

}

如果这些写好的sql并不能满足mybatis还为我们提供了自定义查询

//example⽅法
Example example = new Example(User.class);
// 设置条件 id = 1
example.createCriteria().andEqualTo("id", 1);
//⾃定义查询
List<User> users = userMapper.selectByExample(example);
}

学习收获

1、mybatis可拦截处理方法都有哪些
2、mybatis自定义插件机制
3、对平常使用的pageHelper和通用Mapper有了更深刻的印象

标签:插件,拦截器,Object,源码,mybatis,拦截,class
来源: https://blog.csdn.net/weixin_44146930/article/details/113198888

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

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

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

ICode9版权所有