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 ! 結束了, 希望對你有所幫助, 如有不當或者需要改進的地方,請留言。
謝謝!