1、前言
Mybatis 的 Pagehelper 插件相信大家都使用过(没用过的请飘过~~~~),并且用起来确实很方便。但是每次都的PageHelper.startPage(PageNum, PageSize)
,对于我这种比较懒的人来说,是万万忍受不了的,怎么办?那就的想一劳永逸的方法了。
废话就不多说了,下面直接上code
2、调用方式改变
正常流程,伪代码
//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;
}
变化大家一眼就可看到,我就不啰嗦了, 下面直接撸代码。
3、 实现
3.1 创建 ControllerPagehelper 注解
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行期间
@Documented
public @interface ControllerPagehelper {
}
3.2 创建 ServicePagehelper 注解
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行期间
@Documented
public @interface ServicePagehelper {
}
3.3 AOP实现拦截
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));
}
}
}
}
3.4 辅助类pageInputDTO
/**
* 分页请求参数
*
* @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;
}
4 总结
到这就搞定了, 这样以来我们在使用分页时是不是简单了很多。
改进:
1: 如果觉得注解使用起来还是不爽,可以将切面,改成拦截固定结尾的方法如**bypage等
2: pageControllerAspect 的切面可以改进为 Around 将结果也进行处理, 不过这个处理起来比较麻烦,个人感觉有点得不偿失, 如想处理,自己实现下也不难。
OK ! 结束了, 希望对你有所帮助, 如有不当或者需要改进的地方,请留言。
谢谢!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。