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

如何解决Dagger中TokenAuthenticator与AuthsRepository的依赖循环?

解决Dagger依赖循环问题(添加RefreshToken功能后)

问题背景

添加refreshToken功能后触发Dagger依赖循环错误,移除TokenAuthenticator对AuthsRepository的依赖则错误消失。循环依赖链路如下:

public abstract interface AppComponent extends dagger.android.AndroidInjector<com.technoface.chicmen.ChicMenApplication>
{
^
retrofit2.Retrofit is injected at
com.technoface.chicmen.RestModule.provideAuthsService(retrofit)
com.technoface.data.AuthsService is injected at
com.technoface.chicmen.RepositoryModule.provideAuthsRepository(authsService)
com.technoface.data.repository.AuthsRepository is injected at
com.technoface.chicmen.RetrofitModule.provideTokenAuthenticator(�,authsRepository)
com.technoface.data.TokenAuthenticator is injected at
com.technoface.chicmen.RetrofitApiModule.provideOkHttpClient(�,tokenAuthenticator, �)
okhttp3.OkHttpClient is injected at
com.technoface.chicmen.RetrofitApiModule.provideRetrofitClient(�,client)
com.technoface.data.RetrofitClient is injected at
com.technoface.chicmen.RetrofitApiModule.provideRetrofit(retrofitClient)
retrofit2.Retrofit is injected at
com.technoface.chicmen.RestModule.provideAuthsService(retrofit)
...

相关代码

RetrofitApiModule.kt

@AppScope
@Provides
fun provideOkHttpClient(
    headerInterceptor: HeaderInterceptor,
    tokenAuthenticator: TokenAuthenticator,
    logger: HttpLoggingInterceptor
): OkHttpClient {
    val builder = OkHttpClient.Builder()
    builder.addNetworkInterceptor(headerInterceptor)
    builder.authenticator(tokenAuthenticator)
    builder.addInterceptor(logger)
    builder.readTimeout(READ_TIME_OUT_V1, TimeUnit.SECONDS)
    builder.writeTimeout(WRITE_TIME_OUT_V1, TimeUnit.SECONDS)
    return builder.build()
}
}

RetrofitModule.kt

@Module(
    includes = [
        RetrofitApiModule::class
    ]
)

object RetrofitModule {

    @AppScope
    @REST
    @Provides
    fun provideBaseUrl(): String {
        return BuildConfig.BASE_URL
    }

    @AppScope
    @Provides
    fun provideHeaderInterceptor(
        userProvider: UserProviderListener
    ): HeaderInterceptor {
        return HeaderInterceptor(userProvider)
    }

    @AppScope
    @Provides
    fun provideTokenAuthenticator(
        tokenInvalidator: TokenInvalidatorListener,
        userProvider: UserProviderListener,
        authsRepository: AuthsRepository
    ): TokenAuthenticator {
        return TokenAuthenticator(tokenInvalidator, userProvider, authsRepository)
    }

    @AppScope
    @Provides
    fun provideLoggingInterceptor(): HttpLoggingInterceptor {
        val logging = HttpLoggingInterceptor()
        logging.setLevel(HttpLoggingInterceptor.Level.BODY)
        return logging
    }

    @AppScope
    @Provides
    fun provideGson(): Gson {
        return Gson()
    }

}

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class REST

RestModule.kt

@Module(
    includes = [
        RetrofitModule::class
    ]
)

object RestModule {

