Angular Injector的实现初解
在Angular的开发实践中,我们大量依赖Angular自带的@Injectable
注解,以及provide
装饰器,来管理依赖。
在Angular组件实现中或通过构造函数自动注入,或通过inject方法主动注入,那么Angular是如何实现依赖注入的呢?
接下来我们将主要分析各个provider的注册过程,已及inject时寻找provider的过程。
Injector
Injector是provider的容器,其实现类为R3Injector
、NodeInjector
、ChainedInjector
, 继承了Injector
类,并实现了get
方法。
注意:Injector类似Zone.js,具有链式结构,get方法通常会逐层向上查找所需provider。
但Injector的实现并不依赖于Zone.js。
provider的注册过程
1. boostrap
项目bootstrap的阶段,将会创建platformInjector
与applicationInjector
,调用路径bootstrapApplication
-> internalCreateApplication
。
其中platformInjector是Injector
类型,而applicationInjector是R3Injector
类型。applicationInjector的parent为platformInjector。
export function internalCreateApplication(config: {
rootComponent?: Type<unknown>,
appProviders?: Array<Provider|EnvironmentProviders>,
platformProviders?: Provider[],
}): Promise<ApplicationRef> {
try {
const {rootComponent, appProviders, platformProviders} = config;
if ((typeof ngDevMode === 'undefined' || ngDevMode) && rootComponent !== undefined) {
assertStandaloneComponentType(rootComponent);
}
const platformInjector = createOrReusePlatformInjector(platformProviders as StaticProvider[]);
// Create root application injector based on a set of providers configured at the platform
// bootstrap level as well as providers passed to the bootstrap call by a user.
const allAppProviders = [
provideZoneChangeDetection(),
...(appProviders || []),
];
const adapter = new EnvironmentNgModuleRefAdapter({
providers: allAppProviders,
parent: platformInjector as EnvironmentInjector,
debugName: (typeof ngDevMode === 'undefined' || ngDevMode) ? 'Environment Injector' : '',
// We skip environment initializers because we need to run them inside the NgZone, which
// happens after we get the NgZone instance from the Injector.
runEnvironmentInitializers: false,
});
const envInjector = adapter.injector;
const ngZone = envInjector.get(NgZone);
// ...
} catch (e) {
return Promise.reject(e);
}
}
createModule
创建module时将同时创建相应的R3Injector,此时同时收集provider。
createComponent
创建component将同时创建ChainedInjector与,NodeInjector。请参考源码 ComponentFactory.get
实现。
provider的查找过程
如果想要注入provider,有两点很重要,查找到当前的injector,以及通过injector逐层查找对应的provider。
以下以inject方法为例。
其请求路径 inject
-> ɵɵinject
-> getInjectImplementation
| injectInjectorOnly
-> XXXInjector.get
export function ɵɵinject<T>(token: ProviderToken<T>, flags = InjectFlags.Default): T|null {
return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
}
getInjectImplementation
| injectInjectorOnly
获取当前injector。
在获取到injector后调用重写的get方法以在不同Injector中以特定的逻辑获取provider。
对于R3Injector,调用get时将会重新设定currentInjectImplementation和currentInjector。
在组件生成阶段,框架会注入context,参考Inject context。不过这部分还未找到源码验证。
/**
* Current implementation of inject.
*
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
* way for two reasons:
* 1. `Injector` should not depend on ivy logic.
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
*/
let _injectImplementation: (<T>(token: ProviderToken<T>, flags?: InjectFlags) => T | null)|
undefined;
export function getInjectImplementation() {
return _injectImplementation;
}
export function injectInjectorOnly<T>(token: ProviderToken<T>): T;
export function injectInjectorOnly<T>(token: ProviderToken<T>, flags?: InjectFlags): T|null;
export function injectInjectorOnly<T>(token: ProviderToken<T>, flags = InjectFlags.Default): T|
null {
if (_currentInjector === undefined) {
throw new RuntimeError(
RuntimeErrorCode.MISSING_INJECTION_CONTEXT,
ngDevMode &&
`inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`EnvironmentInjector#runInContext\`.`);
} else if (_currentInjector === null) {
return injectRootLimpMode(token, undefined, flags);
} else {
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
}
}