首页
/ 使用 @Service 装饰器创建可注入服务:Angular 依赖注入入门实战

使用 @Service 装饰器创建可注入服务:Angular 依赖注入入门实战

2026-09-07 12:57:03作者:韦蓉瑛

依赖注入(Dependency Injection,DI)是 Angular 框架中最强大的特性之一,它让框架能在应用运行时自动"提供"你所需要的一切资源(service 或其它对象),组件无需关心对象如何创建与组装。本篇文章对应 Angular 仓库中 Learn Angular 交互式教程的第 19 步 19-creating-an-injectable-service,围绕其中的 CarService 实例展开,带你掌握用 @Service 装饰器把一个普通 TypeScript 类变为可注入服务的方法,并理解其在依赖注入系统底层是如何编译生效的,为下一步将服务真正注入组件铺平道路。

本教程所有源码与练习都位于仓库内 19-creating-an-injectable-service 目录下,其中 src/app/car.service.ts 是练习初始文件,answer/src/app/car.service.ts 是完成后的参考答案,可随时对照查阅。

什么是服务(Service)?为什么要把它做成可注入

服务是用来与数据、API 交互的常用载体。要设计出可复用的服务,核心思路是:把业务逻辑封装在服务内部,当应用任何地方需要它时,再共享给对应组件。这样组件保持轻量、专注视图,数据获取与业务规则只维护一份。

教程中的 car.service.ts 就是一个典型例子,它维护了一份车辆清单并对外提供查询方法:

export class CarService {
  cars = ['Sunflower GT', 'Flexus Sport', 'Sprout Mach One'];

  getCars(): string[] {
    return this.cars;
  }

  getCar(id: number) {
    return this.cars[id];
  }
}

这个类此刻还只是普通类——你可以手动 new CarService(),但它尚未接入 Angular 的依赖注入体系,无法被组件通过注入的方式使用。让它"可注入"的关键一步,就是添加 @Service 装饰器。

添加 @Service 装饰器,让类可被 DI 注入

为了让一个类能被 DI 系统注入,请在类上方使用 @Service 装饰器:

import {Service} from '@angular/core';

@Service()
class UserService {
  // methods to retrieve and return data
}

@Service 装饰器做了两件事:

  1. 把类标记为 service
  2. 通知 DI 系统:UserService 可以在应用的任何地方被访问

默认情况下 Angular 会在整个应用范围内提供该服务,因此无需编写任何额外配置即可全局注入使用。

本步练习:为 CarService 添加装饰器

在编辑器中打开 car.service.ts,把 @Service() 装饰器添加到 CarService 类上即可。参考 answer/src/app/car.service.ts,正确的完整代码是:

import {Service} from '@angular/core';

@Service()
export class CarService {
  cars = ['Sunflower GT', 'Flexus Sport', 'Sprout Mach One'];

  getCars(): string[] {
    return this.cars;
  }

  getCar(id: number) {
    return this.cars[id];
  }
}

现在 CarService 已经是可注入(injectable)状态,可以正式参与到 DI 流程中——教程的下一步将把它注入到组件里使用。

@Service 是 @Injectable 的现代化替代

从 Angular 的发展脉络看,@Service 装饰器是传统 @Injectable({providedIn: 'root'}) 写法的现代、更符合直觉的简化形式(shorthand)。它默认就把类提供在 root injector(根注入器)上,省去了每次手写 providedIn: 'root' 的样板。两者的取舍可参考 creating-and-using-services 指南中的对照表:

