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

Angular 19:为何每次注入@Injectable服务都会创建新实例?

Angular认证服务被多次实例化,路由守卫无法获取正确认证状态

我创建了一个标记为@Injectable的认证服务AuthService,将_isAuthenticated状态以类属性的形式保存,并通过providedIn: 'root'配置为根注入器提供。随后我将该服务注入到函数式路由守卫中,期望Angular以单例模式注入,但实际每次路由守卫生效时都会创建新的服务实例。

在服务构造函数中设置断点后发现,每次路由守卫触发都会进入构造函数,导致守卫只能获取到_isAuthenticated的初始值false,无法获取登录后的正确状态。

请问为什么会出现每次注入都创建新服务实例的问题?


相关代码

AuthService代码

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, catchError, map, Observable, of } from 'rxjs';
import { User } from '../dto/user';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  
  private apiUrl = 'http://192.168.178.39:8080/auth';
  private _isAuthenticated = new BehaviorSubject<boolean>(false);

  constructor(private http: HttpClient) {
    localStorage.removeItem('JWT_Token');
  }

  login(username: string, password: string ): Observable<boolean> {
    return this.http.post<any>(this.apiUrl + '/login', {username, password})
      .pipe(
        map(response => {
          localStorage.setItem('JWT_Token', response.token);
          this._isAuthenticated.next(true);
          return true;
        }),
        catchError(error => {
          console.log(error);
          this.logout();
          return of(false);
        })
      );
  }

  logout(): void {
    localStorage.removeItem('JWT_Token');
    this._isAuthenticated.next(false);
  }

  get isAuthenticated() {
    return this._isAuthenticated.value;
  }
}

路由守卫代码

import { CanActivateFn } from '@angular/router';
import { AuthService } from '../service/auth.service';
import { inject } from '@angular/core';

export const authGuard: CanActivateFn = (route, state) => {

  const authService = inject(AuthService);
  let isAuth = authService.isAuthenticated;

  return isAuth;
};

app.routes.ts代码

import { Routes } from '@angular/router';
import { HomeComponent } from './feature/home/home.component';
import { LoginComponent } from './core/auth/login/login.component';
import { BookListComponent } from './feature/bookstore/book-list/book-list.component';
import { authGuard } from './core/auth/guard/auth.guard';

export const routes: Routes = [
    {path: 'login', component : LoginComponent},
    {path: '', redirectTo: 'login', pathMatch : 'full'},
    {path: 'home', component : HomeComponent, canActivate : [authGuard]},
    {path: 'bookstore', component : BookListComponent, canActivate : [authGuard]},
];

app.config.ts代码

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { AuthInterceptor } from './core/auth/intercept/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }), 
    provideRouter(routes),
    provideHttpClient(withInterceptorsFromDi()),
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
  ]
};

问题分析与解决方案

1. 核心排查:是否真的是多实例

首先通过添加实例ID验证服务是否为单例:

@Injectable({ providedIn: 'root' })
export class AuthService {
  private instanceId = Math.random().toString(36).slice(2, 10);

  constructor(private http: HttpClient) {
    console.log('AuthService实例化,ID:', this.instanceId);
    // 原初始化逻辑
  }
}
  • 如果控制台输出的instanceId始终相同:说明是单例,问题出在状态维护逻辑
  • 如果instanceId每次不同:说明服务被重复提供,需排查配置

2. 常见原因及修复

原因1:服务被重复声明

检查项目中是否在组件/模块的providers数组中重复声明了AuthService(比如某个组件的@Component装饰器里添加了providers: [AuthService]),这会创建局部注入器的独立实例,覆盖根注入器的单例。

修复: 移除所有非根注入器层级的AuthService声明,仅保留@Injectable({ providedIn: 'root' })配置。

原因2:构造函数的错误逻辑干扰

你的AuthService构造函数中强制执行localStorage.removeItem('JWT_Token');,这会在服务初始化时清除token,即使是单例服务,也会导致认证状态被重置为false,让你误以为是多实例问题。

修复: 移除构造函数中的token清除逻辑,改为初始化时恢复已有token的认证状态:

constructor(private http: HttpClient) {
  // 初始化时读取本地存储的token,恢复认证状态
  const token = localStorage.getItem('JWT_Token');
  this._isAuthenticated.next(!!token);
}
原因3:守卫获取状态的方式不严谨

当前守卫直接读取BehaviorSubject的value,无法实时响应状态变化。建议改为返回Observable,确保获取最新状态:

// 在AuthService中添加公共的Observable获取方法
get isAuthenticated$(): Observable<boolean> {
  return this._isAuthenticated.asObservable();
}

// 路由守卫中使用
export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  return authService.isAuthenticated$;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 02:33:10