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

Hilt依赖循环问题求助:拦截器中如何调用ApiService

解决Hilt中的依赖循环:拦截器调用ApiService刷新令牌

我在使用Hilt时遇到了[Dagger/DependencyCycle]依赖循环错误,想通过拦截器在令牌过期时完成刷新操作,但修复未果。以下是相关代码和错误信息:

AuthInterceptor类代码

class AuthInterceptor @Inject constructor(private val apiService: ApiService) : Interceptor, Authenticator {

    private val preferenceProvider = PreferenceProvider()

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        request = request.newBuilder()
            .addHeader("Content-Type", "application/json")
            .addHeader("Accept", "application/json")
            .build()
        return chain.proceed(request)
    }

    override fun authenticate(route: Route?, response: Response): Request? {
        val token = preferenceProvider.getString(PREF_TOKEN) ?: return null
        // 此处计划调用apiService的刷新令牌方法
        return null
    }
}

NetworkModule类代码

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Singleton
    @Provides
    fun provideHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.HEADERS
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        val builder = OkHttpClient.Builder()
        builder.addInterceptor(interceptor)
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .addInterceptor(authInterceptor)
            .authenticator(authInterceptor)
        return builder.build()
    }

    @Singleton
    @Provides
    fun provideConverterFactory(): GsonConverterFactory {
        return GsonConverterFactory.create()
    }

    @Singleton
    @Provides
    fun provideRetrofitInstance(okHttpClient: OkHttpClient, gsonConverterFactory: GsonConverterFactory) : Retrofit {
        return Retrofit.Builder().baseUrl("BASE_URL").client(okHttpClient).addConverterFactory(gsonConverterFactory).build()
    }

    @Singleton
    @Provides
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
}

错误信息

.ApiService 被注入到
AuthInterceptor(apiService)
.AuthInterceptor 被注入到
.NetworkModule.provideHttpClient(authInterceptor)
okhttp3.OkHttpClient 被注入到
.NetworkModule.provideRetrofitInstance(okHttpClient, …)
retrofit2.Retrofit 被注入到
.NetworkModule.provideApiService(retrofit)
.ApiService 被注入到
.AuthInterceptor(apiService)
...


解决方案

1. 用Lazy<ApiService>打破依赖循环

依赖循环的核心是AuthInterceptor → ApiService → Retrofit → OkHttpClient → AuthInterceptor,使用Hilt的Lazy延迟初始化ApiService,避免创建AuthInterceptor时立刻触发ApiService的实例化。

修改AuthInterceptor的构造和使用逻辑:

class AuthInterceptor @Inject constructor(
    private val apiService: Lazy<ApiService>,
    private val preferenceProvider: PreferenceProvider // 建议改为注入,而非手动new
) : Interceptor, Authenticator {

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        request = request.newBuilder()
            .addHeader("Content-Type", "application/json")
            .addHeader("Accept", "application/json")
            .build()
        return chain.proceed(request)
    }

    override fun authenticate(route: Route?, response: Response): Request? {
        val token = preferenceProvider.getString(PREF_TOKEN) ?: return null
        // 通过apiService.value获取实例,此时OkHttpClient已创建完成
        val refreshResponse = apiService.value.refreshToken(token).execute()
        val newToken = refreshResponse.body()?.token ?: return null
        
        preferenceProvider.putString(PREF_TOKEN, newToken)
        // 返回携带新令牌的请求
        return response.request.newBuilder()
            .header("Authorization", "Bearer $newToken")
            .build()
    }
}

2. 优化:避免刷新请求被拦截

为防止刷新令牌的请求被AuthInterceptor重复拦截导致无限循环,在intercept方法中添加路径判断:

override fun intercept(chain: Interceptor.Chain): Response {
    val request = chain.request()
    // 跳过刷新令牌接口的拦截逻辑
    if (request.url.pathSegments.contains("refresh-token")) {
        return chain.proceed(request)
    }
    
    val modifiedRequest = request.newBuilder()
        .addHeader("Content-Type", "application/json")
        .addHeader("Accept", "application/json")
        .addHeader("Authorization", "Bearer ${preferenceProvider.getString(PREF_TOKEN)}")
        .build()
    return chain.proceed(modifiedRequest)
}

3. 补充:注入PreferenceProvider

将手动创建的PreferenceProvider改为依赖注入,保持一致性:

// 给PreferenceProvider添加@Inject构造
class PreferenceProvider @Inject constructor(
    @ApplicationContext private val context: Context
) {
    // 原有逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:37:39