项目运行环境:Spring Boot + Mybatis/Mybatis-plus + PageHelper
项目前提:本项目引入了一个由其它Spring Boot项目打包成的JAR包(这个很关键)
问题描述
在IDEA中调试程序,分页正常;但通过WAR包部署时报错:“在系统中发现了多个分页插件,请检查系统配置”,具体报错信息如下:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
### Cause: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!
错误的解决方法
根据网上的方法尝试设置@SpringBootApplication(exclude = PageHelperAutoConfiguration.class)
,之后独立WAR包部署的项目可以正常运行,但是在IDEA中分页功能却失效了。
解决方法
上面的解决方法虽然没能最终解决问题,但是提供了一个思路,提示可能是自动配置的过程中出了问题
在上面的解决方法中设置@SpringBootApplication(exclude = PageHelperAutoConfiguration.class)
之后独立WAR包部署的项目可以正常运行,但是在IDEA中分页功能却失效了。说明确实是自动配置的问题,自动配置是需要的,但是如果配置两次的话就会报出“多个分页插件”的错误!
问题分析
-
不管是在IDEA中调试还是独立WAR包部署,测试环境和生产环境基本一致,唯一不同是本地测试用的是Spring Boot的内置Tomcat启动的,而生产环境是运行在独立Tomcat中的。
而用内置Tomcat启动类启动没有问题,但是WAR包部署到独立Tomcat就会出现这个报错,所以要看看有什么区别。 -
在将Spring Boot项目打包成WAR包时需要配置Application启动类继承SpringBootServletInitializer,并重写configure方法,而configure方法的作用就是要指定启动资源。
-
根据前面的分析可以猜测,是PageHelper相关的资源被指定了两次,所以启动的时候加载了两次这个资源,两次都自动配置了PageHelper插件,从而导致报错。所以可以根据这个思路去寻找项目中有哪些地方继承了SpringBootServletInitializer,并重写了configure方法。
-
很简单地,可以猜到可能是自己引入的那个由其它Spring Boot项目打包成的JAR包出的问题。
正常来说,将一个Spring Boot项目打包成JAR包时要删除其Application启动类,但是在某次打包时由于疏忽忘记了这个步骤,所以导致之后生成的WAR包中有两个继承了SpringBootServletInitializer的类,所以导致PageHelper插件被配置了两次。
- 修改将被打包成JAR包的Spring Boot项目,去掉多余的Application,即可解决问题。
说明
本人追溯到的原因只是一种情况,但是可以按照这个思路去找问题。
拓展部分
PageHelper的自动配置类源码分析:PageHelperAutoConfiguration
@Configuration
@ConditionalOnBean(SqlSessionFactory.class)
@EnableConfigurationProperties(PageHelperProperties.class)
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class PageHelperAutoConfiguration {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@Autowired
private PageHelperProperties properties;
/**
* 接受分页插件额外的属性
*
* @return
*/
@Bean
@ConfigurationProperties(prefix = PageHelperProperties.PAGEHELPER_PREFIX)
public Properties pageHelperProperties() {
return new Properties();
}
@PostConstruct
public void addPageInterceptor() {
PageInterceptor interceptor = new PageInterceptor();
Properties properties = new Properties();
//先把一般方式配置的属性放进去
properties.putAll(pageHelperProperties());
//在把特殊配置放进去,由于close-conn 利用上面方式时,属性名就是 close-conn 而不是 closeConn,所以需要额外的一步
properties.putAll(this.properties.getProperties());
interceptor.setProperties(properties);
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
}
}
}
- 从源码也可以看出两次自动配置会加入了两个拦截器PageInterceptor,所以才会提示“多个插件”;
- 加上
exclude=PageHelperAutoConfiguration.class
之后,相当于排除了PageHelperAutoConfiguration.class,即不让PageHelper自动配置,所以取消自动配置后又出现分页失效的问题。