Mybatis 的 Pagehelper 插件相信大家都使用过(没用过的请飘过~~~~),并且用起来确实很方便。但是每次都的 PageHelper.startPage(PageNum, PageSize)
,对于我这种比较懒的人来说,是万万忍受不了的,怎么办?那就的想一劳永逸的方法了。
废话就不多说了,下面直接上code
正常流程,伪代码
//controller @PostMapping("/") public Object findUserList(int otherParams, int pageNum, int pageSize) { // 参数处理 // 业务处理 Object users = service.findUserByPage(pageNum, pageSize); // 业务处理 return Object; } // service public Object findUserByPage(int pageNum, int pageSize) { PageHelper.startPage(PageNum, PageSize); // 业务处理 // 结果处理 return Object; }
正常我们使用MyBatis PageHelper 分页插件基本就是上面写法(只是伪代码,有错误请多多包涵)
改造后,流程
//controller @ControllerPagehelper @PostMapping("/") public Object findUserList(int otherParams, int pageNum, int pageSize) { // 参数处理 // 业务处理 Object users = service.findUserByPage(); // 业务处理 return Object; } // service @ServicePagehelper public Object findUserByPage() { // 业务处理 // 结果处理 return Object; }
变化大家一眼就可看到,我就不啰嗦了, 下面直接撸代码。
@Target({ElementType.METHOD}) // 作用在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行期间 @Documented public @interface ControllerPagehelper { }
@Target({ElementType.METHOD}) // 作用在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行期间 @Documented public @interface ServicePagehelper { }
import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.ibatis.javassist.ClassClassPath; import org.apache.ibatis.javassist.ClassPool; import org.apache.ibatis.javassist.CtClass; import org.apache.ibatis.javassist.CtMethod; import org.apache.ibatis.javassist.Modifier; import org.apache.ibatis.javassist.bytecode.CodeAttribute; import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute; import org.apache.ibatis.javassist.bytecode.MethodInfo; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import com.github.pagehelper.PageHelper; import com.ochain.mall.product.dto.input.PageInputDTO; import lombok.extern.slf4j.Slf4j; /** * 分页拦截 * * @author yueli * @date Feb 25, 2019 11:56:54 AM */ @Aspect @Component @Slf4j @Order(3) public class PagehelperAspect { // ThreadLocal 存放 分也参数 private static final ThreadLocal<PageInputDTO> PAGE_INPUTDTO_CONTEXT = new ThreadLocal<>(); // 参数类型 private static String[] types = { "java.lang.Integer", "java.lang.Double", "java.lang.Float", "java.lang.Long", "java.lang.Short", "java.lang.Byte", "java.lang.Boolean", "java.lang.Char", "java.lang.String", "int", "double", "long", "short", "byte", "boolean", "char", "float" }; private static final String CURRENTPAGE = "pageNum"; private static final String PAGESIZE = "pageSize"; @Pointcut("@annotation(com.annotations.ServicePagehelper)") public void pageServiceAspect() { } @Pointcut("@annotation(com.annotations.ControllerPagehelper)") public void pageControllerAspect() { } @Before("pageControllerAspect()") public void controllerAop(JoinPoint joinPoint) throws Exception { log.info("ControllerAop ->>> 开会分页拦截"); // PageInputDTO 分装分也信息 PageInputDTO pageInputDTO = null; Object[] args = joinPoint.getArgs(); // 获取类名 String clazzName = joinPoint.getTarget().getClass().getName(); // 获取方法名称 String methodName = joinPoint.getSignature().getName(); // 通过反射获取参数列表 Map<String, Object> nameAndArgs = this.getFieldsName(this.getClass(), clazzName, methodName, args); Object object = nameAndArgs.get("pageInputDTO"); if (null != object) { pageInputDTO = (PageInputDTO) object; } else { pageInputDTO = new PageInputDTO(); pageInputDTO.setPageNum( (Integer) nameAndArgs.get(CURRENTPAGE) == null ? 0 : (int) nameAndArgs.get(CURRENTPAGE)); pageInputDTO.setPageSize( ((Integer) nameAndArgs.get(PAGESIZE) == null || (Integer) nameAndArgs.get(PAGESIZE) <= 0) ? 10 : (Integer) nameAndArgs.get(PAGESIZE)); } // 将分页参数放置线程变量中 PAGE_INPUTDTO_CONTEXT.set(pageInputDTO); } @Before("pageServiceAspect()") public void serviceImplAop() throws Throwable { log.info("Service正在执行PageHelperAop"); PageInputDTO pageBean = PAGE_INPUTDTO_CONTEXT.get(); PageHelper.startPage(pageBean.getPageNum(), pageBean.getPageSize()); // ** 使用完成ThreadLocal后必须调用remove方法,防止内存溢出 PAGE_INPUTDTO_CONTEXT.remove(); } private Map<String, Object> getFieldsName(@SuppressWarnings("rawtypes") Class cls, String clazzName, String methodName, Object[] args) throws Exception { Map<String, Object> map = new HashMap<String, Object>(8); ClassPool pool = ClassPool.getDefault(); ClassClassPath classPath = new ClassClassPath(cls); pool.insertClassPath(classPath); CtClass cc = pool.get(clazzName); CtMethod cm = cc.getDeclaredMethod(methodName); MethodInfo methodInfo = cm.getMethodInfo(); CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); if (attr == null) { return new HashMap<>(1); } int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1; for (int i = 0; i < cm.getParameterTypes().length; i++) { Object arg = args[i]; log.info("advice -- >> arg{}, arg#class:{}", arg, arg==null ? null : arg.getClass()); // 如果controller中使用PageInputDTO 来接收分页参数的,直接将其保存起来 if (arg instanceof PageInputDTO) { map.put("pageInputDTO", arg); break; } if (arg == null || arg.getClass() == null) { continue; } if (CURRENTPAGE.equals(attr.variableName(i + pos)) || PAGESIZE.equals(attr.variableName(i + pos))) { map.put(attr.variableName(i + pos), arg); continue; } // 因我们只取 pageNum 和 pageSize 取到多就直接返回,不用在遍历。 if (map.size() >= 2) { break; } if (!Arrays.asList(types).contains(arg.getClass().getTypeName())) { Class<?> superclass = arg.getClass().getSuperclass(); if (superclass != null) { Object newInstance = superclass.newInstance(); if (newInstance instanceof PageInputDTO) { map.put("pageInputDTO", arg); break; } } } } return map; } public static void getFieldsValue(Object obj, Map<String, Object> map) throws IllegalArgumentException, IllegalAccessException { Field[] fields = obj.getClass().getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); String name = f.getName(); if (CURRENTPAGE.equals(name) || PAGESIZE.equals(name)) { map.put(f.getName(), f.get(obj)); } } } }
/** * 分页请求参数 * * @author yueli * @date Feb 22, 2019 1:03:52 PM */ @Data public class PageInputDTO { @ApiModelProperty(value = "当前页默认值:0", dataType = "Integer") private Integer pageNum; @ApiModelProperty(value = "每页显示条数默认值:10", dataType = "Integer") private Integer pageSize; }
到这就搞定了, 这样以来我们在使用分页时是不是简单了很多。
改进:
1: 如果觉得注解使用起来还是不爽,可以将切面,改成拦截固定结尾的方法如 * *bypage等
2: pageControllerAspect 的切面可以改进为 Around 将结果也进行处理, 不过这个处理起来比较麻烦,个人感觉有点得不偿失, 如想处理,自己实现下也不难。
OK ! 结束了, 希望对你有所帮助, 如有不当或者需要改进的地方,请留言。
谢谢!