1. 程式人生 > >動態代理使用分頁外掛PageHelper

動態代理使用分頁外掛PageHelper

本來只是研究一下Total的值,後面越陷越深!

  資料查詢到html發現沒有總數!!!一開始想著Total應該是查的資料總數,但是測試幾遍發現,返回的是當前分頁的總條數,這顯然不是我想要的,然後發現了PageInfo的繼承類PageSerializable下的初始化方法:

很顯然,如果我們傳例項不是屬於Page,那麼就會把list.size()複製給total;

Page page =   PageHelper.startPage(page,pageSize);
//資料查詢語句。。。 返回的List
//資料包裝類等等....
PageInfo pageInfo = new PageInfo<>(你自己的list);
//需要賦值下
pageInfo.setTotal(sqlpage.getTotal()); 

    這個分頁外掛前後執行的都是一樣的程式碼,很顯然這個分頁方法應該提取出來。然後我寫了一個A方法,查詢出List,呼叫A方法。呃呃,居然沒有分頁,而是查詢了所有資料。一開始對分頁理解不夠,查詢語句必須要再中間執行

    既然前後程式碼一樣的就可用動態代理來寫。

主要程式碼

JDK 代理方式:

public class PageProxy implements InvocationHandler {
    private Object object;

    public PageProxy(Object object) {
        this.object = object;
    }

    public Object getinfoimpl() {
        //初始值
        ClassLoader classLoader = this.object.getClass().getClassLoader();
        Class<?>[] cs = this.object.getClass().getInterfaces();
        return Proxy.newProxyInstance(this.object.getClass().getClassLoader(), cs, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //用來獲取總數,如果說資料不進行傳輸類轉換 可以直接返回 page
        Page sqlpage =   PageHelper.startPage((int)args[0],(int)args[1]);

        //執行方法返回的list
        List objlist= (List) method.invoke(object, args);

        PageInfo pageInfo = new PageInfo<>(objlist);
        //設定總頁
        pageInfo.setTotal(sqlpage.getTotal());
        return pageInfo;

    }
}

 AOP 註解方式: 

@Component
@Aspect
public class PagingAop {

    @Pointcut("execution(* com.lemon.sell.service.impl.*.select*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint pj) {
        //獲取引數 page sieze 分頁
        Object[] args = pj.getArgs();
        Page sqlpage = PageHelper.startPage((int) args[0], (int) args[1]);

        Object ret = null;
        try {
            //這裡應該返回 傳輸類的list集合
            ret = pj.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //強轉為List   實際返回值為PageInfo的例項物件
        PageInfo pageInfo = new PageInfo<>((List)ret);
        //設定總頁
        pageInfo.setTotal(sqlpage.getTotal());
        return pageInfo;
    }

}

其他動態代理方式,自行研究。


最終我選擇AOP註解方式的動態代理(程式碼少)。發現被代理的方法返回值必須是Object(需要修改返回值,返回List給代理類處理,返回Pageinfo給Controller層)。

那麼我Controller層返回是Object型別,需要強轉,每次在Controller層強轉一個Object型別很難看啊。

我想著直接在Service層進行強轉,例如:

//這是被代理的方法
public Object B(int page,int size){
    //查詢資料庫返回  list資料
   List list = .....;
   return list;
}

//這是Controller層呼叫的方法
public PageInfo C(int page,int size){
    //呼叫被代理的方法
    PageInfo pageInfo = (PageInfo)B(page,size);
    return pageInfo;
}

我興致勃勃的去測試,丫的!AOP根本沒有攔截。然後以為自己的execution路徑配置有問題(建議攔截某個介面方法)。檢查了幾遍,發現類的內部呼叫自個方法,根本不會被攔截。詳細檢視:AOP 不攔截內部方法原因 (另外幾種方法,自行了解)


直接通過上下文獲取bean來呼叫方法,而不直接從方法內部呼叫。

//啟動類
public class App {
    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(App.class, args);
        SpringContextUtil.setApplicationContext(applicationContext);
    }
}
public class SpringContextUtil {

    private static ApplicationContext applicationContext;

    //獲取上下文
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //設定上下文
    public static void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextUtil.applicationContext = applicationContext;
    }

    //通過名字獲取上下文中的bean
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    //通過型別獲取上下文中的bean
    public static Object getBean(Class<?> requiredType) {
        return applicationContext.getBean(requiredType);
    }

}

修改後這樣就可以正常攔截了

 //因為內部呼叫 其他方法 是不會被Aop攔截的  所以內部要通過獲取bean來呼叫
private XXXServiceImpl getService() {
    return (XXXServiceImpl) SpringContextUtil.getBean(this.getClass());
}

//這是被代理的方法
public Object B(int page,int size){
    //查詢資料庫返回  list資料
   List list = .....;
   return list;
}

//這是Controller層呼叫的方法
public PageInfo C(int page,int size){
    //呼叫被代理的方法
    PageInfo pageInfo = (PageInfo)getService.B(page,size);
    return pageInfo;
}

血案到此結束!