Java-Angular依赖注入深度解析


文档摘要

Angular依赖注入深度解析 (2026年03月27日) 依赖注入(DI)概述 依赖注入是Angular的核心特性之一,它是一种设计模式,用于实现控制反转(IoC)。DI使得类之间的依赖关系由容器(Injector)管理,而不是由类自身创建依赖对象。这种模式提高了代码的可测试性、可维护性和可扩展性。 Angular DI系统架构 Injector(注入器) 注入器是Angular DI系统的核心,负责创建和管理依赖项的实例。Angular在启动时会创建一个层级注入器树。 Provider(提供者) Provider告诉注入器如何创建或获取依赖项。Provider是注册依赖项的配置对象。 Token(令牌) Token是依赖项的唯一标识符,注入器通过Token查找对应的Provider。

Angular依赖注入深度解析 (2026年03月27日)

依赖注入(DI)概述

依赖注入是Angular的核心特性之一,它是一种设计模式,用于实现控制反转(IoC)。DI使得类之间的依赖关系由容器(Injector)管理,而不是由类自身创建依赖对象。这种模式提高了代码的可测试性、可维护性和可扩展性。

Angular DI系统架构

Injector(注入器)

注入器是Angular DI系统的核心,负责创建和管理依赖项的实例。Angular在启动时会创建一个层级注入器树。

Provider(提供者)

Provider告诉注入器如何创建或获取依赖项。Provider是注册依赖项的配置对象。

Token(令牌)

Token是依赖项的唯一标识符,注入器通过Token查找对应的Provider。

Provider配置方式

Class Provider

// 最常见的Provider类型 providers: [ { provide: UserService, useClass: UserService } ] // 简写形式 providers: [UserService]

Value Provider

// 提供固定值 export const API_CONFIG = { baseUrl: 'https://api.example.com', timeout: 5000 }; providers: [ { provide: 'API_CONFIG', useValue: API_CONFIG } ] // 使用InjectionToken import { InjectionToken } from '@angular/core'; export const API_CONFIG = new InjectionToken<any>('api.config'); providers: [ { provide: API_CONFIG, useValue: { baseUrl: 'https://api.example.com' } } ]

Factory Provider

// 使用工厂函数创建依赖 export function createLogger(config: AppConfig) { return new Logger(config.enableDebug); } providers: [ { provide: AppConfig, useValue: { enableDebug: true } }, { provide: Logger, useFactory: createLogger, deps: [AppConfig] } ]

Alias Provider

// 为现有服务创建别名 providers: [ { provide: UserService, useClass: UserService }, { provide: 'IUserService', useExisting: UserService } ]

依赖注入装饰器

@Injectable()

// 标记服务为可注入的服务 @Injectable({ providedIn: 'root' // 注册到根注入器 }) export class UserService { constructor(private http: HttpClient) {} }

@Inject()

// 注入非类类型的依赖 import { Inject, Injectable } from '@angular/core'; @Injectable() export class ApiService { constructor( @Inject(API_CONFIG) private config: { baseUrl: string } ) {} get url() { return this.config.baseUrl; } }

@Optional()

// 标记可选依赖 export class MyComponent { constructor( @Optional() private logger?: LoggerService ) {} }

@Self()、@SkipSelf()、@Host()

export class ChildComponent { constructor( @Self() private localService: LocalService, // 只查看自身的注入器 @SkipSelf() private parentService: ParentService, // 跳过自身,从父级开始查找 @Host() private hostService: HostService // 限制在宿主元素范围内 ) {} }

注入器层级

模块注入器

// 在模块级别提供服务 @NgModule({ providers: [UserService] // 注册到模块注入器 }) export class AppModule {}

组件注入器

// 在组件级别提供服务 @Component({ selector: 'app-root', providers: [LoggerService], // 仅对该组件及其子组件可见 template: '...' }) export class AppComponent {}

ElementInjector与ModuleInjector

Angular v14+引入了新的注入器层级结构:

  • ModuleInjector:模块级注入器,使用@NgModule.providers@Injectable({providedIn})注册
  • ElementInjector:元素级注入器,使用@Component.providers@Directive.providers注册

解析顺序:ElementInjector(自身)→ ElementInjector(父级)→ ModuleInjector

依赖注入实战

服务类开发

// user.service.ts @Injectable({ providedIn: 'root' }) export class UserService { private users$ = new BehaviorSubject<User[]>([]); constructor(private http: HttpClient) {} getUsers(): Observable<User[]> { return this.http.get<User[]>('/api/users').pipe( tap(users => this.users$.next(users)) ); } getUserById(id: number): Observable<User> { return this.http.get<User>(`/api/users/${id}`); } createUser(user: User): Observable<User> { return this.http.post<User>('/api/users', user).pipe( tap(newUsers => this.getUsers().subscribe()) ); } }

组件中使用服务

// user-list.component.ts @Component({ selector: 'app-user-list', template: ` <div *ngFor="let user of users | async"> {{ user.name }} </div> ` }) export class UserListComponent implements OnInit { users$: Observable<User[]>; constructor(private userService: UserService) {} ngOnInit() { this.users$ = this.userService.getUsers(); } }

服务间依赖

// auth.service.ts @Injectable({ providedIn: 'root' }) export class AuthService { constructor(private http: HttpClient) {} login(credentials: Credentials): Observable<Token> { return this.http.post<Token>('/api/login', credentials).pipe( tap(token => localStorage.setItem('token', token.value)) ); } } // data.service.ts @Injectable({ providedIn: 'root' }) export class DataService { constructor( private http: HttpClient, private authService: AuthService // 依赖AuthService ) {} getData(): Observable<any> { const token = localStorage.getItem('token'); const headers = new HttpHeaders().set('Authorization', `Bearer ${token}`); return this.http.get('/api/data', { headers }); } }

高级特性

自定义装饰器

// 便捷注入装饰器 export function InjectConfig(configKey: string) { return function(target: any, key: string | symbol | undefined, index: number) { const originalType = Reflect.getMetadata('design:paramtypes', target)[index]; Object.defineProperty(target, key, { get() { const config = this.injector.get(APP_CONFIG); return config[configKey]; } }); }; } // 使用自定义装饰器 export class MyComponent { constructor(@InjectConfig('apiUrl') public apiUrl: string) {} }

InjectionToken类型安全

// 创建类型安全的InjectionToken import { InjectionToken } from '@angular/core'; interface AppConfig { apiUrl: string; enableDebug: boolean; } export const APP_CONFIG = new InjectionToken<AppConfig>('app.config'); // 在模块中提供 @NgModule({ providers: [ { provide: APP_CONFIG, useValue: { apiUrl: 'https://api.example.com', enableDebug: true } as AppConfig } ] }) export class AppModule {} // 在组件中注入 export class MyComponent { constructor(@Inject(APP_CONFIG) private config: AppConfig) {} }

测试中的依赖注入

// TestBed配置 describe('UserService', () => { let service: UserService; let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [UserService] }); service = TestBed.inject(UserService); httpMock = TestBed.inject(HttpTestingController); }); afterEach(() => { httpMock.verify(); }); it('should get users', () => { const mockUsers = [{ id: 1, name: 'John' }]; service.getUsers().subscribe(users => { expect(users).toEqual(mockUsers); }); const req = httpMock.expectOne('/api/users'); req.flush(mockUsers); }); });

最佳实践

  1. 服务注册位置

    • 全局共享服务:@Injectable({providedIn: 'root'})
    • 特定模块服务:在@NgModule.providers中注册
    • 组件级服务:在@Component.providers中注册
  2. 依赖项设计原则

    • 保持服务简单,单一职责
    • 避免循环依赖
    • 使用接口提高可测试性
  3. 性能优化

    • 懒加载模块的服务仅在模块加载时实例化
    • 使用providedIn: 'root'实现单例服务,自动tree-shaking
    • 避免在构造函数中执行耗时操作
  4. 可测试性

    • 使用依赖注入便于Mock依赖项
    • 保持服务方法纯净,避免副作用
    • 使用TestBed进行单元测试
  5. 类型安全

    • 优先使用类Provider而非字符串Token
    • 使用InjectionToken提供类型检查
    • 为复杂依赖项定义接口

运行流程

  1. Angular启动时创建root注入器
  2. root注入器读取@NgModule.providers@Injectable({providedIn})注册的Provider
  3. 当请求某个依赖时,注入器根据Token查找Provider
  4. 注入器创建(或复用)依赖项实例
  5. 注入器将依赖项注入到请求者中

Angular的依赖注入系统是其强大功能的基石,掌握DI对于开发可维护、可测试的Angular应用至关重要。


发布者: 作者: 转发
评论区 (0)
U