依赖注入 #

一、依赖注入概述 #

依赖注入(DI)是一种设计模式,用于实现控制反转(IoC),将依赖的创建和管理交给框架处理。

text
传统方式                          依赖注入方式
┌─────────────────┐              ┌─────────────────┐
│     组件        │              │     组件        │
│                 │              │                 │
│ service = new   │              │ constructor(    │
│   Service()     │              │   private svc   │
│                 │              │ )               │
└─────────────────┘              └────────┬────────┘
        ↓                                 ↓
   自己创建依赖                    ┌─────────────────┐
                                  │    注入器       │
                                  │  自动创建注入   │
                                  └─────────────────┘

二、核心概念 #

2.1 注入器(Injector) #

注入器负责创建和管理依赖实例。

typescript
// Angular内部维护注入器树
RootInjector
    ├── ModuleInjector (模块级)
    │   └── ComponentInjector (组件级)
    │       └── ComponentInjector
    └── ...

2.2 提供者(Provider) #

提供者告诉注入器如何创建实例。

typescript
// 类型提供者(最常用)
providers: [UserService]

// 等价于
providers: [
  { provide: UserService, useClass: UserService }
]

2.3 令牌(Token) #

令牌用于标识依赖项。

typescript
// 类型令牌
{ provide: UserService, useClass: UserService }

// 字符串令牌(不推荐)
{ provide: 'API_URL', useValue: 'https://api.example.com' }

// InjectionToken(推荐)
export const API_URL = new InjectionToken<string>('API_URL');
{ provide: API_URL, useValue: 'https://api.example.com' }

三、提供者类型 #

3.1 类提供者 #

typescript
// 直接使用类
providers: [UserService]

// 使用useClass
providers: [
  { provide: UserService, useClass: UserService }
]

// 使用不同的实现
providers: [
  { provide: LoggerService, useClass: ConsoleLoggerService }
]

// 条件提供
providers: [
  {
    provide: LoggerService,
    useClass: environment.production 
      ? ProductionLoggerService 
      : ConsoleLoggerService
  }
]

3.2 值提供者 #

typescript
providers: [
  { provide: API_URL, useValue: 'https://api.example.com' },
  { 
    provide: APP_CONFIG, 
    useValue: {
      apiUrl: 'https://api.example.com',
      timeout: 30000
    }
  }
]

3.3 工厂提供者 #

typescript
providers: [
  {
    provide: UserService,
    useFactory: (http: HttpClient, config: AppConfig) => {
      return new UserService(http, config.apiUrl);
    },
    deps: [HttpClient, APP_CONFIG]
  }
]

// 条件工厂
providers: [
  {
    provide: LoggerService,
    useFactory: () => {
      if (environment.production) {
        return new ProductionLoggerService();
      }
      return new ConsoleLoggerService();
    }
  }
]

3.4 别名提供者 #

typescript
providers: [
  OldUserService,
  { provide: NewUserService, useExisting: OldUserService }
]

四、注入层级 #

4.1 层级注入器 #

text
PlatformInjector (平台级)
    │
    └── RootInjector (根注入器)
            │
            ├── ModuleInjector (模块级)
            │       │
            │       └── ComponentInjector (组件级)
            │               │
            │               └── ComponentInjector
            │
            └── ...

4.2 providedIn配置 #

typescript
// 根注入器(全局单例)
@Injectable({
  providedIn: 'root'
})
export class GlobalService { }

// 平台注入器(跨应用共享)
@Injectable({
  providedIn: 'platform'
})
export class PlatformService { }

// 任何注入器(懒加载模块独立实例)
@Injectable({
  providedIn: 'any'
})
export class FeatureService { }

// 特定模块
@Injectable({
  providedIn: UserModule
})
export class ModuleService { }

4.3 组件级注入 #

typescript
@Component({
  selector: 'app-user',
  template: '...',
  providers: [UserService]  // 每个组件实例独立的服务实例
})
export class UserComponent {
  constructor(private userService: UserService) {}
}

4.4 模块级注入 #

typescript
@NgModule({
  providers: [UserService]
})
export class UserModule { }

五、依赖注入进阶 #

5.1 @Inject装饰器 #

typescript
import { Inject } from '@angular/core';

@Component({
  selector: 'app-example',
  template: '...'
})
export class ExampleComponent {
  constructor(
    @Inject(API_URL) private apiUrl: string,
    @Inject(APP_CONFIG) private config: AppConfig
  ) {}
}

5.2 @Optional装饰器 #

typescript
import { Optional } from '@angular/core';

@Component({
  selector: 'app-example',
  template: '...'
})
export class ExampleComponent {
  constructor(
    @Optional() private logger?: LoggerService
  ) {
    if (this.logger) {
      this.logger.log('Logger available');
    }
  }
}

5.3 @Self装饰器 #

只在当前注入器查找:

typescript
import { Self } from '@angular/core';

@Component({
  selector: 'app-example',
  template: '...',
  providers: [UserService]
})
export class ExampleComponent {
  constructor(
    @Self() private userService: UserService
  ) {}
}

5.4 @SkipSelf装饰器 #

跳过当前注入器,从父级开始查找:

typescript
import { SkipSelf } from '@angular/core';

@Component({
  selector: 'app-child',
  template: '...'
})
export class ChildComponent {
  constructor(
    @SkipSelf() private parentService: ParentService
  ) {}
}

5.5 @Host装饰器 #

在宿主组件处停止查找:

typescript
import { Host } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(
    @Host() private hostComponent: HostComponent
  ) {}
}

5.6 组合使用 #

typescript
constructor(
  @Optional() @SkipSelf() private service?: MyService
) {
  this.service = service ?? new DefaultService();
}

六、InjectionToken #

6.1 创建InjectionToken #

typescript
import { InjectionToken } from '@angular/core';

// 简单类型
export const API_URL = new InjectionToken<string>('API_URL');

// 复杂类型
export interface AppConfig {
  apiUrl: string;
  timeout: number;
  theme: 'light' | 'dark';
}

export const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');

// 带描述
export const FEATURE_FLAGS = new InjectionToken<FeatureFlags>(
  'FeatureFlags',
  {
    providedIn: 'root',
    factory: () => ({
      featureA: true,
      featureB: false
    })
  }
);

6.2 使用InjectionToken #

typescript
// 提供值
@NgModule({
  providers: [
    { provide: API_URL, useValue: 'https://api.example.com' },
    { 
      provide: APP_CONFIG, 
      useValue: {
        apiUrl: 'https://api.example.com',
        timeout: 30000,
        theme: 'light'
      }
    }
  ]
})
export class AppModule { }

// 注入使用
@Injectable({ providedIn: 'root' })
export class ApiService {
  constructor(
    @Inject(API_URL) private apiUrl: string,
    @Inject(APP_CONFIG) private config: AppConfig
  ) {}
}

6.3 带工厂的InjectionToken #

typescript
export const LOCAL_STORAGE = new InjectionToken<Storage>(
  'LocalStorage',
  {
    providedIn: 'root',
    factory: () => window.localStorage
  }
);

export const WINDOW = new InjectionToken<Window>(
  'Window',
  {
    providedIn: 'root',
    factory: () => window
  }
);

// 使用
@Injectable({ providedIn: 'root' })
export class StorageService {
  constructor(
    @Inject(LOCAL_STORAGE) private storage: Storage
  ) {}
  
  setItem(key: string, value: string) {
    this.storage.setItem(key, value);
  }
}

七、多提供者 #

7.1 multi提供者 #

typescript
export const LOG_HANDLER = new InjectionToken<LogHandler>('LogHandler');

@NgModule({
  providers: [
    { provide: LOG_HANDLER, useClass: ConsoleLogHandler, multi: true },
    { provide: LOG_HANDLER, useClass: FileLogHandler, multi: true },
    { provide: LOG_HANDLER, useClass: RemoteLogHandler, multi: true }
  ]
})
export class AppModule { }

7.2 注入多个值 #

typescript
@Injectable()
export class LogService {
  constructor(
    @Inject(LOG_HANDLER) private handlers: LogHandler[]
  ) {}
  
  log(message: string) {
    this.handlers.forEach(handler => handler.handle(message));
  }
}

7.3 HTTP拦截器示例 #

typescript
@NgModule({
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: LoggingInterceptor,
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: CacheInterceptor,
      multi: true
    }
  ]
})
export class AppModule { }

八、循环依赖解决 #

8.1 使用forwardRef #

typescript
import { forwardRef } from '@angular/core';

@Injectable()
export class ServiceA {
  constructor(
    @Inject(forwardRef(() => ServiceB))
    private serviceB: ServiceB
  ) {}
}

@Injectable()
export class ServiceB {
  constructor(private serviceA: ServiceA) {}
}

8.2 使用Injector #

typescript
@Injectable()
export class ServiceA {
  private serviceB: ServiceB;
  
  constructor(private injector: Injector) {}
  
  doSomething() {
    if (!this.serviceB) {
      this.serviceB = this.injector.get(ServiceB);
    }
    this.serviceB.doOther();
  }
}

8.3 重构避免循环依赖 #

typescript
// 提取共享逻辑到独立服务
@Injectable()
export class SharedService { }

@Injectable()
export class ServiceA {
  constructor(private shared: SharedService) {}
}

@Injectable()
export class ServiceB {
  constructor(private shared: SharedService) {}
}

九、依赖注入最佳实践 #

9.1 使用providedIn: ‘root’ #

typescript
// 推荐
@Injectable({
  providedIn: 'root'
})
export class UserService { }

// 不推荐(除非需要模块级作用域)
@NgModule({
  providers: [UserService]
})
export class UserModule { }

9.2 使用InjectionToken #

typescript
// 推荐
export const API_URL = new InjectionToken<string>('API_URL');

// 不推荐
providers: [
  { provide: 'apiUrl', useValue: '...' }
]

9.3 避免循环依赖 #

typescript
// 重构服务结构,提取共享逻辑
// 或使用Injector延迟获取

9.4 合理使用作用域 #

场景 推荐方式
全局共享服务 providedIn: 'root'
模块独立服务 providedIn: FeatureModule
组件独立状态 组件级providers

十、总结 #

概念 说明
Injector 创建和管理依赖实例
Provider 定义如何创建实例
Token 标识依赖项
@Inject 注入特定Token
@Optional 可选依赖
@Self 当前注入器查找
@SkipSelf 跳过当前注入器
@Host 宿主处停止查找
InjectionToken 类型安全的注入令牌
multi 多提供者

下一步:路由基础

最后更新:2026-03-26