Quarkus Keycloak Admin Client闲置后触发ClassNotFoundException问题
解决Quarkus开发模式下Keycloak Admin Client闲置后
ClassNotFoundException问题 问题原因
- Quarkus开发模式启用了类加载优化,闲置15分钟以上时,未被持续引用的类(如
org.keycloak.representations.AccessTokenResponse)会被JVM卸载 - 手动构建Keycloak实例时,未绑定Quarkus的上下文类加载器,后续重新获取Token时无法加载已卸载的类
- TokenManager内部的RestClient代理在闲置后丢失类加载上下文,导致反序列化Token响应时失败
解决方案
方案1:使用Quarkus原生CDI注入管理Keycloak实例
利用quarkus-keycloak-admin-rest-client提供的自动注入能力,让Quarkus容器管理实例生命周期和类加载上下文,彻底避免手动管理的问题。
- 在
application.properties中配置Keycloak Admin Client参数:
quarkus.keycloak-admin-client.server-url=http://localhost:8111 quarkus.keycloak-admin-client.realm=quarkus quarkus.keycloak-admin-client.client-id=admin-rest-client quarkus.keycloak-admin-client.client-secret=你的客户端密钥 quarkus.keycloak-admin-client.username=你的管理员账号 quarkus.keycloak-admin-client.password=你的管理员密码 quarkus.keycloak-admin-client.grant-type=password
- 注入Keycloak实例并使用:
import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.representations.idm.RealmRepresentation; @ApplicationScoped public class KeycloakAdminService { @Inject Keycloak keycloak; public boolean verifyConnection(String realmName) { try { RealmResource realmResource = keycloak.realm(realmName); UsersResource usersResource = realmResource.users(); System.out.println("Realm内用户数量: " + usersResource.count()); RealmRepresentation realmRep = realmResource.toRepresentation(); System.out.println("Realm显示名称: " + realmRep.getDisplayName()); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }
方案2:手动构建时强制绑定Quarkus上下文类加载器
如果必须手动创建Keycloak实例,在操作前后切换线程的上下文类加载器,确保使用Quarkus的类加载器加载相关类。
修改你的doConnectToKeycloak方法:
private Boolean doConnectToKeycloak(String host, String realm, String clientId, String clientSecret, String username, String password) { ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); try { // 切换为Quarkus上下文类加载器 Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); if (keycloak != null) { try { keycloak.close(); } catch (Exception e) { System.out.println("关闭现有连接警告: " + e.getMessage()); } } keycloak = KeycloakBuilder.builder() .serverUrl("http://" + host) .realm(realm) .clientId(clientId) .clientSecret(clientSecret) .grantType(OAuth2Constants.PASSWORD) .username(username) .password(password) .build(); realmResource = keycloak.realm(realm); if (realmResource != null) { UsersResource usersResource = realmResource.users(); System.out.println("Realm内用户数量: " + usersResource.count()); RealmRepresentation tmpRes = realmResource.toRepresentation(); System.out.println("Realm显示名称: " + tmpRes.getDisplayName()); realmIsOk = true; } else { System.out.println("无法获取Realm资源"); realmIsOk = false; } } catch (Exception e) { System.out.println("连接失败: " + e.toString()); e.printStackTrace(); realmIsOk = false; } finally { // 恢复原类加载器 Thread.currentThread().setContextClassLoader(originalClassLoader); } return realmIsOk; }
方案3:临时禁用开发模式类卸载(仅调试用)
在application.properties中添加配置,关闭JVM类卸载功能,避免闲置时类被回收,但会增加内存占用,不建议长期使用:
quarkus.class-loader.enable-jvm-class-unloading=false
内容的提问来源于stack exchange,提问作者Murrah
相关产品推荐
相关产品推荐

