You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@RequestScope Bean为null时如何处理NullBean类型转换异常

Spring Boot集成Keycloak时匿名请求AccessToken注入异常解决方案

问题现象

Spring Boot应用通过Keycloak完成安全加固后,除从请求上下文加载当前AccessToken的逻辑外,其余功能均运行正常。
原有AccessToken的Bean定义如下:

@Bean
@RequestScope(proxyMode = TARGET_CLASS)
public AccessToken accessToken() {
  return Optional.ofNullable(
          (KeycloakSecurityContext)
              RequestContextHolder.currentRequestAttributes()
                  .getAttribute(KeycloakSecurityContext.class.getName(), SCOPE_REQUEST))
      .map(KeycloakSecurityContext::getToken)
      .orElse(null);
}

在业务类中通过如下方式注入该Bean:

@Resource(name = "accessToken")
private AccessToken accessToken;

当用户已登录、请求携带有效Token时,所有逻辑运行符合预期;但匿名无Token的请求会抛出如下类型转换异常:

Caused by: java.lang.ClassCastException: class org.springframework.beans.factory.support.NullBean cannot be cast to class org.keycloak.representations.JsonWebToken (org.springframework.beans.factory.support.NullBean and org.keycloak.representations.JsonWebToken are in unnamed module of loader 'app')

常规null判断逻辑始终无法命中,异常仍会抛出,原有业务判断代码如下:

public Optional<TenantAdapter> current() {
   // 该判断始终返回false
   if (accessToken.equals(null)) {
      return Optional.empty();
   } else {
      // 此处会抛出异常
       Map<String, Object> claims = accessToken.getOtherClaims();
    
   }   
}

问题根因

  1. Spring对请求作用域(RequestScope)的Bean做AOP代理时,如果工厂方法返回null,不会向注入点赋值null,而是生成一个NullBean类型的代理对象作为占位。这个代理对象本身不是null,因此所有针对null的判断都不会命中;当尝试调用代理对象上属于AccessToken类型的方法时,就会触发类型转换异常。
  2. 原有判断逻辑accessToken.equals(null)本身存在Java基础逻辑错误:按照Java equals方法的约定,任何非null对象调用equals(null)都必须返回false;如果accessToken真的为null,调用equals方法会直接抛出空指针异常,正确的null值判断应该使用== null,但该判断在此场景下依然无法识别NullBean代理。

修复方案

优先选择无Spring内部API耦合的实现方式:

  • 调整Bean定义,禁止请求作用域Bean的工厂方法返回null,使用Optional<AccessToken>作为Bean的返回类型,从根源上避免NullBean代理生成:
@Bean
@RequestScope(proxyMode = TARGET_CLASS)
public Optional<AccessToken> accessToken() {
  return Optional.ofNullable(
          (KeycloakSecurityContext)
              RequestContextHolder.currentRequestAttributes()
                  .getAttribute(KeycloakSecurityContext.class.getName(), SCOPE_REQUEST))
      .map(KeycloakSecurityContext::getToken);
}
  • 对应调整注入和业务判断逻辑,直接通过Optional的方法判断Token是否存在,无需额外写null判断:
@Resource(name = "accessToken")
private Optional<AccessToken> accessToken;

public Optional<TenantAdapter> current() {
   return accessToken.map(token -> {
       Map<String, Object> claims = token.getOtherClaims();
       // 此处编写已登录状态下的租户信息转换逻辑
       return buildTenantAdapterFromClaims(claims);
   });
}

如果暂时无法修改Bean的返回类型,也可以通过判断代理对象类型识别空Token场景,注意该方式依赖Spring内部API,后续版本升级可能存在兼容性问题:

public Optional<TenantAdapter> current() {
   if (accessToken instanceof NullBean) {
      return Optional.empty();
   }
   Map<String, Object> claims = accessToken.getOtherClaims();
   // 后续业务逻辑
}

内容的提问来源于stack exchange,提问作者Stefano Veloccia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 08:18:28