Mybaits插件案例、源码分析及PageHelper源码分析

目录

Mybaits插件简介

MyBatis四大组件所允许拦截的⽅法

Mybaits插件开发案例

自定义插件源码分析

pagehelper源码分析

pagehelper案例展示

pagehelper源码分析


Mybaits插件简介

        为自己的框架提供插件和其他形式的扩展点,主要就是供开发者能自行拓展。比如可以做分页、分表、监控等功能。

        由于插件和业务无关,业务也无法感知插件的存在。

        因此可以无感植入插件,无形中增强功能。

        而Mybaits作为一个应用广泛的优秀的ORM开源框架,具有强大的灵活性,这个灵活性主要体现在四大组件上:Executor、StatementHandler、ParameterHandler、ResultSetHandler

        这四大组件提供了简单易用的插件扩展方式。

        Mybaits对持久层的操作就是借助四大核心对象。

        Mybaits支持用插件对四大核心对象进行拦截,从而实现功能的增强效果。

        增强功能的本质其实就是使用动态代理实现的。换句话说,Mybaits中的四大对象都是代理对象。

        这四个组件的关系图如下:

        

 

MyBatis四大组件所允许拦截的⽅法

        执⾏器 Executor (update、query、commit、rollback等⽅法);

        SQL语法构建器 StatementHandler (prepare、parameterize、batch、updates query等⽅ 法);

        参数处理器 ParameterHandler (getParameterObject、setParameters⽅法);

        结果集处理器 ResultSetHandler (handleResultSets、handleOutputParameters等⽅法);

Mybaits插件开发案例

        自己开发一个Mybaits插件还是比较简单的。

        第一步:先创建一个MyPlugin类,需要实现Mybatis自己写的接口Interceptor,具体可以看看下面的代码和注释

import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;


@Intercepts({
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
})
// 这个注解就是解释了当前我们写的这个拦截器主要是拦截mybaits四大组件中的哪个组件,
//     目前我们是在SQL语法构建时执行我们的增强方法 intercept()
// 也可以理解为是在mybaits执行的什么阶段去进行功能增强
public class MyPlugin implements Interceptor {

    /**
     * 拦截方法:只要被拦截的目标对象的目标方法被执行时,每次都会执行intercept方法
     * 看上面的注释可以得知我们配置的拦截方法是
     *      StatementHandler类中的prepare方法
     * 一旦该方法执行,就会被 这个方法执行
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("执行了增强方法");
        return invocation.proceed();
    }

    /**
     * 主要为了把当前的拦截器生成代理存到拦截器链中
     *
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        System.out.println("把当前的拦截器生成代理存到拦截器链中");
        Object wrap = Plugin.wrap(target, this);
        return wrap;
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println("获取到的配置文件的参数是:" + properties);
    }
}

        第二步:将我们自定义的插件,配置在sqlMapConfig.xml文件中,注意加入的位置顺序。

<!-- 自定义插件 -->
<plugins>
    <plugin interceptor="cn.dyc.components.demo.mybaits.quick.start.plugin.MyPlugin">
        <property name="name" value="xiaoxue"/>
    </plugin>
</plugins>

 

        第三步:当我们执行之前测试的方法调用Mybaits的查询方法时,可以看到已经成功了

                并且通过观察我们可以发现重写的三个方法的执行顺序为:

                        setProperties -> plugin -> intercept

 

自定义插件源码分析

        我们都知道在自定义插件的时候,需要先将插件加入到拦截器里:

                Object wrap = Plugin.wrap(target, this);

        那么我们就从Plugin.class入手

        这个类实现了InvocationHandler接口,也就是他会执行 invoker方法

        观察这个方法可以发现,在获取我们注解上配置的类、方法、参数后生成的Method对象,如果当前执行的这个方法正好是我们配置需要拦截的方法,那么就会调用interceptor.intercept方法,也就是之前我们重写的那个方法。

        这个插件还是比较简单的,本身没有特别复杂的逻辑,存储依赖于jdk中的 动态代理 + 反射 这两种技术来实现插件。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    Set<Method> methods = signatureMap.get(method.getDeclaringClass());
    if (methods != null && methods.contains(method)) {
      return interceptor.intercept(new Invocation(target, method, args));
    }
    return method.invoke(target, args);
  } catch (Exception e) {
    throw ExceptionUtil.unwrapThrowable(e);
  }
}

pagehelper源码分

        其实自定义的分析过了之后,基本原理也就搞明白了,剩下的就看怎么玩成花了。

        比如说用的很火的分页插件 pagehelper 怎么用呢?分析下!

pagehelper案例展示

        ①pom中加入依赖

<!-- 分页助手 -->
<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>

        ②把插件类配置到sqlMapConfig.xml中,PageHelper这个类怎么找到的,源码包中没几个类,唯一实现 interceptor 的就是这个类

    <!-- 自定义插件 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>

        ③执行测试类就成功了

@Test
public void pageTest() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);

    PageHelper.startPage(1, 1);
    List<User> list = userDao.findAll();
    PageInfo<User> userPageInfo = new PageInfo<>(list);
    System.out.println("总条数:" + userPageInfo.getTotal());
    System.out.println("总页数:" + userPageInfo.getPages());
    System.out.println("当前页:" + userPageInfo.getPageNum());
    System.out.println("每页显示的条数:" + userPageInfo.getPageSize());

    sqlSession.close();
}

pagehelper源码分析

        核心类就是实现了 interceptor 的 PageHelper

        这个方法里面有一个工具类 SqlUtil,真正去执行SQL的时候就是靠这个工具类去实现分页的

        在setProperties阶段,就已经把这个工具类实例化出来了,并且添加了他的方言,这也就是为什么我们在集成这个插件的时候在配置文件中需要加上那么一个<property name="dialect" value="mysql"/>的值了

        当执行intercept()方法时,就是执行在PageHelper上用各种静态方法赋值的参数交由sqlUtil这个类去执行,比如 PageHelper.startPage(1, 1); 设置他的当前页数和每页数据

        那么为什么执行 PageHelper.startPage 必须挨着 要执行的sql语句这个问题,大家应该也已经明白了吧

        SqlUtil会用静态方法把这个Page对象封装到本地线程中的page参数中,供后面执行sql语句是进行sql拼装

Page page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
SqlUtil.setLocalPage(page);
return page;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值