    @AppScope
    @Provides
    fun provideAuthsService(
        retrofit: Retrofit
    ): AuthsService {
        return retrofit.create(AuthsService::class.java)
    }

TokenAuthenticator.kt

class TokenAuthenticator(
    private val tokenInvalidator: TokenInvalidatorListener,
    private val userProvider: UserProviderListener,
    private val authsRepository: AuthsRepository
) : Authenticator {

    override fun authenticate(route: Route?, response: Response): Request? {
        return if (userProvider.getIsLoginUser()) {
            val refreshResponse = authsRepository.refreshToken().blockingGet().exceptionOrNull()
            if (refreshResponse != null) {
                tokenInvalidator.invalidateUser()
                userProvider.logout()
                return null
            }
            return response.request()
        } else {
            null
        }
    }

}

AuthsRepository.kt

class AuthsRepository(
    private val authsService: AuthsService
) : Repository {

    fun verifyOtpForgotPassword(request: VerifyOtpForgotPasswordRequest): Single<Result<RestResponse<Any>>> {
        return authsService.verifyOtpForgotPassword(request).map {
            Result.success(it)
        }.onErrorReturn {
            Result.failure(it)
        }
    }

    fun refreshToken(): Single<Result<RestResponse<LoginResponse>>> {
        return authsService.refreshToken().map {
            Result.success(it)
        }.onErrorReturn {
            Result.failure(it)
        }
    }

}

解决方案

方案1:为RefreshToken请求创建独立的OkHttpClient

刷新token的请求不需要触发token验证(否则会无限循环),因此可以单独为该请求创建一个不绑定TokenAuthenticator的OkHttpClient和Retrofit实例,打破循环链。

步骤1:新增刷新专用的OkHttpClient和Retrofit

在RetrofitApiModule中添加:

@AppScope
@Provides
@Named("refresh")
fun provideRefreshTokenOkHttpClient(
    headerInterceptor: HeaderInterceptor,
    logger: HttpLoggingInterceptor
): OkHttpClient {
    val builder = OkHttpClient.Builder()
    builder.addNetworkInterceptor(headerInterceptor)
    // 不添加TokenAuthenticator,避免循环
    builder.addInterceptor(logger)
    builder.readTimeout(READ_TIME_OUT_V1, TimeUnit.SECONDS)
    builder.writeTimeout(WRITE_TIME_OUT_V1, TimeUnit.SECONDS)
    return builder.build()
}

@AppScope
@Provides
@Named("refresh")
fun provideRefreshRetrofit(
    @REST baseUrl: String,
    @Named("refresh") client: OkHttpClient,
    gson: Gson
): Retrofit {
    return Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()
}

步骤2:新增刷新专用的AuthsService

在RestModule中添加:

@AppScope
@Provides
@Named("refresh")
fun provideRefreshAuthsService(
    @Named("refresh") retrofit: Retrofit
): AuthsService {
    return retrofit.create(AuthsService::class.java)
}

步骤3:修改AuthsRepository使用刷新专用的Service

class AuthsRepository(
    private val authsService: AuthsService,
    @Named("refresh") private val refreshAuthsService: AuthsService
) : Repository {

    // 其他方法保持不变

    fun refreshToken(): Single<Result<RestResponse<LoginResponse>>> {
        return refreshAuthsService.refreshToken().map {
            Result.success(it)
        }.onErrorReturn {
            Result.failure(it)
        }
    }
}

方案2:使用Dagger Lazy延迟注入

通过Dagger的Lazy<T>延迟获取AuthsRepository,避免在初始化TokenAuthenticator时立即实例化依赖,从而打破循环。

步骤1:修改TokenAuthenticator的依赖类型

class TokenAuthenticator(
    private val tokenInvalidator: TokenInvalidatorListener,
    private val userProvider: UserProviderListener,
    private val authsRepository: Lazy<AuthsRepository> // 改为Lazy
) : Authenticator {

    override fun authenticate(route: Route?, response: Response): Request? {
        return if (userProvider.getIsLoginUser()) {
            // 需要使用时才调用get()获取实例
            val refreshResult = authsRepository.get().refreshToken().blockingGet()
            if (refreshResult.isFailure) {
                tokenInvalidator.invalidateUser()
                userProvider.logout()
                return null
            }
            // 注意:这里需要更新请求头中的新token,原代码未处理,需补充
            refreshResult.getOrNull()?.data?.let { loginResponse ->
                userProvider.saveToken(loginResponse.token)
                return response.request().newBuilder()
                    .header("Authorization", "Bearer ${loginResponse.token}")
                    .build()
            }
            return null
        } else {
            null
        }
    }
}

步骤2:修改RetrofitModule中的提供方法

@AppScope
@Provides
fun provideTokenAuthenticator(
    tokenInvalidator: TokenInvalidatorListener,
    userProvider: UserProviderListener,
    authsRepository: Lazy<AuthsRepository> // 改为Lazy
): TokenAuthenticator {
    return TokenAuthenticator(tokenInvalidator, userProvider, authsRepository)
}

注意事项

  • 方案1更稳妥,避免了刷新请求触发验证循环的风险,适合大多数场景
  • 方案2更简洁,但要确保调用Lazy.get()时依赖已经完成初始化(网络请求触发的authenticate方法满足这个条件)

内容的提问来源于stack exchange,提问作者Ahmet Yılmaz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:27:00