1、技术栈:
JDK 17 + SpringBoot 3.1.0 + PageHelper 1.4.0
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project ...> 3 <parent> 4 <groupId>org.springframework.boot</groupId> 5 <artifactId>spring-boot-starter-parent</artifactId> 6 <version>3.1.0</version> 7 <relativePath/> 8 </parent> 9 ... 10 <properties> 11 <java.version>17</java.version> 12 <pagehelper.boot.version>1.4.0</pagehelper.boot.version> 13 ... 14 </properties> 15 16 <dependencyManagement> 17 <dependencies> 18 <!-- pagehelper 分页插件 --> 19 <dependency> 20 <groupId>com.github.pagehelper</groupId> 21 <artifactId>pagehelper-spring-boot-starter</artifactId> 22 <version>${pagehelper.boot.version}</version> 23 </dependency> 24 </dependencies> 25 ... 26 </dependencyManagement> 27 </project>View Code
2、异常表现:
PageHelper 分页拦截器不生效
3、解决方案:
将 pagehelper-spring-boot-starter 版本由 1.4.0 升级到 1.4.7 (写这篇博客时的最新版)
4、源码分析:
分析、解决这个bug需要对PageHelper源码、SpringBoot源码有一定的了解。
Step1: 首先分页插件PageHelper的本质是mybatis拦截器实现,可以大胆推测其核心类是一个Intercepter, 即 PageIntercepter.class 类
1 public class PageInterceptor implements org.apache.ibatis.plugin.Interceptor { 2 ... 3 }View Code
Step2: 在 PageIntercepter 核心方法 intercept() 上打断点,debug发现代码运行时没有经过此处断点,推测可能是该 PageIntercepter 没有被创建,或者创建出的对象没有被绑定到 SqlSessionFactory 上
1 @Override 2 public Object intercept(Invocation invocation) throws Throwable {}View Code
Step3: 使用Idea分析 PageIntercepter 的构造方法,发现 PageIntercepter 的构造方法在 PageHelperAutoConfiguration 类的 afterPropertiesSet() 方法中被调用,在 PageHelperAutoConfiguration.afterPropertiesSet() 方法上打断点发现运行时逻辑也没有执行到当前断点
1 @Configuration 2 @ConditionalOnBean(SqlSessionFactory.class) 3 @EnableConfigurationProperties({PageHelperProperties.class, PageHelperStandardProperties.class}) 4 @AutoConfigureAfter(MybatisAutoConfiguration.class) 5 //@Import(PageHelperProperties.class) 6 @Lazy(false) 7 public class PageHelperAutoConfiguration implements InitializingBean { 8 // ... 9 @Override 10 public void afterPropertiesSet() throws Exception { 11 PageInterceptor interceptor = new PageInterceptor(); 12 interceptor.setProperties(this.properties); 13 for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) { 14 org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration(); 15 if (!containsInterceptor(configuration, interceptor)) { 16 configuration.addInterceptor(interceptor); 17 } 18 } 19 } 20 }View Code
Step4: 继续分析 PageHelperAutoConfiguration 源码,发现其被 @Configuration 注解标识,且在 com.github.pagehelper.autoconfigure 包下,由此判断 PageHelperAutoConfiguration 是一个被SpringBoot自动装配的类,且 PageHelperAutoConfiguration 被 @ConditionalOnBean(SqlSessionFactory.class) 注解标识
Step5: 在 SpringBootApplication 启动类的main方法结尾处打断点, 发现SpringContext中实际是存在SqlSessionFactory Bean的,由此推断问题出现在SpringBoot自动装配阶段,在AutoConfigurationImportSelector.getCandidateConfigurations() 方法内打断点,确认PageHelperAutoConfiguration不在待加载的configurations内
Step6: 回到SpringBoot源码,@SpringBootApplication -> @EnableAutoConfiguration -> @Import(AutoConfigurationImportSelector.class)
-> AutoConfigurationImportSelector.getCandidateConfigurations() ->ImportCandidates.load() 方法, 发现 ImportCandidates.LOCATION = "META-INF/spring/%s.imports" (SpringBootStarter 最新版)
Step7: 查看 com.github.pagehelper.autoconfigure-1.4.0.jar下 /META-INF 文件夹 发现 EnableAutoConfiguration配置信息放在 spring.factories 文件内(SpringBootStarter 旧版本支持),与Step6中的ImportCandidates.LOCATION不匹配,导致 PageHelperAutoConfiguration类未被装载
1 public final class ImportCandidates implements Iterable<String> { 2 3 private static final String LOCATION = "META-INF/spring/%s.imports"; 4 5 public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) { 6 Assert.notNull(annotation, "'annotation' must not be null"); 7 ClassLoader classLoaderToUse = decideClassloader(classLoader); 8 String location = String.format(LOCATION, annotation.getName()); 9 10 Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location); 11 List<String> importCandidates = new ArrayList<>(); 12 while (urls.hasMoreElements()) { 13 URL url = urls.nextElement(); 14 importCandidates.addAll(readCandidateConfigurations(url)); 15 } 16 return new ImportCandidates(importCandidates); 17 } 18 }View Code
Step8: 在 maven 中央仓库中查找pagehelper最新版本(1.4.7),升级pagehelper依赖版本到最新版,检查com.github.pagehelper.autoconfigure-1.4.0.jar下/META-INF 文件夹/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,与ImportCandidates.LOCATION匹配! 重新测试代码,发现PageHelperAutoConfiguration装载成功,分页功能正常。
5、心得感悟:
1、“内事不决问百度,外事不决问谷歌”, 出于赶项目工期等原因,一般在开发过程中遇到bug首先会去网上看有没有人遇到过同样的问题,有的话可以直接参考其原因及解决方案,没有的话再去打断点、分析源码
2、To Be Honest, 对于各个框架的源码,我的学习方式也是直接在B站上先跟着专门做相关培训的老师后面先看一遍,因为他们投入了大量的时间整理出了阅读源码的核心思路,然后自己再参照视频实际看一下相关的源码,留一个大致的映像,在需要用到源码知识解决问题的时候再去源码中debug,有一种“且放白鹿青崖间,须行即骑访名山”的意思~
标签:1.4,SpringBoot,pagehelper,源码,PageHelperAutoConfiguration,class From: https://www.cnblogs.com/sanbanfu-howie/p/17615172.html