在將 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 Mappagehelper = 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 ListsqlSessionFactoryList; @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); } }}
閱讀更多 JAVA小酷 的文章