# 问题描述
---
3.2.0 dtp安装后前端页面无法登录,提示查看panwei_dtp.log,其中日志报 NullPointerException



# 问题诊断
---
## 信息收集
---
系统名称:Panwei_dtp 单机部署
数据库/工具版本:3.1.1B01
问题模块:安装
服务器环境信息:Intel X86_64 BCLinux / 7.8 英文
## 问题处理过程
---
2025-07-21 思考过程
分析日志信息:
### 日志内容逐行拆解
#### 1. 整体报错头信息
```log
[09:49:38:433] [https-jsse-nio-31030-exec-10] [ERROR] - org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:175) - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
```
- **翻译**:
在 `[09:49:38:433]` 时间点,`https-jsse-nio-31030-exec-10` 线程中,`org.apache.juli.logging.DirectJDKLog` 类的 `log` 方法(位于 `DirectJDKLog.java` 第 175 行)记录到:
路径为 `[]` 的 Web 应用上下文里,`dispatcherServlet` 这个 Servlet 在处理请求时抛出异常,异常原因是**请求处理失败**,嵌套异常为 `java.lang.NullPointerException`(空指针异常 ),并给出了 “根本原因”(root cause)。
- **解释**:
这是 Spring MVC 框架(通过 `dispatcherServlet` 处理请求)抛出的顶层错误,告知 “请求处理时出错,根源是空指针”,后续堆栈会展示空指针具体发生在哪里。
#### 2. 空指针异常起点
```log
java.lang.NullPointerException: null
at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) ~[?:1.8.0_432]
```
- **翻译**:
`java.lang.NullPointerException`(空指针异常):空值导致
发生在 `sun.awt.FontConfiguration` 类的 `getVersion` 方法中,代码位于 `FontConfiguration.java` 第 1264 行,依赖的 JDK 版本是 `1.8.0_432`(`~[?:1.8.0_432]` 表示类加载器及 JDK 版本 )。
- **解释**:
空指针首次被捕获的位置,说明在 `FontConfiguration` 类的 `getVersion` 方法执行时,某个对象未初始化(为 `null` )就被调用了。`sun.awt.FontConfiguration` 是 JDK 内部负责**字体配置加载、管理**的类,这里出问题通常和 “系统字体环境、JDK 字体配置解析” 相关。
#### 3. 字体配置初始化链路
```log
at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) ~[?:1.8.0_432]
at sun.awt.FontConfiguration.init(FontConfiguration.java:107) ~[?:1.8.0_432]
at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) ~[?:1.8.0_432]
at sun.font.SunFontManager$2.run(SunFontManager.java:441) ~[?:1.8.0_432]
```
- **逐行翻译**:
- 在 `sun.awt.FontConfiguration` 类的 `readFontConfigFile` 方法(`FontConfiguration.java` 第 219 行 )
- 在 `sun.awt.FontConfiguration` 类的 `init` 方法(`FontConfiguration.java` 第 107 行 )
- 在 `sun.awt.X11FontManager` 类的 `createFontConfiguration` 方法(`X11FontManager.java` 第 774 行 )
- 在 `sun.font.SunFontManager` 类的内部匿名类 `$2` 的 `run` 方法(`SunFontManager.java` 第 441 行 )
- **逐行解释**:
这是 JDK 字体管理初始化的**调用链路**:
1. `readFontConfigFile`:尝试读取系统字体配置文件(如 Linux 下的 `/etc/fonts/fonts.conf` 等 )。
2. `init`:初始化字体配置,解析配置文件内容,构建字体管理所需的数据结构。
3. `createFontConfiguration`:`X11FontManager`(针对 Linux X11 图形环境的字体管理器 )创建字体配置实例,准备为 GUI 应用提供字体支持。
4. `SunFontManager$2.run`:`SunFontManager`(JDK 字体管理核心类 )启动一个任务,执行字体配置初始化流程。
这些步骤说明:空指针发生在**JDK 初始化字体支持环境**的过程中,和系统字体配置、JDK 对字体环境的兼容性强相关。
#### 4. 权限控制与类初始化
```log
at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_432]
at sun.font.SunFontManager.<init>(SunFontManager.java:386) ~[?:1.8.0_432]
at sun.awt.FcFontManager.<init>(FcFontManager.java:35) ~[?:1.8.0_432]
at sun.awt.X11FontManager.<init>(X11FontManager.java:57) ~[?:1.8.0_432]
```
- **逐行翻译**:
- 在 `java.security.AccessController` 类的 `doPrivileged` 本地方法(Java 原生方法,无具体代码行 )
- 在 `sun.font.SunFontManager` 类的构造方法(`SunFontManager.java` 第 386 行 )
- 在 `sun.awt.FcFontManager` 类的构造方法(`FcFontManager.java` 第 35 行 )
- 在 `sun.awt.X11FontManager` 类的构造方法(`X11FontManager.java` 第 57 行 )
- **逐行解释**:
这部分是 JDK 内部**字体管理器的构造和权限控制**:
1. `AccessController.doPrivileged`:JDK 执行需要权限的操作(如读取系统字体配置文件 ),会通过该方法申请权限,避免因权限不足报错。
2. `SunFontManager.<init>`:创建 `SunFontManager` 实例,它是 JDK 管理字体的核心类,会整合系统字体、配置等信息。
3. `FcFontManager.<init>`、`X11FontManager.<init>`:针对不同系统(这里是 Linux 基于 FontConfig 和 X11 的环境 )初始化具体的字体管理器,适配系统图形环境。
这说明:字体管理器初始化过程中,因前面步骤抛出空指针,导致后续构造流程受影响。
#### 5. 反射与工厂模式创建实例
```log
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_432]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_432]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_432]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_432]
at java.lang.Class.newInstance(Class.java:442) ~[?:1.8.0_432]
at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83) ~[?:1.8.0_432]
at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_432]
```
- **逐行翻译**:
- 在 `sun.reflect.NativeConstructorAccessorImpl` 类的 `newInstance0` 本地方法(无具体代码行 )
- 在 `sun.reflect.NativeConstructorAccessorImpl` 类的 `newInstance` 方法(`NativeConstructorAccessorImpl.java` 第 62 行 )
- 在 `sun.reflect.DelegatingConstructorAccessorImpl` 类的 `newInstance` 方法(`DelegatingConstructorAccessorImpl.java` 第 45 行 )
- 在 `java.lang.reflect.Constructor` 类的 `newInstance` 方法(`Constructor.java` 第 423 行 )
- 在 `java.lang.Class` 类的 `newInstance` 方法(`Class.java` 第 442 行 )
- 在 `sun.font.FontManagerFactory` 类的内部匿名类 `$1` 的 `run` 方法(`FontManagerFactory.java` 第 83 行 )
- 在 `java.security.AccessController` 类的 `doPrivileged` 本地方法(无具体代码行 )
- **逐行解释**:
这是 JDK 通过**反射和工厂模式**创建字体管理器实例的流程:
1. `NativeConstructorAccessorImpl` 等:JDK 反射机制的实现类,用于通过字节码创建对象实例(这里是创建字体管理器相关类的实例 )。
2. `Constructor.newInstance`、`Class.newInstance`:反射调用构造方法,动态创建类的实例,让 JDK 能根据系统环境(如 Linux、Windows )选择对应的字体管理器。
3. `FontManagerFactory$1.run`:字体管理器工厂类启动任务,根据系统环境决定创建哪种字体管理器(如 Linux 下的 `X11FontManager` )。
这部分逻辑复杂,但核心是:**因前面字体配置初始化时抛出空指针,导致反射创建实例失败**,最终引发整个字体管理初始化流程异常。
### 整体问题总结
从日志链路看,空指针发生在 **JDK 初始化字体支持环境** 的核心流程中,具体是 `sun.awt.FontConfiguration` 类处理字体配置时,某个对象为 `null` 导致。
#### 可能的根因方向:
1. **系统字体环境问题**:
- 系统缺少必要字体,或字体配置文件(如 Linux 下的 `fonts.conf` )格式错误、路径异常,导致 `FontConfiguration` 解析时出错。
- 系统图形环境(如 X11 )异常,影响 AWT 字体管理初始化。
2. **JDK 兼容性 / 缺陷**:
- 当前使用的 JDK 版本(`1.8.0_432` )可能存在已知的字体处理 Bug,尤其在特定系统环境(如 Linux 最小化安装、无图形界面 )下容易触发。
3. **应用程序触发场景**:
- 应用程序(DTP )可能在启动时或请求处理中,触发了 AWT 字体初始化流程(比如某些报表、图形渲染功能隐式依赖 AWT ),而系统环境未正确支持。
#### 下一步排查建议:
1. **检查系统字体环境**:
- 在 Linux 上,运行 `fc-list` 查看已安装字体;检查 `/etc/fonts/fonts.conf` 等配置文件是否存在、格式是否正常。
- 确保系统有基本字体(如 `DejaVu Sans` ),若缺少,安装字体包(如 `yum install fontconfig dejavu-sans-fonts` )。
2. **验证 JDK 字体处理**:
- 编写简单 AWT 测试类(如弹出窗口、绘制文字 ),验证 JDK 能否正常初始化字体环境:
java
运行
```java
import java.awt.Font;
import java.awt.GraphicsEnvironment;
public class FontTest {
public static void main(String[] args) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Font[] fonts = ge.getAllFonts();
System.out.println("已安装字体数量:" + fonts.length);
for (Font f : fonts) {
System.out.println(f.getName());
}
}
}
```
编译运行(`javac FontTest.java && java FontTest` ),若报错或字体数量异常,说明 JDK 字体环境有问题。
3. **调整应用程序依赖**:
- 若 DTP 并非必须使用 AWT 字体功能,可尝试通过 JVM 参数禁用 AWT 初始化(如 `-Djava.awt.headless=true` ),避免触发字体相关错误。
通过以上分析和步骤,能逐步定位 “系统字体环境、JDK 兼容性” 等根因,解决因字体配置初始化失败导致的空指针问题。
2025-07-21 思考过程
本地测试了zookeeper,Kafka,dtp的java版本是否一致,java运行环境(执行java检查代码)的字体数目,都未发现明显问题,沟通索要上下文信息如下:
```
[16:04:51:721] [Timer-1] [INFO] - cn.com.dts.full.schedule.IncrementalServiceHeartbeat.heartbeat(IncrementalServiceHeartbeat.java:61) - 心跳检测启动...
[16:04:57:047] [pool-3-thread-1] [INFO] - cn.com.dts.full.module.SystemConfig.refreshDataIsolationLevel(SystemConfig.java:486) - 获取数据隔离级别最新配置:none
[16:09:51:721] [Timer-1] [INFO] - cn.com.dts.full.schedule.IncrementalServiceHeartbeat.heartbeat(IncrementalServiceHeartbeat.java:61) - 心跳检测启动...
[16:09:57:049] [pool-3-thread-1] [INFO] - cn.com.dts.full.module.SystemConfig.refreshDataIsolationLevel(SystemConfig.java:486) - 获取数据隔离级别最新配置:none
[16:14:51:722] [Timer-1] [INFO] - cn.com.dts.full.schedule.IncrementalServiceHeartbeat.heartbeat(IncrementalServiceHeartbeat.java:61) - 心跳检测启动...
[16:14:57:051] [pool-3-thread-1] [INFO] - cn.com.dts.full.module.SystemConfig.refreshDataIsolationLevel(SystemConfig.java:486) - 获取数据隔离级别最新配置:none
[16:16:26:917] [https-jsse-nio-31030-exec-4] [ERROR] - org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:175) - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) ~[?:1.8.0_432]
at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) ~[?:1.8.0_432]
at sun.awt.FontConfiguration.init(FontConfiguration.java:107) ~[?:1.8.0_432]
at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) ~[?:1.8.0_432]
at sun.font.SunFontManager$2.run(SunFontManager.java:441) ~[?:1.8.0_432]
at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_432]
at sun.font.SunFontManager.<init>(SunFontManager.java:386) ~[?:1.8.0_432]
at sun.awt.FcFontManager.<init>(FcFontManager.java:35) ~[?:1.8.0_432]
at sun.awt.X11FontManager.<init>(X11FontManager.java:57) ~[?:1.8.0_432]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:1.8.0_432]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[?:1.8.0_432]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:1.8.0_432]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_432]
at java.lang.Class.newInstance(Class.java:442) ~[?:1.8.0_432]
at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83) ~[?:1.8.0_432]
at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_432]
at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74) ~[?:1.8.0_432]
at java.awt.Font.getFont2D(Font.java:491) ~[?:1.8.0_432]
at java.awt.Font.access$000(Font.java:224) ~[?:1.8.0_432]
at java.awt.Font$FontAccessImpl.getFont2D(Font.java:228) ~[?:1.8.0_432]
at sun.font.FontUtilities.getFont2D(FontUtilities.java:200) ~[?:1.8.0_432]
at sun.font.StandardGlyphVector.initFontData(StandardGlyphVector.java:1126) ~[?:1.8.0_432]
at sun.font.StandardGlyphVector.init(StandardGlyphVector.java:1115) ~[?:1.8.0_432]
at sun.font.StandardGlyphVector.<init>(StandardGlyphVector.java:167) ~[?:1.8.0_432]
at java.awt.Font.createGlyphVector(Font.java:2549) ~[?:1.8.0_432]
at com.google.code.kaptcha.text.impl.DefaultWordRenderer.renderWord(DefaultWordRenderer.java:67) ~[kaptcha-2.3.3.jar:?]
at com.google.code.kaptcha.impl.DefaultKaptcha.createImage(DefaultKaptcha.java:43) ~[kaptcha-2.3.3.jar:?]
at cn.com.dts.full.service.impl.CaptchaServiceImpl.generateCaptcha(CaptchaServiceImpl.java:37) ~[classes!/:cmcc-3.2.0_B01]
at cn.com.dts.full.controller.u.af(UserController.java:518) ~[classes!/:cmcc-3.2.0_B01]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_432]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_432]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_432]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_432]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:903) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:809) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:903) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:809) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.39.jar:5.3.39]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.39.jar:5.3.39]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.83.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.39.jar:5.3.39]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.83.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at cn.com.dts.full.filter.JWTFilter.doFilter(JWTFilter.java:68) ~[classes!/:cmcc-3.2.0_B01]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
473,1-8 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) ~[spring-security-web-5.7.12.jar:5.7.12]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-5.3.39.jar:5.3.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.39.jar:5.3.39]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.39.jar:5.3.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:928) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1794) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.83.jar:9.0.83]
at java.lang.Thread.run(Thread.java:750) ~[?:1.8.0_432]
```
2025-07-21 思考过程
日志核心信息提炼:
#### 1. 异常触发链路(关键路径)
```log
# 从验证码生成开始,逐步触发字体初始化
at com.google.code.kaptcha.text.impl.DefaultWordRenderer.renderWord(DefaultWordRenderer.java:67) ~[kaptcha-2.3.3.jar:?]
at com.google.code.kaptcha.impl.DefaultKaptcha.createImage(DefaultKaptcha.java:43) ~[kaptcha-2.3.3.jar:?]
at cn.com.dts.full.service.impl.CaptchaServiceImpl.generateCaptcha(CaptchaServiceImpl.java:37) ~[classes!/:cmcc-3.2.0_B01]
at cn.com.dts.full.controller.u.af(UserController.java:518) ~[classes!/:cmcc-3.2.0_B01]
```
- **翻译**:
验证码生成逻辑(Kaptcha 库)在渲染文字时,触发了 Java AWT 字体初始化,最终因字体配置异常抛出空指针。
- **解释**:
DTP 系统的**登录验证码功能**依赖 Kaptcha 库生成图形验证码,而 Kaptcha 底层使用了 AWT 的字体渲染能力。当 AWT 初始化字体配置失败时(如系统字体环境异常 ),会导致验证码生成失败,进而触发空指针异常,最终导致登录请求报错。
#### 2. 字体初始化异常(根本原因)
```log
java.lang.NullPointerException: null
at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) ~[?:1.8.0_432]
...(中间字体初始化链路)...
at java.awt.Font.createGlyphVector(Font.java:2549) ~[?:1.8.0_432]
at com.google.code.kaptcha.text.impl.DefaultWordRenderer.renderWord(DefaultWordRenderer.java:67) ~[kaptcha-2.3.3.jar:?]
```
- **翻译**:
AWT 字体配置类 `FontConfiguration` 在获取版本时,因某个对象为 `null` 抛出空指针,最终导致验证码渲染失败。
- **解释**:
Kaptcha 生成验证码时,需要调用 AWT 的 `Font.createGlyphVector` 方法渲染文字。而 AWT 渲染文字依赖 `FontConfiguration` 初始化系统字体环境,若字体环境异常(如系统缺少字体、JDK 解析字体配置失败 ),则会抛出空指针,阻断验证码生成流程。
#### 3. 其他日志辅助信息
```log
[16:04:51:721] [Timer-1] [INFO] - ...IncrementalServiceHeartbeat.heartbeat... - 心跳检测启动...
[16:04:57:047] [pool-3-thread-1] [INFO] - ...SystemConfig.refreshDataIsolationLevel... - 获取数据隔离级别最新配置:none
```
- **翻译**:
系统心跳检测、数据隔离级别配置刷新正常,说明除验证码功能外,其他核心服务(如定时任务、配置加载 )未受影响。
- **解释**:
空指针异常**仅触发于登录验证码生成流程**,属于局部功能异常,不影响系统核心服务。
根因定位与解决方案:
#### 1. 根因总结
**“系统字体环境异常” 导致 AWT 字体初始化失败**,进而使依赖字体渲染的验证码功能(Kaptcha )抛出空指针,最终阻断登录请求。
可能的具体场景:
- **系统缺少基础字体**:Linux 最小化安装或未配置字体包,导致 AWT 无法找到可用字体。
- **JDK 字体配置解析失败**:字体配置文件(如 `/etc/fonts/fonts.conf` )格式错误,或 JDK 版本与系统字体环境不兼容。
- **无头模式未启用**:应用运行在无图形界面环境(如服务器),但未启用 AWT 无头模式(`-Djava.awt.headless=true` ),导致字体初始化依赖图形环境。
#### 2. 分步解决方案
##### (1)检查并修复系统字体环境
```bash
# 1. 查看系统已安装字体(Linux)
fc-list
# 2. 若字体数量极少或无,安装基础字体包(以 CentOS/RHEL 为例)
yum install -y fontconfig dejavu-sans-fonts
# 3. 验证字体安装(应输出大量字体信息)
fc-list
```
- **效果**:确保系统有可用字体(如 `DejaVu Sans` ),为 AWT 提供渲染基础。
##### (2)启用 AWT 无头模式(推荐)
修改 DTP 启动脚本,添加 JVM 参数 `-Djava.awt.headless=true`,让 AWT 在无图形界面环境下正常工作:
```bash
# 找到 DTP 启动脚本(如 startup.sh),在 Java 启动命令中添加参数
java -Djava.awt.headless=true -jar dtp.jar
```
- **原理**:
无头模式(Headless Mode)让 AWT 无需依赖图形界面即可初始化字体、渲染文字,避免因系统无显示器、无图形驱动导致的异常。
##### (3)验证验证码功能(确认修复)
重启 DTP 后,手动触发验证码生成(如访问登录页),检查日志或页面:
- 若页面正常显示验证码,且日志无空指针报错,说明修复成功。
- 若仍报错,检查 Kaptcha 配置(如 `kaptcha.textproducer.font.names` ),确保配置的字体系统已安装。
##### (4)兜底方案:替换验证码实现(可选)
若 Kaptcha 与系统环境兼容性差,可替换为纯字符验证码(无需 AWT 渲染),或使用其他验证码库(如 `SimpleCaptcha` )。
示例:修改 DTP 验证码生成逻辑,跳过 AWT 依赖:
```java
// 伪代码:纯字符验证码(无图形渲染,仅作示例)
public String generateCaptcha() {
return RandomStringUtils.randomAlphanumeric(6); // 使用 Apache Commons Lang
}
```
上下文提到了google的图形验证码问题,佐证了字体问题,根据特殊的,差异化的报错信息,上网查阅到类似解决方案
2025-07-21 杨小满
方案依据:
根据网上相似问题解决方案,以及根因定位在验证码程序找不到可以使用的字体上,考虑下载fontconfig dejavu-sans-fonts dejavu-serif-fonts尝试能否覆盖,若不能,考虑查看dtp验证逻辑的代码,发现具体指定的字体
## 诊断结论
---
初步判断是字体方向的空指针问题,根据上word文档中的详细上下文和异常部分,发现了
# 从验证码生成开始,逐步触发字体初始化
at com.google.code.kaptcha.text.impl.DefaultWordRenderer.renderWord(DefaultWordRenderer.java:67) ~[kaptcha-2.3.3.jar:?] at com.google.code.kaptcha.impl.DefaultKaptcha.createImage(DefaultKaptcha.java:43) ~[kaptcha-2.3.3.jar:?] at cn.com.dts.full.service.impl.CaptchaServiceImpl.generateCaptcha(CaptchaServiceImpl.java:37) ~[classes!/:cmcc-3.2.0_B01] at cn.com.dts.full.controller.u.af(UserController.java:518) ~[classes!/:cmcc-3.2.0_B01]
- 验证码生成逻辑(Kaptcha 库)在渲染文字时,触发了 Java AWT 字体初始化,最终因字体配置异常抛出空指针。
- 解释:
DTP 系统的登录验证码功能依赖 Kaptcha 库生成图形验证码,而 Kaptcha 底层使用了 AWT 的字体渲染能力。当 AWT 初始化字体配置失败时(如系统字体环境异常 ),会导致验证码生成失败,进而触发空指针异常,最终导致登录请求报错。
发现特性问题后查阅报错信息,定位问题是图形验证码找不到适合的字体,而且程序没有判空逻辑导致
# 建议
---
## 临时修复方案
---
安装字体包:
sudo yum install -y fontconfig dejavu-sans-fonts dejavu-serif-fonts
刷新字体缓存:
sudo fc-cache -fv
验证字体安装情况:
fc-list
重启dtp试试
## 后续处理
---
无




