本文主要内容:

大多数框架都支持插件,用户可通过编写插件来自行扩展功能,Mybatis也不例外。
在Mybatis中最出名的就是PageHelper 分页插件,下面我们先来使用一下这个分页插件。
如何集成分页插件
Spring-Boot+Mybatis+PageHelper 。
引入pom依赖
com.github.pagehelper pagehelper-spring-boot-starter 1.2.3 
配置分页插件配置项
- pagehelper:
 - helperDialect: mysql
 - reasonable: true
 - supportMethodsArguments: true
 - params: count=countSql
 
service接口代码中
- PageInfo selectUsersByName(int pageIndex, int pageSize);
 
service实现类代码中
- @Override
 - public PageInfo selectUsersByName(int pageIndex, int pageSize) {
 - PageHelper.startPage(pageIndex, pageSize);
 - List
 users = userMapper.selectUsersByName(null); - return new PageInfo(users);
 - }
 
Mapper代码代码
- select * from m_user
 - `name` = #{userName}
 
- List
 selectUsersByName(@Param("userName") String userName); 
controller中代码
- @GetMapping("/user/name")
 - public PageInfo selectUsersByName(int pageIndex, int pageSize) {
 - return userService.selectUsersByName(pageIndex, pageSize);
 - }
 
然后我们访问
http://localhost:9002/user/name?pageIndex=1&pageSize=10
输出结果:
输出重要项说明:
我们在看看输出SQL:
发现其实执行了两条SQL:count和limit。
猜测分页插件实现
1.这个分页插件无非就是在我们的查询条件上拼接了个limit和做了一个count查询。
2.我们这里使用的是Mysql作为数据库,如果是Oracle的话那就不是limit了,所以这里有多重数据库对应的方案。
3.在没有此插件的前面拦截并做了sql和相关处理。
根据官网快速入门插件
下面是来自官网的一段话:
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。这些都是更底层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
那我们就尝试着按照官方来写一个插件。
自定义插件
- @Intercepts({@Signature(
 - type= Executor.class,
 - method = "update",
 - args = {MappedStatement.class,Object.class})})
 - public class TianPlugin implements Interceptor {
 - private Properties properties = new Properties();
 - @Override
 - public Object intercept(Invocation invocation) throws Throwable {
 - System.out.println("老田写的一个Mybatis插件--start");
 - Object returnObject = invocation.proceed();
 - System.out.println("老田写的一个Mybatis插件---end");
 - return returnObject;
 - }
 - }
 
然后把插件类注入到容器中。
这里的自定义完全是官网给出的案例。从自定义的插件类中看到有个update,我们猜测肯定是需要执行update才会被拦截到。
访问前面的代码:http://localhost:9002/updateUser
成功了。
这是大家肯定会联想到我们刚刚开始学动态代理的时候,不就是在要调用的方法的前面和后面做点小东东吗?
Mybatis的插件确实就是这样的。
我们来分析一下官方的那段话和我们自定义的插件。
分析
首先,我们自定义的插件必须是针对下面这四个类以及方法。
其次,我们必须实现Mybatis的Interceptor。
Interceptor中三个方法的作用:
plugin方法
- default Object plugin(Object target) {
 - return Plugin.wrap(target, this);
 - }
 
默认实现方法,里面调用了Plugin.wrap()方法。
- public class Plugin implements InvocationHandler {
 - private Object target;
 - private Interceptor interceptor;
 - private Map
 , Set > signatureMap; - private Plugin(Object target, Interceptor interceptor, Map
 , Set > signatureMap) { - this.target = target;
 - this.interceptor = interceptor;
 - this.signatureMap = signatureMap;
 - }
 - public static Object wrap(Object target, Interceptor interceptor) {
 - Map
 , Set > signatureMap = getSignatureMap(interceptor); - Class> type = target.getClass();
 - Class>[] interfaces = getAllInterfaces(type, signatureMap);
 - if (interfaces.length > 0) {
 - // 创建JDK动态代理对象
 - return Proxy.newProxyInstance(
 - type.getClassLoader(),
 - interfaces,
 - new Plugin(target, interceptor, signatureMap));
 - }
 - return target;
 - }
 - @Override
 - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 - try {
 - Set
 methods = signatureMap.get(method.getDeclaringClass()); - // 判断是否是需要拦截的方法(很重要)
 - if (methods != null && methods.contains(method)) {
 - // 回调intercept()方法
 - return interceptor.intercept(new Invocation(target, method, args));
 - }
 - return method.invoke(target, args);
 - } catch (Exception e) {
 - throw ExceptionUtil.unwrapThrowable(e);
 - }
 - }
 - //...省略其他不相关代码
 - }
 
这不就是一个JDK动态代理吗?
Map
所以,我们不要动不动就说反射性能很差,那是因为你没有像Mybatis一样去缓存一个对象的反射结果。
判断是否是需要拦截的方法,这句注释很重要,一旦忽略了,都不知道Mybatis是怎么判断是否执行拦截内容的,要记住。
Plugin.wrap(target, this)是干什么的?
使用JDK的动态代理,给target对象创建一个delegate代理对象,以此来实现方法拦截和增强功能,它会回调intercept()方法。
为什么要写注解?注解都是什么含义?
在我们自定义的插件上有一堆注解,别害怕。
Mybatis规定插件必须编写Annotation注解,是必须,而不是可选。
- @Intercepts({@Signature( type= Executor.class, method = "update",
 - args = {MappedStatement.class,Object.class})}
 - )
 - public class TianPlugin implements Interceptor {
 
@Intercepts注解:装载一个@Signature列表,一个@Signature其实就是一个需要拦截的方法封装。那么,一个拦截器要拦截多个方法,自然就是一个@Signature列表。
- type= Executor.class, method = "update",args = {MappedStatement.class,Object.class}
 
解释:要拦截Executor接口内的query()方法,参数类型为args列表。
那如果想拦截多个方法呢?
- @Documented
 - @Retention(RetentionPolicy.RUNTIME)
 - @Target(ElementType.TYPE)
 - public @interface Intercepts {
 - Signature[] value();
 - }
 
这就简单了吧,我们在@Intercepts注解中可以存放多个@Signature注解。
比如说前面分页插件中就是拦截多个方法的。
为什么拦截两个都是query方法呢?因为在Executor中有两个query方法。
总结下:
Mybatis规定必须使用@Intercepts注解。
@Intercepts注解内可以添加多个类多个方法,注意方法名和参数类型个数一定要对应起来。
本文转载自微信公众号「 Java后端技术全栈」,可以通过以下二维码关注。转载本文请联系 Java后端技术全栈公众号。
                网站标题:MyBatis插件原理分析,看完感觉自己better了
                
                标题链接:http://www.csdahua.cn/qtweb/news18/324168.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网