pagehelper 分頁插件java.lang.NullPointerException

在將 springboot 轉到 springcloud 時,出了許多難以言喻的坑,現貼一個.

使用 springboot 1.5.4.RELEASE, pagehelper-spring-boot-starter 1.0.0

出現異常

### SQL: SELECT TMP_PAGE.*, ROWNUM ROW_ID FROM ( SELECT count(0) FROM TB_LEND_USER t ) TMP_PAGE WHERE ROWNUM <= 10### Cause: java.lang.NullPointerExceptionat org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)... 36 moreCaused by: java.lang.NullPointerExceptionat com.github.pagehelper.dialect.AbstractHelperDialect.afterCount(AbstractHelperDialect.java:71)at com.github.pagehelper.PageHelper.afterCount(PageHelper.java:73)at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:115)at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)at com.sun.proxy.$Proxy163.query(Unknown Source)at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)... 42 more

debug

debug 一個 demo 分頁查詢,發現進入 PageInterceptor.intercept 方法兩次, 第一次進入的時候執行了clearPage方法,導致第二次進入NullPointerException.

1.0.0源碼:

@Configuration@ConditionalOnBean(SqlSessionFactory.class)@ConfigurationProperties@EnableConfigurationProperties@AutoConfigureAfter(MybatisAutoConfiguration.class)public class PageHelperAutoConfiguration { private final SqlSessionFactory sqlSessionFactory; private Map pagehelper = new LinkedHashMap(); public PageHelperAutoConfiguration(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @PostConstruct public void addPageInterceptor() { PageInterceptor interceptor = new PageInterceptor(); Properties properties = new Properties(); properties.putAll(pagehelper); interceptor.setProperties(properties); //PostConstruct 註解導致兩個 interceptor 加入 mybatis 攔截器鏈. sqlSessionFactory.getConfiguration().addInterceptor(interceptor); } public Map getPagehelper() { return pagehelper; } public void setPagehelper(Map pagehelper) { this.pagehelper = pagehelper; }}

猜測,是不是 Configuration 和 ConfigurationProperties 註解合在一起會導致 PostConstruct 方法執行兩次.

創建了一個 DemoConfig 用來測試

@Configuration@ConfigurationProperties@Slf4j@Datapublic class DemoConfig { public DemoConfig() { log.info("--構造"); } @PostConstruct public void init(){ log.info("輸出 init"); }}

debug 和 log 結果顯示,PostConstruct 方法確實調用了兩次.

將 ConfigurationProperties 註釋, PostConstruct 執行一次.

將 Configuration 註釋, DemoConfig 不會創建.

將 Configuration 註釋,加入 Component 註解,或者其他 Configuration類中使用 @EnableConfigurationProperties(DemoConfig.class), PostConstruct 還是執行了兩次.

確定 pagehelper 的 NullPointerException 是由於 ConfigurationProperties 註解導致 PostConstruct 方法執行兩次造成.

但具體為什麼 ConfigurationProperties 註解會導致 PostConstruct 執行兩次還有待進一步閱讀 springboot 源碼.(ps: 有閱讀到關鍵代碼的朋友可以在下面講解下,貼下關鍵類和方法)

解決

前往 pagehelper-spring-boot, 發現已經有人提了issues: https://github.com/pagehelper/pagehelper-spring-boot/issues/1#issuecomment-274104093

升級pagehelper-spring-boot-starter 1.0.0 到 1.1.1 後,解決問題.

貼一下 1.1.1 中 PageHelperAutoConfiguration 源碼

可以看到,是將 ConfigurationProperties 拆出來了

package com.github.pagehelper.autoconfigure;import com.github.pagehelper.PageInterceptor;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;import org.springframework.boot.bind.RelaxedPropertyResolver;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.context.EnvironmentAware;import org.springframework.context.annotation.Configuration;import org.springframework.core.env.Environment;import javax.annotation.PostConstruct;import java.util.List;import java.util.Map;import java.util.Properties;/*** 自定注入分頁插件** @author liuzh*/@Configuration@ConditionalOnBean(SqlSessionFactory.class)@EnableConfigurationProperties(PageHelperProperties.class)@AutoConfigureAfter(MybatisAutoConfiguration.class)public class PageHelperAutoConfiguration implements EnvironmentAware { @Autowired private List sqlSessionFactoryList; @Autowired private PageHelperProperties pageHelperProperties; private RelaxedPropertyResolver resolver; @Override public void setEnvironment(Environment environment) { resolver = new RelaxedPropertyResolver(environment, "pagehelper."); } @PostConstruct public void addPageInterceptor() { PageInterceptor interceptor = new PageInterceptor(); Properties properties = pageHelperProperties.getProperties(); Map subProperties = resolver.getSubProperties(""); for (String key : subProperties.keySet()) { if (!properties.containsKey(key)) { properties.setProperty(key, resolver.getProperty(key)); } } interceptor.setProperties(properties); for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) { sqlSessionFactory.getConfiguration().addInterceptor(interceptor); } }}


分享到:


相關文章: