插件开发
插件的运行原理
-
在创建四大对象的时候每个创建出来的对象不是直接返回的,而是执行了 interceptorChain.pluginAll(parameterHandler);
-
获取到所有的Interceptor(拦截器),插件需要实现的接口调用interceptor.plugin(target);返回target包装后的对象
-
插件机制,我们可以使用插件为目标对象创建一个代理对象(AOP思想)
-
我们的插件可以为四大对象创建出代理对象
-
代理对象就可以拦截到四大对象的每一个执行
-
编写插件的步骤
-
编写Interceptor的实现类
-
继承Interceptor实现其中的方法
-
intercept(Invocation invocation):拦截目标对象的目标方法的执行
-
invocation.proceed(),放行,执行目标方法
-
-
-
plugin(Object target):包装目标对象,为目标对象创建一个代理对象
-
setProperties(Properties properties):将插件运行时的属性设置进来
- 在自定义的插件类上使用@Intercepts告诉mybatis拦截的方法,完成插件的签名

需要填写一个Signature数组,继续点击

例如要拦截StatementHandler,它的里面有下列的方法:

-
将写好的插件注册到全局配置文件中
<!-- 注册插件 --> <plugins> <plugin interceptor="com.jx.mbg.dao.MyPlugin"> <!-- 还可以传一些值 --> <!-- 这些properties会包装到Properties中,可以在重写的setProperties()方法中看到 @Override public void setProperties(Properties properties) { System.out.println("插件配置的信息" + properties); } --> <property name="username" value="root"/> <property name="password" value="123"/> </plugin> </plugins>
完整的信息
package com.jx.mbg.dao;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Statement;
import java.util.Properties;
/**
* 完成插件签名,告诉mybatis当前拦截用来拦截哪个对象的哪个方法
*/
@Intercepts({
@Signature(type = StatementHandler.class, method = "parameterize", args = Statement.class)
})
public class MyPlugin implements Interceptor {
/**
* 拦截目标方法
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取要拦截的方法
System.out.println("MyPlugin...interceptor:" + invocation.getMethod());
// 放行,执行目标方法
Object proceed = invocation.proceed();
return proceed;
}
/**
* 包装目标对象,为目标对象创建一个代理对象
* @param target
* @return
*/
@Override
public Object plugin(Object target) {
System.out.println("mybatis将要包装的对象:" + target);
// 可以借助Plugin的wrap方法来使用我们当前的Interceptor包装我们的目标对象
Object wrap = Plugin.wrap(target, this);
// 返回为当前target创建放入动态代理
return wrap;
}
/**
* 将插件注册时的属性设置进来
* @param properties
*/
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置的信息" + properties);
}
}

多插件运行

多个插件就会产生多层代理
在拦截StatementHandler的时候,第一个插件会先拦截,产生一个代理对象。而这个代理对象又会交给第二个插件拦截,又产生一个代理对象。会产生多层代理
因此在执行目标方法的时候会先进最外层的代理对象,也就是先执行第二个插件的interceptor()方法,再执行第一个插件的interceptor()方法,最后执行真正目标对象的方法
举个栗子
动态改变sql运行参数,例:以前查询1号员工,在插件中更改为3号员工

@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取要拦截的对象,StatementHandler
Object target = invocation.getTarget();
// 拿到:StatementHandler ==> ParameterHandler ==> parameterObject的值
// 拿到target的元数据
MetaObject metaObject = SystemMetaObject.forObject(target);
// 获取参数值
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("SQL语句用的参数是:" + value);
// 修改参数值
metaObject.setValue("parameterHandler.parameterObject", 3);
// 放行,执行目标方法
Object proceed = invocation.proceed();
return proceed;
}
使用PageHelper
在知道了插件的运行原理后,就可以尝试着自定义一些插件来完成一些功能,有一款比较出色的插件,叫做PageHelper,使用它就可以帮我们完成分页功能
1、导入jar包或者maven依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.8</version> </dependency>
2、在全局配置文件中配置插件
<!-- 配置pageHelper --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> </plugin>
参数设置参考:
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
3、使用
只需要在查询方法前加上PageHelper.startPage(int start, int size); 即可
注意:这一方法会返回分页的所有信息
@Test
public void test3() throws IOException {
String configPath = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(configPath);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
/**
* 在查询前加上该方法,startPage的参数
* 参数一:起始的页数,第一页就是1
* 参数二:要查询的记录数
* 该方法会返回一个分页对象,可以查看分页的信息
*/
Page<Object> objects = PageHelper.startPage(1, 2);
List<Employee> employees = mapper.selectAll();
employees.forEach(System.out::println);
// 打印分页信息
System.out.println("当前页码:" + objects.getPageNum());
System.out.println("总记录数:" + objects.getTotal());
System.out.println("每页的记录数:" + objects.getPageSize());
System.out.println("当前页码:" + objects.getPages());
}
}
当然,也可以使用PageInfo获取分页信息,只需要把查询后的list传入即可
//获取第1页,10条内容,默认查询总数countPageHelper.startPage(1, 10);List<Country> list = countryMapper.selectAll();//用PageInfo对结果进行包装PageInfo page = new PageInfo(list);//测试PageInfo全部属性//PageInfo包含了非常全面的分页属性 assertEquals(1, page.getPageNum()); assertEquals(10, page.getPageSize()); assertEquals(1, page.getStartRow()); assertEquals(10, page.getEndRow()); assertEquals(183, page.getTotal()); assertEquals(19, page.getPages()); assertEquals(1, page.getFirstPage()); assertEquals(8, page.getLastPage()); assertEquals(true, page.isFirstPage()); assertEquals(false, page.isLastPage()); assertEquals(false, page.isHasPreviousPage()); assertEquals(true, page.isHasNextPage());
PageInfo还有一个更加强大的用法,,它提供了第二个参数,可以设置连续显示几页
什么意思?如果我们需要在前端做显示,经常会在下面显示几个按钮,上面是页码,比如连续显示5页,那么PageInfo就会实时的显示这些页码,例如:
当页面是第一页时:返回一个数组 [1, 2, 3, 4, 5]
第二页:[1, 2, 3, 4, 5]
第三页:[1, 2, 3, 4, 5]
第四页:[2, 3, 4, 5, 6] // 可以看到这里页面已经变化了
PageHelper.startPage(1, 2); List<Employee> employees = mapper.selectAll(); employees.forEach(System.out::println); // 这里连续显示5页 PageInfo<Employee> employeePageInfo = new PageInfo<>(employees, 5); // 拿到连续显示的页码 int[] navigatepageNums = employeePageInfo.getNavigatepageNums(); System.out.println(navigatepageNums); System.out.println(employeePageInfo.getNavigateFirstPage()); System.out.println(employeePageInfo.getNavigateLastPage());
有了这样一个数组,在前端做分页显示就非常方便了

浙公网安备 33010602011771号