• 什么是Angular依赖提供者?


    依赖提供者

    笔记参考:https://angular.cn/guide/dependency-injection-providers

    依赖提供者会使用DI令牌(什么是DI令牌?)来配置注入器,注入器会用它来提供这个依赖值的具体的、运行时版本。注入器依靠“提供者配置”来创建依赖的实例,并把该实例注入到组件、指令、管道和其他服务中。

    在下面这个典型的例子中,Logger类自身提供了Logger的实例

    providers: [Logger]

    不过,你也可以用一个替代提供者来配置注入器,这样就可以指定另一些同样能提供日志功能的对象,比如:

    • 你可以提供一个替代类
    • 你可以提供一个类似于Logger的对象
    • 你的提供者可以调用一个工厂函数来创建logger

    Provider对象字面量

    类提供者的语法实际上是一种简写形式,它会扩展成一个由provider接口定义的提供者配置对象。

    providers: [Logger]

    完整的提供者配置对象

    [{ provide: Logger, useClass: Logger }]
    • provide属性存有令牌,它作为一个key,在定位依赖值和配置注入器时使用。
    • 第二个属性是一个提供者定义对象,它告诉注入器要如何创建依赖值。提供者定义对象中的key可以是useClass--就像这个例子中一样。也可以是useExisting useValue或useFactory。每一个key都用于提供一种不同类型的依赖。

    替代类提供者

    不同的类都可用于提供相同的服务。比如,下面的代码告诉注入器,当组件使用Logger令牌请求日志对象时,给它返回一个BetterLogger实例

    [{ provide: Logger, useClass: BetterLogger }]

    带依赖的类提供者

    另一个类EvenBetterLogger可能要在日志信息里显示用户名。这个logger要从注入的UserService实例中来获取该用户。

    @Injectable()
    export class EvenBetterLogger extends Logger {
        constructor(private userService: UserService) { super();}
        
        log(message: string) {
            let name = this.userService.user.name;
            super.log(`Message to ${name}: ${message}`);
        }
    }

    注入器需要提供这个新的日志服务以及该服务所依赖的UserService对象。使用useClass作为提供者定义对象的key,来配置一个logger的替代品,比如BetterLogger。下面的数组同时在父模块和组件的providers元数据选项中指定了这些提供者

    [ UserService,
        { provide: Logger, useClass: EvenBetterLogger }
    ]

    别名类提供者

    假设老的组件依赖于OldLogger类。OldLogger和NewLogger的接口相同,但是由于某种原因,我们没法修改老的组件来使用NewLogger

    当老的组件要使用OldLogger记录信息时,你可能希望改用NewLogger的单例处理它。在这种情况下,无论某个组件请求老的logger还是新的logger,依赖注入器都应该注入这个NewLogger的单例。也就是说OldLogger应该是NewLogger的别名。

    如果你试图用useClass为OldLogger指定一个别名NewLogger, 就会在应用中得到NewLogger的两个不同实例。

    [ NewLogger,
        { provide: OldLogger, useClass: NewLogger }
    ]

    要确保只有一个NewLogger实例,就要用useExisting来为OldLogger指定别名。

    [ NewLogger,
       { provide: OldLogger, useExisting: NewLogger}
    ]

    值提供者

    有时候,提供一个现成的对象会比要求注入器从类去创建更简单一些。如果要注入一个你已经创建过的对象,请使用useValue选项来配置该注入器

    下面的代码定义了一个变量,用来创建这样一个能扮演logger角色的对象。

    function silentLoggerFn() {}
    
    export const SilentLogger = {
        logs: ['Silent logger says "Shhhh!". Provided via "useValue"'],
        log: silentLoggerFn
    }

    下面的提供者定义对象使用useValue作为key来把该变量与Logger令牌关联起来。

    [{ provide: Logger, useValue: SilentLogger }]

    非类依赖

    并非所有的依赖都是类。有时候你会希望注入字符串、函数或对象。

    应用通常会用大量的小型参数来定义配置对象,比如应用的标题或Web API端点的地址。这些配置对象不一定总是类的实例。它们还可能是对象字面量,如下例所示:

    src/app/app.config.ts

    export const HERO_DI_CONFIG: AppConfig = {
        apiEndpoint: 'api.heroes.com',
        title: 'Dependency Injection'
    }

    TypeScript接口不是有效的令牌

    在TypeScript中,接口是一个设计期的概念,无法用做DI框架在运行期所需的令牌。

    // 错误,不能使用接口作为提供者
    [{ provide: AppConfig, useValue: HERO_DI_CONFIG }]
    
    // 错误,不能使用接口作为参数类型
    constructor(private config: AppConfig) {}

    如果你曾经在强类型语言中使用过依赖注入功能,这一点可能看起来有点奇怪,那些语言都优先使用接口作为查找依赖的key。不过,JavaScript没有接口,所以,当TypeScript转译成JavaScript时,接口也就消失了。在运行期间,没有留下任何可供Angular进行查找的接口类型信息。

    替代方案之一是以类似于AppModule的方式,在NgModule中提供并注入这个配置对象。

    src/app/app.module.ts(providers)

    providers: [
        UserService,
        { provide: APP_CONFIG, useValue: HERO_DI_CONFIG}
    ]

    另一个为非类依赖选择提供者令牌的解决方案是定义并使用InjectionToken对象。下面的例子展示了如何定义那样一个令牌。

    import { InjectionToken } from '@angular/core';
    
    export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');

    虽然类型参数在这里是可选的,不过还是能把此依赖的类型信息传达给开发人员和开发工具。这个令牌的描述则是开发人员的另一个助力。

    使用InjectionToken对象注册依赖提供者:

    providers: [{provide: APP_CONFIG, useValue: HERO_DI_CONFIG}]

    现在,借助参数装饰器@Inject(),你可以把这个配置对象注入到任何需要它的构造函数中。

    src/app/app.component.ts

    constructor(@Inject(APP_CONFIG) config: AppConfig) {
        this.title = config.title;
    }

    虽然AppConfig接口在依赖注入时没有任何作用,但它可以为该组件类中的这个配置对象指定类型信息。

    工厂提供者

    有时候你需要动态创建依赖值,创建时需要的信息你要等运行期间才能拿到。比如,你可能需要某个在浏览器会话过程中会被反复修改的信息,而且这个可注入服务还不能独立访问这个信息的源头。

    这种情况,你可以使用工厂提供者。当需要从第三方库创建依赖项实例时,工厂提供者也很有用,因为第三方库不是为DI而设计的。

    比如,假设HeroService必须对普通用户隐藏秘密英雄,只有得到授权的用户才能看到他们。

    而认证信息可能会在应用的单个会话中发生变化,比如你改用另一个用户登录。

    假设你不希望直接把UserService注入到HeroService中,因为你不希望把这个服务与那些高敏感的信息牵扯到一起。这样HeroService就无法直接访问到用户信息,来决定谁有权访问,谁没有。

    要解决这个问题,我们给HeroService的构造函数一个逻辑型标志,以控制是否显示秘密英雄。

    src/app/heroes/hero.service.ts(excerpt)

    constructor(
        private logger: Logger,
        private isAuthorized: boolean
    ) {}
    
    getHeroes() {
        let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
        this.logger.log(`Getting heroes for ${auth} user.`);
        return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
    }

    你可以注入Logger但是不能注入isAuthorized标志。

    不过你可以改用工厂提供者来为HeroService创建一个新的logger实例。

    工厂提供者需要一个工厂函数

    src/app/heroes/hero.service.provider.ts(excerpt)

    let heroServiceFactory = (logger: Logger, userService: UserService) => {
          return new HeroService(logger, userService.user.isAuthorized)  
    }

    虽然HeroService不能访问UserService,但是工厂函数可以。你把Logger和UserService注入到了工厂提供者中,并让注入器把它们传给这个工厂函数。

    export let heroServiceProvider = {
        provide: HeroService,
        useFactory: heroServiceFactory,
        deps: [Logger, UserService]
    }

    useFactory字段告诉Angular该提供者是一个工厂函数,该函数的实现代码是heroServiceFactory

    deps属性是一个提供者令牌数组。Logger和UserService类作为它们自己的类提供者令牌使用。注入器解析这些令牌,并把与之对应的服务注入到相应的工厂函数参数表中。

  • 相关阅读:
    委托和事件的区别和联系(转载
    Captcha验证码识别走向3D化
    3DCaptcha for .net
    委托之异步(转自http://www.cnblogs.com/inforasc/archive/2009/10/21/1587756.html
    static的初始化顺序
    各式各样的验证码
    [SQL优化工具]SQL Tuning for SQL Server(转)
    浅析C#深拷贝与浅拷贝 (转载)
    [orcle] oracle截取字符串的函数substr
    struts2.0的工作原理
  • 原文地址:https://www.cnblogs.com/cathy1024/p/13572661.html
Copyright © 2020-2023  润新知