特性 / 需求 @Service @Injectable
inject() 函数支持
基于构造函数的 DI
隐式的根级单例提供 否(需显式 {providedIn: 'root'}
高级 provider 键(useClass 等)
自定义初始化工厂(factory)
非根作用域(如 platform

何时选哪个?如果你的新服务是一个使用 inject() 获取依赖的单例类,优先 @Service;如果仍需要以下能力,则继续使用 @Injectable

  • 构造函数式依赖注入——@Service 只配合 inject() 函数使用;
  • 高级 provider 配置useClassuseValueuseExistinguseFactory),@Service 只暴露单一的 factory 选项;
  • 非根作用域,例如 providedIn: 'platform'

autoProvided 与 factory 选项

@Service 默认将类提供在根注入器(root injector)。如果需要手动提供(例如要把服务作用域限定到某个路由或组件上),可以设置 autoProvided: false

import {Service} from '@angular/core';

@Service({autoProvided: false})
export class AnalyticsLogger {
  trackEvent(name: string) {
    console.log('event:', name);
  }
}

设置之后,你就要像使用普通 @Injectable() 那样,负责把服务手动加入某处的 providers 数组。此外,@Service 还支持通过 factory 选项自定义单例的创建方式(factory 运行在注入上下文中,内部可以使用 inject() 读取其它依赖),可参考指南中的 Analytics 示例

源码级验证:@Service 在 Angular 内部如何编译

@Service 并不只是一层语法糖,其底层实现可追溯到核心源码 packages/core/src/di/service.ts

packages/core/src/di/service.ts 中,Service 通过 makeDecorator 创建,其元数据类型定义了两个选项:

export interface Service {
  /**
   * Determines whether the service should be provided automatically or by the user.
   * Defaults to `true`.
   */
  autoProvided?: boolean;

  /**
   * A function to invoke to create a value for this service.
   */
  factory?: () => unknown;
}

也就是说:

  • autoProvided 默认为 true(对应教程中"默认全局提供"的行为);
  • factory 用于自定义创建逻辑;
  • 当传入 {autoProvided: false} 时,该服务不再被自动暴露给 DI 系统,交由用户在 providers 中登记。

同时,makeDecorator 还注册了一个 compileService 编译钩子(见 service.ts)。当类声明真正被编译时,JIT 编译器会执行 packages/core/src/di/jit/service.ts 中的 compileService

  • 如果类上还没有 NG_PROV_DEF 定义,就通过编译器编译出 provider 定义 ɵprov,并把 autoProvidedfactory 元数据透传给编译器(见 getServiceMetadata);
  • 如果类上还没有 NG_FACTORY_DEF 定义,则通过 reflectDependencies 反射类的构造函数依赖,编译出工厂函数 ɵfac

换言之,@Service() 装饰后,Angular 会为类生成 ɵprov(provider 定义)与 ɵfac(工厂函数),这正是 DI 系统能够在运行时创建并注入实例的基础。这种"编译期打补丁"的实现也解释了为何服务一旦被 @Service 标记,整个应用的注入器都能感知它。

后续衔接:把服务注入组件(inject())

@Service 让服务变得可注入只是依赖注入的第一步。下一步教程 20-inject-based-di 演示了在组件中通过 inject() 函数获取服务实例的完整形态——在教程练习完成后,App 组件会这样消费 CarService

import {Component, inject} from '@angular/core';
import {CarService} from './car.service';

@Component({
  selector: 'app-root',
  template: '<p> {{ carService.getCars() }} </p>',
})
export class App {
  carService = inject(CarService);
}

从本步到下一步的衔接关系清晰可见:

  1. 本步:CarService@Service() → 类已注册进 DI 体系;
  2. 下步:组件里 inject(CarService) → 拿到同一个实例并使用其方法。

之所以两个文件(src/app/car.service.tssrc/app/app.ts)会被同时打开编辑,也是为了让学习者直观体会到"先创建服务、后注入使用"的完整闭环(练习配置见 config.json)。

关键要点回顾

  • 服务适合封装与数据/API 交互的逻辑,逻辑放在服务里、按需共享给应用各处,即可实现可复用;
  • 给类加上 @Service() 装饰器,即可让 DI 系统在整个应用范围内提供它,无需额外配置;
  • @Service@Injectable({providedIn: 'root'}) 的现代化简写,但只支持 inject(),不支持构造函数注入与高级 provider 键;
  • 需要限定作用域时使用 @Service({autoProvided: false}) 并自行加入 providers;需要自定义创建方式时使用 factory 选项;
  • 从源码(service.tsdi/jit/service.ts)可以看到,@Service 会在编译期为类生成 ɵprovɵfac,这正是"可注入"能力的底层保证;
  • 想深入了解 DI 概念本身,可继续阅读官方指南 dependency-injection essentials 对应章节,以及 di/creating-and-using-services
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388