拦截器实现分页2
使用拦截器实现分页的前提需要有一个执行查询的方法,这里在serviceImpl中就不再调用查询所有记录的方法了(原来调用查询所有是要计算出Page的其他属性),这里只是把Page参数传入到Dao中,在配置文件sql中定义的查询是查询所有的记录并没有实现分页功能的。无论是自己实现还是拦截器实现分页都需要传入Page对象到配置文件sql中。
定义拦截器:拦截器根据功能的不同,可以不止一个,创建拦截器的包。
在该包下创建拦截器类,命名为PageInterceptor,Mybatis提供了拦截的操作,可以实现分页的功能,所以要实现Mybatis的接口Interceptor(该接口是org.apache.ibatis.plugin.Interceptor包下的)。

首先该拦截器需要对Mybatis的xml文件中执行的指定的sql语句进行拦截,并转换为分页的sql语句(拦截的时间是在获得PreparedStatement对象之前,拦截的地点是在Mybatis源码中)。
Mybatis获取PreparedStatement对象是在StatementHandler接口(org.apache.ibatis.executor.statement)中,该接口中的prepare方法返回的是Statement对象。

该接口的实现类有两个(BaseStatementHandler、RoutingStatementHandler),该接口的实现类是BaseStatementHandler中,statement是通过该方法中的instantiateStatement(connection)获取到的。
instantiateStatement方法是一个抽象方法(BaseStatementHandler接口也有三个实现类CallableStatementHandler、PreparedStatementHandler、SimpleStatementHandler),该方法是在PreparedStatementHandler实现类中实现的,如下图就和JDBC中相似了,拦截器的拦截位置就是在这里。

拦截器是通过Mybatis提供的注解来拦截到该位置:也就是在拦截器类的上添加@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})}),type指的是拦截接口的class,method指向拦截的方法。args指的是拦截方法中需要传入参数的类类型。

这样该类对应的实现代码,在执行sql语句之前就可对源码的功能进行修改,因为该实现代码在执行之前已经获取到sql语句,该实现代码就会修改该sql语句并返回给Mybatis。

拦截器类实现的接口需要实现三个方法:
plugin方法需要传入一个Object参数,如果该对象符合拦截的规则(规则就是拦截器类上的注解信息),就会返回一个代理,该代理就可以使用分页的功能,这个代理通过Plugin.wrap(Object对象,this)方法返回,this指的是当前拦截器实例。
可以通过查看wrap方法的源码,它是由Mybatis实现的,该方法调用getSignatureMap(拦截器实例)
getSignatureMap(Interceptor interceptor)方法通过拦截器实例获取到拦截器类上写的注解的信息(该注解的作用在于就在于这里),通过这个注解就可以找到拦截的类型
如果传入的对象符合了拦截器的规则,就会执行拦截器的功能,修改相应的sql语句。通过interceptor(Invocation invocation)方法。如果不符合规则就不会执行interceptor方法,因为没有获取到代理类。
