@celljs/core v3.6.4
Cell - Core Component
概览
Cell 核心组件是一个轻量级的应用框架,用于构建现代化的应用程序。通过依赖注入、AOP 面向切面编程、配置属性、日志等功能,提供了一套简单易用的 API 接口,支持类对象的注册、解析和生命周期管理。Cell 核心组件是基于 InversifyJS 构建的,支持 TypeScript 语言。
特性
- 依赖注入
- 面向切面编程
- 配置属性
- 日志
安装
使用 npm 安装 Cell 核心组件:
npm install @celljs/core或者使用 yarn:
yarn add @celljs/core快速开始
以下是一个简单的依赖注入示例,展示如何使用 Cell 核心组件将一个类对象注入到 IoC 容器中:
import { Component } from '@celljs/core';
@Component()
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}然后,在另一个对象中使用 @Autowired 注解来注入 HelloWorld 类对象:
import { Autowired, ApplicationFactory } from '@celljs/core';
import { HelloWorld } from './hello-world';
export class SampleService {
@Autowired()
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}接着,在 IoC 容器模块中注册 HelloWorld 和 SampleService 类对象:
import { ApplicationFactory } from '@celljs/core';
import { HelloWorld } from './hello-world';
import { SampleService } from './sample-service';
export const SampleModule = autoBind();最后,通过 ApplicationFactory 创建应用实例,加载 IoC 容器模块,组装成一个完整的 IoC 容器:
import { ApplicationFactory } from '@celljs/core';
import { SampleService } from './sample-service';
import { SampleModule } from './sample-module';
(async () => {
const appProps = {};
const app = await ApplicationFactory.create(appProps, SampleModule);
app.start();
})();依赖注入
Cell 核心组件提供了依赖注入功能,支持将类对象注入到 IoC 容器中,并在其他类对象中使用 @Autowired 注解来注入依赖对象。以下是一个简单的依赖注入示例:
import { Component, Autowired } from '@celljs/core';
@Component()
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}
@Component()
export class SampleService {
@Autowired()
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}属性注入
Cell 核心组件支持属性注入功能,通过 @Value 注解可以将配置属性注入到类对象中。以下是一个简单的属性注入示例:
import { Component, Value } from '@celljs/core';
@Component()
export class SampleService {
@Value('app.name')
appName: string;
run() {
console.log(`App Name: ${this.appName}`);
}
}可以被注入的属性是在应用启动时通过 ApplicationFactory 的 create 方法传入的 appProps 对象中定义的。例如:
import { ApplicationFactory } from '@celljs/core';
import { SampleService } from './sample-service';
(async () => {
const appProps = {
app: { name: 'MyApp' }
};
const app = await ApplicationFactory.create(appProps, SampleModule);
app.start();
})();属性注入时,可以使用 . 来访问嵌套属性,例如 @Value('app.name')。
除此之外,@Value 的字符串才是支持 EL 表达式的,底层是基于 jexl 实现的。例如:
import { Component, Value } from '@celljs/core';
@Component()
export class SampleService {
// 对象访问
@Value('app.name')
appName: string;
// 三元表达式
@Value('app.name == "MyApp" ? "Hello, MyApp!" : "Hello, World!"')
greeting: string;
// 提供默认值
@Value('app.version ?: "1.0.0"')
appVersion: string;
// 数学运算
@Value('app.port + 1')
port: number;
// 逻辑运算
@Value('app.debug && app.env == "dev"')
debug: boolean;
// 数组访问
@Value('app.tags[0]')
tag: string;
// 数组过滤
@Value('employees[.age >= 30 && .age < 40]')
middleAgedEmployees: Employee[];
// 变换
// 假如定义如下两个变换:
// jexl.addTransform('split', (val, char) => val.split(char))
// jexl.addTransform('lower', (val) => val.toLowerCase())
@Value('"Pam Poovey"|lower|split(' ')[1]')
lastName: string;
// 函数调用
// 假如定义如下函数:
// jexl.addFunction('min', Math.min)
@Value('min(3, 5)')
min: number;
}IoC 容器
Cell 核心组件提供了一个轻量级的 IoC 容器,用于管理类对象的生命周期和依赖关系。通过 @Component 注解可以将类对象注册到 IoC 容器中,通过 @Autowired 注解可以在其他类对象中注入依赖对象。
IoC 容器底层实现是基于 InversifyJS 的,提供了一套简单易用的 API 接口,支持类对象的注册、解析和生命周期管理。借鉴 Spring Framework 的设计理念,提供了一套类似 Spring 的装饰器,例如 @Component、@Autowired、@Value 等。
IoC 容器是由一些列容器模块组成的,每个容器模块是由一些类对象组成的,通过 @Component 注解注册到容器模块。为了方便注册类对象到容器模块中,Cell 核心组件提供了 autoBind 方法,可以自动注册类对象到容器模块中。autoBind 通过文件模块依赖分析,自动注册类对象到容器模块中,无需手动注册。
以下是一个简单的 IoC 容器示例:
import { Component, Autowired, ApplicationFactory } from '@celljs/core';
@Component()
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}
@Component()
export class SampleService {
@Autowired()
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}
export const SampleModule = autoBind();autoBind 方法还提供一些列的回调函数,用于自定义注册行为。例如:
import { autoBind } from '@celljs/core';
export const SampleModule = autoBind((bind, unbind, isBound, rebind) => {
bind(HelloWorld).toSelf().inSingletonScope();
bind(SampleService).toSelf().inSingletonScope();
});Cell 核心组件还提供了 ContainerFactory,用于创建 IoC 容器实例。例如:
import { ContainerFactory } from '@celljs/core';
import { SampleModule } from './sample-module';
(async () => {
const container = await ContainerFactory.create(SampleModule);
const sampleService = container.get(SampleService);
sampleService.run();
})();除了使用 @Autowired 装饰器来注入依赖对象,还可以使用 ContainerUtil 工具类来手动获取容器中的对象。例如:
import { ContainerUtil } from '@celljs/core';
import { SampleService } from './sample-service';
@Component()
export class SampleService {
run() {
const helloWorld = ContainerUtil.get(HelloWorld);
console.log(this.helloWorld.greet());
}
}注意:ContainerUtil 工具类只能容器加载完后才能使用,否则会报错。
配置属性
Cell 核心组件提供了配置属性功能,支持将配置属性注入到类对象中。通过 @Value 注解可以将配置属性注入到类对象中,支持 EL 表达式。
配置属性是在应用启动时通过 ApplicationFactory 的 create 方法传入的 appProps 对象中定义的。例如:
import { ApplicationFactory } from '@celljs/core';
import { SampleService } from './sample-service';
(async () => {
const appProps = {
app: { name: 'MyApp' }
};
const app = await ApplicationFactory.create(appProps, SampleModule);
app.start();
})();除此之外,还提供了一个 ConfigProvider 接口,用于加载配置文件。Cell 核心组件提供了一个默认实现,开发者也可以自定义实现替换掉默认实现。例如:
import { ConfigProvider } from '@celljs/core';
@Component({ id: ConfigProvider, rebind: true })
export class MyConfigProvider implements ConfigProvider {
get<T>(key: string, defaultValue?: T): T {
return process.env[key] || defaultValue;
}
}除了前面介绍的使用 @Value 注解来注入配置属性,还可以使用 ConfigUtil 工具类来手动获取配置属性。例如:
import { ConfigUtil } from '@celljs/core';
import { SampleService } from './sample-service';
@Component()
export class SampleService {
run() {
const appName = ConfigUtil.get('app.name');
console.log(`App Name: ${appName}`);
}
}装饰器
Cell 核心组件提供了一套类似 Spring 的装饰器,用于注册类对象到 IoC 容器中。以下是一些常用的装饰器:
@Component:注册类对象到 IoC 容器中。@Autowired:注入依赖对象。@Value:注入配置属性。@PostConstruct:在类对象初始化后执行。@PreDestroy:在类对象销毁前执行。@Service:注册服务对象到 IoC 容器中。是@Component的别名。@Inject:注入依赖对象。是InversifyJS的inject的别名。@Injectable:标识类对象是可注入的。是InversifyJS的injectable的别名。@Named:指定注入对象的名称。是InversifyJS的named的别名。@Optional:标识注入对象是可选的。是InversifyJS的optional的别名。@Tagged:标识注入对象是标记的。是InversifyJS的tagged的别名。@TargetName:指定注入对象的名称。是InversifyJS的targetName的别名。@Unmanaged:标识类对象是不受管理的。是InversifyJS的unmanaged的别名。@Aspect:定义切面对象。提供 AOP 编程的能力。@AutowiredProvider:注入依赖提供者,通过依赖提供者对象,获取一类对象列表,支持排序能力。@Constant:注册常量对象到 IoC 容器中。@Decorate:装饰类对象。是InversifyJS的decorate的别名。
除此之外,还提供了一些装饰器,用于在非托管类对象中使用(当然,也可以直接使用 ContainerUtil 工具类来手动获取容器中的对象),例如在前端的 React 类组件中使用。以下是一些常用的非托管类对象的装饰器:
@Autowired:注入依赖对象。@Value:注入配置属性。@AutowiredProvider:注入依赖提供者,通过依赖提供者对象,获取一类对象列表,支持排序能力。
例如:
import { Autowired, Value } from '@celljs/core/lib/common/annotation/detached';
export class SampleComponent extends React.Component {
@Autowired()
sampleService: SampleService;
@Value('app.name')
appName: string;
render() {
return <div>{this.appName}</div>;
}
}@Component 装饰器
@Component 装饰器用于注册类对象到 IoC 容器中。该装饰器有以下属性:
id:类对象的标识符,可以是一个或多个。scope:类对象的作用域,默认是Scope.Singleton。name:类对象的名称。tag:类对象的标签。default:是否是默认的类对象。当给定的服务标识符有多个绑定可用时,可以通过设置default属性来指定默认的类对象。当通过服务标识符获取单个对象时,则返回默认对象。when:一个函数,用于判断是否应该创建该类对象。如果返回true,则创建该类对象;否则不创建。rebind:是否重新绑定类对象。默认是false。proxy:是否使用代理类对象。默认是false。当为true时,可以被 AOP 切面拦截。sysTags:系统标签,用于标识类对象。在 AOP 切面中,可以通过系统标签来拦截类对象。onActivation:在类对象激活时执行的回调函数。
例如:
import { Component, Scope } from '@celljs/core';
@Component({ id: 'HelloWorld', scope: Scope.Singleton })
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}当 id 属性是一个数组时,表示一个类对象有多个标识符。例如:
import { Component } from '@celljs/core';
@Component({ id: ['HelloWorld', 'Hello'] })
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}当不设置 id 属性时,则默认使用类作为标识符。例如:
import { Component } from '@celljs/core';
@Component()
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}无论有没有设置 id 属性,装饰器所在的类永远都是一个标识符,都可以通过该类获取到类对象。例如:
import { Component } from '@celljs/core';
@Component()
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}
@Component()
export class SampleService {
@Autowired()
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}@Autowired 装饰器
@Autowired 装饰器用于注入依赖对象。该装饰器有以下属性:
id:类对象的标识符。multi:是否是多个依赖对象。默认是根据属性类型是否为数组,自动判断,在某些情况下,需要强制指定为true。detached:是否是非托管类对象。默认是false。
例如:
import { Autowired } from '@celljs/core';
@Component()
export class SampleService {
@Autowired({ id: HelloWorld })
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}当不设置 id 属性时,则默认使用属性类型作为标识符。例如:
import { Autowired } from '@celljs/core';
@Component()
export class SampleService {
@Autowired()
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}当一个标识在容器中存在多个对象时,可以把属性类型定义为数组,表示是多个依赖对象。例如:
import { Autowired } from '@celljs/core';
@Component()
export class SampleService {
@Autowired()
helloWorlds: HelloWorld[];
run() {
console.log(this.helloWorlds.map(helloWorld => helloWorld.greet()));
}
}当一个标识在容器中存在多个对象时,也可以通过 @AutowiredProvider 装饰器来注入依赖提供者对象,通过依赖提供者对象,获取一类对象列表,支持排序能力。例如:
import { AutowiredProvider } from '@celljs/core';
@Component()
export class SampleService {
@AutowiredProvider({ id: HelloWorld })
helloWorldProvider: Provider<HelloWorld>;
run() {
const helloWorlds = this.helloWorldProvider.sortSync();
console.log(helloWorlds.map(helloWorld => helloWorld.greet()));
}
}在使用 esbuild 编译的项目中,由于编译后的代码中没有类型信息,所以无法自动判断是否是多个依赖对象,需要强制指定为 true, 或者使用 @AutowiredProvider 装饰器。
@Value 装饰器
@Value 装饰器用于注入配置属性。该装饰器有以下属性:
el:配置属性的 EL 表达式。detached:是否是非托管类对象。默认是false。
例如:
import { Value } from '@celljs/core';
@Component()
export class SampleService {
@Value('app.name')
appName: string;
run() {
console.log(`App Name: ${this.appName}`);
}
}@Constant 装饰器
@Constant 装饰器用于注册常量对象到 IoC 容器中。该装饰器有以下属性:
id:常量对象的标识符。constantValue:常量对象的值。rebind:是否重新绑定常量对象。默认是false。
例如:
import { Constant } from '@celljs/core';
@Constant('app.name', 'MyApp')
@Constant('app.version', '1.0.0')
export default class {
}
@Component()
export class SampleService {
@Autowired('app.name')
name: string;
@Autowired('app.version')
version: string;@PostConstruct 装饰器
@PostConstruct 装饰器用于在类对象初始化后执行。例如:
import { Component, PostConstruct } from '@celljs/core';
@Component()
export class SampleService {
@PostConstruct()
init() {
console.log('SampleService initialized.');
}
}@PreDestroy 装饰器
@PreDestroy 装饰器用于在类对象销毁前执行。例如:
import { Component, PreDestroy } from '@celljs/core';
@Component()
export class SampleService {
@PreDestroy()
destroy() {
console.log('SampleService destroyed.');
}
}@Named 装饰器
@Named 装饰器用于指定注入对象的名称。例如:
import { Autowired, Named } from '@celljs/core';
@component({ name: 'HelloWorld' })
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}
@Component()
export class SampleService {
@Autowired()
@Named('HelloWorld')
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}@Tagged 装饰器
@Tagged 装饰器用于标识注入对象是标记的。例如:
tag?: { tag: string | number | symbol, value: any };import { Autowired, Tagged } from '@celljs/core';
@Component({ tag: { tag: 'tag1', value: 'HelloWorld' } })
export class HelloWorld {
greet() {
return 'Hello, World!';
}
}
@Component()
export class SampleService {
@Autowired()
@Tagged('tag1', 'HelloWorld')
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld.greet());
}
}@Optional 装饰器
@Optional 装饰器用于标识注入对象是可选的。当类属性添加了一个 @Autowired 注解时,如果没有找到对应的类对象,会抛出一个异常。如果添加了一个 @Optional 注解,当没有找到对应的类对象时,会设置属性值为 undefined。例如:
import { Autowired, Optional } from '@celljs/core';
@Component()
export class SampleService {
@Autowired()
@Optional()
helloWorld: HelloWorld;
run() {
console.log(this.helloWorld?.greet());
}
}@Aspect 装饰器
@Aspect 装饰器用于定义切面对象。提供 AOP 编程的能力。继承了 @Component 装饰器的所有属性。再此之上,添加了 pointcut 属性,用于指定切点。该装饰器有以下属性:
id:类对象的标识符。相较于@Component装饰器,id属性不是数组,只能是一个标识符。pointcut:切点。默认是COMPONENT_TAG。表示拦截所以使用@Component装饰器注册的类对象。也可以自定义切点,例如AOP_TAG。
例如:
import { MethodBeforeAdvice, Autowired, Aspect, AfterReturningAdvice, Value, ConfigUtil, Injectable } from '@celljs/core';
import { AccessDecisionManager, MethodSecurityMetadataContext, ResourceNameResolver, SecurityMetadataSource, SECURITY_EXPRESSION_CONTEXT_KEY } from './access-protocol';
import { AOP_POINTCUT } from '@celljs/web';
import { SecurityContext } from '../context';
import { AuthorizeType } from '../../common';
import { Context } from '@celljs/web/lib/node';
const pointcut = ConfigUtil.getRaw().cell.security.aop?.pointcut || AOP_POINTCUT;
@Injectable()
export abstract class AbstractSecurityMethodAdivice {
@Autowired(AccessDecisionManager)
protected readonly accessDecisionManager: AccessDecisionManager;
@Autowired(SecurityMetadataSource)
protected readonly securityMetadataSource: SecurityMetadataSource;
@Autowired(ResourceNameResolver)
protected readonly resourceNameResolver: ResourceNameResolver;
@Value('cell.security.enabled')
protected readonly enabled: boolean;
protected needAccessDecision(method: string | number | symbol) {
return this.enabled && typeof method === 'string' && SecurityContext.getCurrent();
}
}
@Aspect({ id: MethodBeforeAdvice, pointcut })
export class SecurityMethodBeforeAdivice extends AbstractSecurityMethodAdivice implements MethodBeforeAdvice {
async before(method: string | number | symbol, args: any[], target: any): Promise<void> {
if (this.needAccessDecision(method)) {
const ctx: MethodSecurityMetadataContext = { method: method as string, args, target, authorizeType: AuthorizeType.Pre, grant: 0 };
const securityMetadata = await this.securityMetadataSource.load(ctx);
const resouces = await this.resourceNameResolver.resolve(ctx);
for (const resource of resouces) {
securityMetadata.resource = resource;
await this.accessDecisionManager.decide(securityMetadata);
}
}
}
}
@Aspect({ id: AfterReturningAdvice, pointcut })
export class SecurityAfterReturningAdvice extends AbstractSecurityMethodAdivice implements AfterReturningAdvice {
async afterReturning(returnValue: any, method: string | number | symbol, args: any[], target: any): Promise<void> {
if (this.needAccessDecision(method)) {
const oldCtx = Context.getAttr<MethodSecurityMetadataContext>(SECURITY_EXPRESSION_CONTEXT_KEY);
const newCtx = { method: method as string, args, target, returnValue, authorizeType: AuthorizeType.Post, grant: oldCtx.grant };
const securityMetadata = await this.securityMetadataSource.load(newCtx);
const resouces = await this.resourceNameResolver.resolve(newCtx);
for (const resource of resouces) {
securityMetadata.resource = resource;
await this.accessDecisionManager.decide(securityMetadata);
}
}
}
}其他装饰器
其他非常用的装饰器,可以参考 InversifyJS。
AOP 面向切面编程
Cell 核心组件提供了 AOP 面向切面编程的能力,支持在类对象的方法执行前、执行后、执行异常时执行一些操作。通过 @Aspect 装饰器可以定义切面对象,通过 MethodBeforeAdvice、AfterReturningAdvice、AfterThrowsAdvice 接口可以定义通知对象。
AOP 能力涉及一下接口:
MethodBeforeAdvice:在方法执行前执行。AfterReturningAdvice:在方法执行后执行。AfterThrowsAdvice:在方法执行异常时执行。
可以使用 @Aspect 装饰器定义切面对象,通过 pointcut 属性指定切点。例如:
import { MethodBeforeAdvice, Autowired, Aspect, AfterReturningAdvice, Value, ConfigUtil, Injectable } from '@celljs/core';
const pointcut = 'test';
@Component({ id: MethodBeforeAdvice, pointcut })
export class TestMethodBeforeAdvice implements MethodBeforeAdvice {
async before(method: string | number | symbol, args: any[], target: any): Promise<void> {
console.log('before');
}
}
@Component({ id: AfterReturningAdvice, pointcut })
export class TestAfterReturningAdvice implements AfterReturningAdvice {
async afterReturning(returnValue: any, method: string | number | symbol, args: any[], target: any): Promise<void> {
console.log('after');
}
}
@Component({ id: AfterThrowsAdvice, pointcut })
export class TestAfterThrowsAdvice implements AfterThrowsAdvice {
async afterThrows(error: any, method: string | number | symbol, args: any[], target: any): Promise<void> {
console.log('throws');
}
}上面的示例表示定义了一个切面对象,通过 pointcut 属性指定切点为 test。当一个类对象的方法执行前、执行后、执行异常时,会执行相应的操作。其中,切点为 test,表示拦截所有使用 @Component 装饰器注册的类对象,且 sysTag 为 test。
搭建应用
Cell 核心组件提供了一个轻量级的应用框架,用于搭建应用程序。通过 Application 接口可以定义应用程序,通过 ApplicationLifecycle 接口可以定义应用程序的生命周期。通过 ApplicationLifecycle 接口可以定义应用程序的生命周期。通过 ApplicationStateService 接口可以定义应用程序的状态管理。通过 ApplicationFactory 可以创建应用实例。
一般使用 ApplicationFactory 创建应用实例,加载 IoC 容器模块,组装成一个完整的 IoC 容器。例如:
import { ApplicationFactory } from '@celljs/core';
import { SampleService } from './sample-service';
import { SampleModule } from './sample-module';
(async () => {
const appProps = {
app: { name: 'MyApp' }
};
const app = await ApplicationFactory.create(appProps, SampleModule);
app.start();
})();在应用程序中,可以通过 ApplicationLifecycle 接口定义应用程序的生命周期。例如:
import { ApplicationLifecycle, Application } from '@celljs/core';
export class SampleLifecycle implements ApplicationLifecycle {
onStart(app: Application): Promise<void> {
return Promise.resolve();
}
onStop(app: Application): void {
console.log('Application stopped.');
}
}另外,在 browser 和 node 目录下,提供了 Application 的实现,可以直接使用。
目录结构说明
.
├── browser # 浏览器端应用
│ ├── application # 前端应用程序
│ ├── browser.ts # 与浏览器相关的工具方法
│ ├── index.ts # 导出模块
│ ├── shell # 前端应用程序启动入口
│ └── static-module.ts # 静态加载类型的 IoC 容器模块
├── common # 公共模块
│ ├── annotation # 装饰器定义目录
│ ├── aop # AOP 相关
│ ├── application # 后端应用程序
│ ├── config # 配置属性
│ ├── constants.ts # 常量定义
│ ├── container # IoC 容器
│ ├── el # EL 表达式
│ ├── error # 错误处理
│ ├── index.ts # 导出模块
│ ├── logger # 日志
│ ├── provider # 依赖提供者
│ ├── static-module.ts # 静态加载类型的 IoC 容器模块
│ ├── test # 测试
│ └── utils # 工具方法
├── node # Node.js 端应用
│ ├── application # 后端应用程序
│ ├── constants.ts # 常量定义
│ ├── context # 上下文
│ ├── index.ts # 导出模块
│ ├── static-module.ts # 静态加载类型的 IoC 容器模块
│ └── utils # 工具方法
└── package.spec.ts # 单元测试错误类型
Cell 核心组件提供了一些常用的错误类型,用于处理异常情况。
CustomError:自定义错误类型。IllegalArgumentError:非法参数错误。IllegalStateError:非法状态错误。InvalidMimeTypeError:无效的 MIME 类型错误。
例如:
import { IllegalArgumentError } from '@celljs/core';
if (condition) {
throw new IllegalArgumentError('Invalid argument.');
}由于 JS 的运行时提供的错误类型无法通过 instanceof 运算符来判断,所以 Cell 核心组件提供了 CustomError 基类,用于自定义错误类型。所以建议自定义错误类型都继承自 CustomError 基类。
日志
Cell 核心组件提供了日志功能,支持日志级别、日志格式、日志输出等功能。通过 Logger 接口可以定义日志对象,通过 TraceIdProvider 接口可以定义跟踪 ID 提供者。
日志级别有以下几种:
verbose:详细信息。debug:调试信息。info:信息。warn:警告。error:错误。
例如:
import { Logger } from '@celljs/core';
@Component()
export class SampleService {
@Autowired(Logger)
logger: Logger;
run() {
this.logger.info('Info message.');
this.logger.error('Error message.');
}
}其中,默认提供的日志输出为 console,为了支持不同类对象,注入的日志对象都是互相独立的,可以通过 setContext 方法设置上下文信息。例如:
import { Logger } from '@celljs/core';
@Component()
export class SampleService {
@Autowired(Logger)
logger: Logger;
run() {
this.logger.setContext('SampleService');
this.logger.info('Info message.');
this.logger.error('Error message.');
}
}开发者也可以根据自己的需求,自定义 Logger 接口的实现,替换掉默认的实现。例如:
import { Component } from '@celljs/core';
@Component({ id: Logger, rebind: true })
export class MyLogger implements Logger {
// TODO
}上下文能力
Cell 核心组件提供了上下文能力,支持应用程序上下文、请求上下文、会话上下文等。通过 Context 类可以设置和获取上下文信息。
上下文信息有以下几种:
App:应用程序上下文。Request:请求上下文。Session:会话上下文。
import { Context, AttributeScope } from '@celljs/core';
Context.setAttr('key', 'value', AttributeScope.Request);
const value = Context.getAttr('key', AttributeScope.Request);上下文运行可以保证上下文信息在函数执行期间有效。例如:
import { Context } from '@celljs/core';
Context.run(async () => {
Context.setAttr('key', 'value1', AttributeScope.Request);
const value = Context.getAttr('key', AttributeScope.Request);
console.log(value); // value1
});
Context.run(() => {
Context.setAttr('key', 'value2', AttributeScope.Request);
const value = Context.getAttr('key', AttributeScope.Request);
console.log(value); // value2
});常用工具类和方法
Cell 核心组件提供了一些常用的工具类和方法,用于处理一些常见的问题。
AnnotationUtil:注解工具类。Assert:断言工具类。Async:异步工具类。Cancellation:取消工具类。ClassUtil:类工具类。Disposable:可释放工具类。Emitter:事件工具类。Event:事件工具类。GlobalUtil:全局工具类。MetadataUtil:元数据工具类。Prioritizeable:优先级工具类。PromiseUtil:Promise 工具类。ProxyUtil:代理工具类。Types:类型工具类。UrlUtil:URL 工具类。generateUUUID:生成 UUID。OS:操作系统工具类。MimeTypeUtil:MIME 类型工具类。MimeType:MIME 类型。
例如:
import { Assert } from '@celljs/core';
Assert.isTrue(condition, 'Invalid argument.');EL 表达式
Cell 核心组件支持 EL 表达式,EL 表达式是一种简单、易读的模板语言,可以用于在模板中动态生成内容。通过 ExpressionCompiler 接口可以编译 EL 表达式,通过 ExpressionHandler 接口可以处理 EL 表达式。
有以下核心接口:
ExpressionCompiler:编译 EL 表达式。ExpressionHandler:处理 EL 表达式。ExpressionContext:EL 表达式上下文。ContextInitializer:上下文初始化器。ExpressionContextProvider:EL 表达式上下文提供者。JexlEngineProvider:Jexl 引擎提供者。
例如:
import { ExpressionHandler } from '@celljs/core';
@Component()
export class SampleService {
@Autowired(ExpressionHandler)
expressionHandler: ExpressionHandler;
run() {
const result = this.expressionHandler.handle('Hello, ${name}!', { name: 'World' });
console.log(result); // Hello, World!
}
}可以为 EL 表达式添加新的上下文变量、函数、变换器等。例如:
import { ContextInitializer } from '@celljs/core';
@Component(ContextInitializer)
export class CoreContextInitializer implements ContextInitializer {
@Autowired(JexlEngineProvider)
protected readonly jexlEngineProvider: JexlEngineProvider<any>;
initialize(ctx: ExpressionContext): void {
if (typeof process !== 'undefined') {
ctx.env = { ...process.env, _ignoreEl: true };
}
const jexlEngine = this.jexlEngineProvider.provide();
jexlEngine.addTransform('replace',
(val: string, searchValue: string | RegExp, replaceValue: string) => val && val.replace(new RegExp(searchValue, 'g'), replaceValue));
jexlEngine.addTransform('regexp', (pattern: string, flags?: string) => new RegExp(pattern, flags));
const expressionHandler = ContainerUtil.get<ExpressionHandler>(ExpressionHandler);
jexlEngine.addTransform('eval', (text: string) => expressionHandler.handle(text));
}
priority = 500;
}应用的属性配置默认支持 EL 表达式模版字符串,例如:
import { ApplicationFactory } from '@celljs/core';
(async () => {
const appProps = {
app: { name: 'MyApp' },
env: '${env}',
version: '${version}',
appVersion: '${app.name}-${version}',
appVersion2: '${app.name}-${version}-${env}',
};
const app = await ApplicationFactory.create(appProps, SampleModule);
app.start();
})();许可证
本项目采用 MIT 许可证。
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
5 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
10 months ago
10 months ago
11 months ago
6 months ago
7 months ago
7 months ago
6 months ago
7 months ago
8 months ago
6 months ago
7 months ago
10 months ago
10 months ago
1 year ago
1 year ago
1 year ago