关注“Java后端技术全栈”
回复“面试”获取全套面试材料
本文次要内容:
大多数框架都反对插件,用户可通过编写插件来自行扩大性能,Mybatis也不例外。
在Mybatis中最闻名的就是PageHelper 分页插件,上面咱们先来应用一下这个分页插件。
如何集成分页插件
Spring-Boot+Mybatis+PageHelper 。
引入pom依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.3</version></dependency>
配置分页插件配置项
pagehelper: helperDialect: mysql reasonable: true supportMethodsArguments: true params: count=countSql
service接口代码中
PageInfo selectUsersByName(int pageIndex, int pageSize);
service实现类代码中
@Overridepublic PageInfo selectUsersByName(int pageIndex, int pageSize) { PageHelper.startPage(pageIndex, pageSize); List<User> users = userMapper.selectUsersByName(null); return new PageInfo(users);}
Mapper代码代码
<select id="selectUsersByName" resultMap="User"> select * from m_user <where> <if test="userName != null and userName != ''"> `name` = #{userName} </if> </where></select>
List<User> 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
输入后果:
输入重要项阐明:
- pageNum:以后页码。
- pageSize:每页数。
- list:就是咱们返回的业务数据。
- total:总数据。
- hasNextPage:是否存在下一页。
咱们在看看输入SQL:
发现其实执行了两条SQL
:count和limit。
猜想分页插件实现
1.这个分页插件无非就是在咱们的查问条件上拼接了个limit和做了一个count查问。
2.咱们这里应用的是Mysql作为数据库,如果是Oracle的话那就不是limit了,所以这里有多重数据库对应的计划。
3.在没有此插件的后面拦挡并做了sql和相干解决。
依据官网疾速入门插件
上面是来自官网的一段话:
MyBatis 容许你在映射语句执行过程中的某一点进行拦挡调用。默认状况下,MyBatis 容许应用插件来拦挡的办法调用包含:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中办法的细节能够通过查看每个办法的签名来发现,或者间接查看 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
的插件的确就是这样的。
咱们来剖析一下官网的那段话和咱们自定义的插件。
剖析
首先,咱们自定义的插件必须是针对上面这四个类以及办法。
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
其次,咱们必须实现Mybatis的Interceptor。
Interceptor中三个办法的作用:
- intercept():执行拦挡内容的中央,比方:在调用某类办法前后做一些本人的解决,简略就是打印日志。
- plugin():决定是否触发intercept()办法。
- setProperties():给自定义的拦截器传递咱们配置的属性参数(这个能够临时不论他,前面咱们写一个绝对残缺点的插件,你就明确是干啥的了)。
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<Class<?>, Set<Method>> signatureMap; private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) { this.target = target; this.interceptor = interceptor; this.signatureMap = signatureMap; } public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> 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<Method> 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<Class<?>, Set> signatureMap:缓存需拦挡对象的反射后果,防止屡次反射,即target的反射后果。
所以,咱们不要动不动就说反射性能很差,那是因为你没有像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注解内能够增加多个类多个办法,留神办法名和参数类型个数肯定要对应起来。
举荐浏览
《图解数据结构》.pdf
面试官:你晓得对象的克隆原理吗?
《亿级流量网站架构核心技术》.pdf