
后端微服务【免费下载链接】loopback-nextLoopBack makes it easy to build modern API applications that require complex integrations.项目地址https://gitcode.com/gh_mirrors/lo/loopback-next点击查看免费下载导读本文聚焦 LoopBack 4loopback-next仓库的可扩展性设计从loopback/context的 IoC 容器与 Context 绑定、依赖注入到「扩展点 扩展」模型、Component打包机制再到系统级与应用级扩展的区分以及用lb4 extension脚手架从零搭建自定义扩展的完整实操路径。读完本文你将掌握 LoopBack 4 插件化架构的底层原理并能够独立开发、注册和发布自己的 LoopBack 4 扩展组件。为什么 LoopBack 4 把「可扩展」当作第一设计目标LoopBack 4 从设计之初就定位为「高度可扩展」highly extensible的框架。其架构取舍的完整理由记录在 Crafting LoopBack 4 中——这份文档阐述了框架从 LoopBack 3 演进时的核心理念不是把功能硬编码进框架内核而是让框架成为一个可以随时被注入新能力的开放平台。这一理念的直接体现是几乎所有框架级能力认证、授权、健康检查、日志、指标、CRUD 路由等在仓库中都不是写死在核心包里的而是以独立 npm 包 Component的形式存在。例如loopback/authentication、loopback/authorization、loopback/rest、loopback/boot等官方组件以及extensions/目录下的cron、health、logging、metrics、sequelize、typeorm等扩展完整清单见 Component.md。这种核心最小化、能力外置化的架构正是扩展性落地的具体形态。扩展性的构建基石loopback/context 与 IoC 容器扩展性并非凭空而来它建立在两个基础设施之上loopback/context模块实现了一个名为Context的 IoCInversion of Control控制反转容器充当服务注册表并原生支持依赖注入Dependency Injection。loopback/core包重新导出loopback/context的全部公开 API。LoopBack 官方建议在 LoopBack 模块和应用中统一从loopback/core导入除非确实需要显式依赖loopback/context。下面两行是等价的import {inject} from loopback/context; import {inject} from loopback/core;IoC 容器的核心作用是解耦服务的提供者与消费者一个服务提供者可以通过一个 key 绑定bind到 Context 上这个 key 相当于服务提供者的地址。消费者不需要知道提供者的具体类实现只需通过 key 定位或注入即可。Context 如何管理服务与依赖以上面的架构图为例Context 中注册了三个服务各自绑定到唯一的 keycontrollers.UserController实现用户管理 API 的控制器repositories.UserRepository为用户记录提供持久化的仓库utilities.PasswordHasher对密码进行哈希的工具函数。注意UserController依赖UserRepository与PasswordHasher的实例——这种依赖关系同样由 Context 管理从而为服务实例提供组合composition能力。通过 inject 声明依赖服务消费者有两种方式获取服务用绑定 key 在 Context 中查找locate或通过inject(binding-key-of-a-service-provider)装饰器声明依赖让容器把服务提供者注入到消费类中。下面的代码展示了inject的典型用法import {inject, Context} from loopback/core; /** * 一个依赖 UserRepository 与 PasswordHasher 的 UserController 实现 */ class UserController { // UserRepository 和 PasswordHasher 通过构造函数注入 constructor( inject(repositories.UserRepository) private userRepository: UserRepository, inject(utilities.PasswordHasher) private passwordHasher: PasswordHasher, ) {} /** * 用用户名和密码登录 */ async login(userName: string, password: string): Promiseboolean { const hash this.passwordHasher.hash(password); const user await this.userRepository.findById(userName); return user user.passwordHash hash; } } const ctx new Context(); // 把 repositories.UserRepository 绑定到 UserRepository 类 ctx.bind(repositories.UserRepository).toClass(MySQLUserRepository); // 把 utilities.PasswordHash 绑定到一个函数 ctx.bind(utilities.PasswordHash).to(PasswordHasher); // 把 UserController 类绑定为用户管理实现 ctx.bind(controllers.UserController).toClass(UserController); // 从 Context 中定位 UserController 实例 const userController: UserController await ctx.getUserController(controllers.UserController); // 执行 login() const ok await userController.login(John, MyPassWord);这段代码还展示了绑定Binding的三种基本形态它们在后续组件开发中会频繁使用绑定方式说明ctx.bind(key).to(value)绑定一个固定值或函数ctx.bind(key).toClass(Class)绑定一个类按需实例化默认每次解析新建实例可通过BindingScope.SINGLETON等作用域调整ctx.bind(key).toProvider(ProviderClass)绑定一个 Provider 类用于在解析时执行自定义逻辑后再返回值关于 Context、Binding 与依赖注入的更多细节可进一步阅读 Context.md 与 Dependency-injection.md。为什么 IoC 容器是扩展性的根基了解 IoC 之后一个自然的问题是为什么 IoC 容器对扩展性如此关键文档给出了两条核心机制1. 替换式扩展重新绑定即可换实现服务提供者的替代实现可以绑定到同一 Context 来替换现有实现。例如密码加密场景我们可以实现不同的哈希函数如 argon2、bcrypt、scrypt用户管理系统无需改动一行业务代码就能接收自定义的密码哈希函数。对扩展开发而言这意味着**覆盖绑定就是最基本的扩展动作**。2. 结构化扩展扩展点 扩展Extension Point Extension服务可以被组织成**扩展点extension point与扩展extension**的关系。以认证为例authentication组件定义一个名为authentication-manager的扩展点而各种认证策略——用户名/密码、LDAP、oAuth2 等——以扩展的身份被贡献到该扩展点上。其关系如下图所示这种模式的落地离不开loopback/context的两类底层设施ContextView见 packages/context/src/context-view.tsContextView在注释中即被明确定义为实现扩展点动态扩展的关键工具。它基于一个BindingFilter维护 Context 中所有匹配绑定的活视图并缓存已解析的值当 Context 发生bind/unbind事件时自动刷新缓存并发出bind、unbind、refresh、resolve、close事件。因此即使应用启动后再动态添加或移除扩展例如动态注册新的认证策略扩展点也能实时感知变化。inject.tag按标签注入inject.tag(authentication.strategies)之类扩展点侧通过标签过滤拿到全部扩展的 Getter扩展侧通过bind.tagged或绑定时的tag选项把自身标记为某扩展点的扩展。Component把扩展打包分发的基本单元为了让一批扩展能被整体贡献给 LoopBack 框架和应用LoopBack 4 引入Component作为打包模型一个 Component 既可以是一个 npm 模块也可以是本地文件夹结构内部包含一个或多个扩展最终以实现了Component接口的类形式对外导出。import {Component, ProviderMap} from loopback/core; export class UserManagementComponent implements Component { providers?: ProviderMap; constructor() { this.controllers [UserController]; this.repositories [UserRepository]; } }应用上下文与UserManagement组件的交互过程见下图组件被app.component()挂载后其承载的 controllers、providers、servers、bindings 等工件被逐一登记到应用 Context 中。Component 接口的完整工件清单结合源码 packages/core/src/component.ts 中Component接口的定义一个组件可以携带以下工件属性类型说明controllersControllerClass[]控制器类数组注册为 REST 端点providersProviderMapProvider 类映射按 key 绑定到应用 Context如{authentication.strategies.ldap: LdapStrategyProvider}classesClassMap普通类映射按 key 绑定如{rest.body-parsers.xml: XmlBodyParser}servers{name: ServerClass}服务器名称/类映射lifeCycleObserversLifeCycleObserver[]生命周期观察者数组servicesServiceOrProviderClass[]服务类或 Provider 类数组bindingsBinding[]预构建的 Binding 对象数组例如Binding.bind(x).to(Value X)componentsComponent[]被当前组件依赖并自动挂载的子组件一个可复制的完整示例来自 Creating-components.mdimport {MyController} from ./controllers/my.controller; import {MyValueProvider} from ./providers/my-value.provider; import {Binding, Component} from loopback/core; export class MyComponent implements Component { servers { my-server: MyServer, }; lifeCycleObservers [MyObserver]; controllers [MyController]; providers { my-value: MyValueProvider, }; classes { my-validator: MyValidator, }; constructor() { // 通过 bindings 属性贡献自定义 Binding const bindingX Binding.bind(x).to(Value X); const bindingY Binding.bind(y).toClass(ClassY); this.bindings [bindingX, bindingY]; } }组件挂载的源码级流程在 packages/core/src/application.ts 中app.component()的完整流程是用createBindingFromClass为组件类创建绑定放入components命名空间打上CoreTags.COMPONENT标签默认作用域为SINGLETON若组件类实现了LifeCycleObserver则额外应用asLifeCycleObserver标记使其能被应用的生命周期管理捕获同步实例化组件this.getSync调用mountComponent(this, instance)把组件携带的所有工件注册进应用。而mountComponent见 packages/core/src/component.ts逐项处理classes、providers、bindings、controllers、servers、lifeCycleObservers、services、components每个 Controller 走app.controller()每个 Server 走app.server()每个 Provider/Class 通过createBindingFromClass按 key 绑定子组件则递归调用app.component()并跳过自身以避免循环挂载。注册与使用组件在应用中使用组件只需调用app.component()import {RestApplication} from loopback/rest; import {AuthenticationComponent} from loopback/authentication; const app new RestApplication(); // 将组件加入应用它提供的绑定用于在 Sequence 中解析已认证请求 app.component(AuthenticationComponent);安装依赖npm install --save loopback/authentication注意一个组件是否要求宿主应用启用某个 Mixin应以该组件的说明为准。例如loopback/repository的RepositoryMixin允许组件贡献 Repositoriesloopback/boot的BootMixin允许组件贡献 Booters。有些 Mixin 会自动注册对应组件无需手动调用app.component()。更完整的清单见 Component.md。扩展的类型System Extensions 与 Application ExtensionsLoopBack 4 官方将扩展分为两大阵营理解这一划分有助于你决定该写什么类型的扩展。系统级扩展System Extensions扩展框架本身的编程模型与集成能力。官方列举的典型例子Binding providers绑定提供者Decorators装饰器如get、injectSequence 与 ActionsREST 处理链及其中间动作Connectors连接器如各类数据库连接器Utility functions工具函数Mixins用于 Application 的混入这类扩展通常由框架/平台维护者开发目标是增强 LoopBack 4 本身的能力面。仓库中packages/目录下的大多数包都属于这一类。应用级扩展Application Extensions承载具体业务逻辑的组件集合。例如一个典型的在线购物应用通常由以下组件构成UserManagement用户管理ShoppingCart购物车AddressBook通讯录OrderManagement订单管理应用级组件通常贡献的工件是Controllers控制器Repositories仓库Models模型Mixins用于 Model 的混入如何构建你自己的扩展第一步向已有的优秀组件学习动手之前建议先研究官方组件的写法。完整清单见 Component.md 的Official components一节其中包括核心组件loopback/authentication、loopback/authorization、loopback/boot、loopback/rest、loopback/rest-crud、loopback/rest-explorer等与扩展组件loopback/apiconnect、loopback/authentication-jwt、loopback/authentication-passport、loopback/context-explorer、loopback/cron、loopback/health、loopback/logging、loopback/metrics、loopback/pooling、loopback/typeorm、loopback/sequelize等。从extensions/与packages/目录中挑选一个与你目标相近的组件观察它的目录结构、src/index.ts导出、Component类定义与package.json依赖声明。第二步用 lb4 extension 脚手架创建扩展项目使用loopback/cli提供的lb4 extension命令即可快速搭建扩展骨架生成器源码见 packages/cli/generators/extension/index.jslb4 extension [options] [name]选项一览完整 CLI 标准选项参见仓库内includes/CLI-std-options.md的约定选项作用--description扩展的描述--outDir扩展的项目根目录--eslint为扩展项目添加 ESLint--prettier添加 Prettier--mocha添加 Mocha 测试框架--loopbackBuild添加loopback/build的脚本集--vscode添加 VSCode 配置文件参数name为可选的扩展名若提供则作为交互提示中的默认值。交互式提示生成器会依次询问扩展名称将显示在package.json中若命令行已提供则跳过该提示须符合 npm 命名规范扩展描述同样写入package.json创建扩展的目录名默认取扩展名是否添加可选模块eslint、prettier、mocha、loopback/build、vscode默认全部为 true。脚手架还会自动生成Component类名默认取项目名的类名化形式加Component后缀、xxxBindings绑定命名空间常量、DEFAULT_XXX_OPTIONS默认选项常量与xxxOptions选项接口——这些命名约定可以从 packages/cli/generators/extension/index.js 的scaffold()中看到。依赖声明的最佳实践扩展应当优先使用目标应用已安装的 LoopBack 模块让应用开发者能自主选择框架模块的版本例如在最新版存在回归 bug 时锁定旧版本或使用自己的 fork 提前应用补丁。官方推荐用peerDependencies声明扩展期望的宿主包用devDependencies让这些包在扩展自己的测试中可用。{ name: my-lb4-extension, version: 1.0.0, dependencies: { tslib: ^2.0.0 }, peerDependencies: { loopback/core: ^2.9.1, loopback/rest: ^5.2.0 }, devDependencies: { loopback/build: ^6.1.0, loopback/core: ^2.9.1, loopback/eslint-config: ^8.0.3, loopback/rest: ^5.2.0, loopback/testlab: ^3.2.0 } }单个扩展版本也可以同时支持框架依赖的多个版本{ peerDependencies: { loopback/core: ^1.9.0 || ^2.0.0 } }重要提醒缩小 peer 依赖的支持版本范围属于破坏性变更应当触发一次 semver-major 发布。例如从^1.5.3改为^1.5.3 || ^2.0.0是向后兼容的而从^1.5.3改为^2.0.0则是破坏性变更。在组件中注入宿主应用实例组件构造函数支持依赖注入例如把宿主的 REST 应用实例注入到组件中这一模式也常见于需要在挂载时立即操作 Context 的场景import {inject, Component, CoreBindings} from loopback/core; import {RestApplication} from loopback/rest; export class MyComponent implements Component { constructor( inject(CoreBindings.APPLICATION_INSTANCE) private application: RestApplication, ) { // 组件中可以访问 REST 应用实例 } }组件的生命周期与异步绑定组件的生命周期同样遵循 LoopBack 4 的生命周期管理模型详见 Life-cycle.md组件在app.component()被调用时同步实例化。构造函数与属性如controllers、bindings用于贡献绑定依赖注入可用例如注入宿主应用但必须保证所有依赖在app.component()调用时能被同步解析。某些场景下组件需要异步贡献绑定此时应实现init方法export class MyComponent implements Component, LifeCycleObserver { status not-initialized; initialized false; // 通过属性贡献绑定 controllers []; bindings []; constructor(inject(CoreBindings.APPLICATION_INSTANCE) private app) { // 通过构造函数贡献绑定 this.app.bind(foo).to(bar); } async init() { // 通过 init 异步贡献绑定 const val await readFromConfig(); this.app.bind(abc).to(val); this.status initialized; this.initialized true; } async start() { this.status started; } async stop() { this.status stopped; } }注意组件会被框架视为生命周期观察者除init外start与stop方法也受支持——它们会在应用启动/停止时被回调参见 packages/core/src/application.ts 中isLifeCycleObserverClass的检测逻辑。从源码理解动态扩展一个可验证的完整闭环将文档中的概念与仓库源码对应起来可以形成一条完整证据链扩展的注册任何扩展本质上是带标签/带 key 的 Binding。组件通过mountComponentpackages/core/src/component.ts把 providers、classes、controllers、servers、bindings 等批量注入应用 Context。扩展点的发现扩展点侧通过ContextViewpackages/context/src/context-view.ts基于BindingFilter动态追踪匹配的扩展绑定并在bind/unbind时自动刷新缓存——这就是认证管理器能感知新注册认证策略的底层机制。替换式扩展ctx.bind(key)允许后注册的绑定覆盖现有实现这也是框架内置扩展可以被应用级扩展升级的原理。如果你想实际验证这套机制仓库内packages/core/src/__tests__/acceptance/application.acceptance.ts中定义了ComponentA/ComponentB等测试组件来验证组件挂载行为packages/context/src/__tests__/unit/context-view.unit.ts则覆盖了ContextView的动态刷新行为——它们都是阅读扩展机制源码的绝佳切入点。延伸阅读Crafting LoopBack 4可扩展性架构设计的完整理由ContextIoC 容器与绑定机制详解Dependency injection依赖注入的完整指南Creating components组件开发的详细规范含依赖、注入、工件注册Component组件的使用方式与官方组件清单Extension generatorlb4 extension生成器的完整选项与交互说明Life cycle组件/观察者的生命周期模型Extending request body parsing一个通过组件贡献自定义 body parser 的实战案例赞分享后端微服务【免费下载链接】loopback-nextLoopBack makes it easy to build modern API applications that require complex integrations.项目地址https://gitcode.com/gh_mirrors/lo/loopback-next点击查看免费下载相关推荐LoopBack 4 扩展点与扩展Extension Point/Extension模式实战用 Context、inject 与 Component 打造可插拔服务LoopBack 4 扩展点与扩展Extension Point/Extension模式实战用 Context、inject 与 Component 打后端微服务NumPy 2.2.5 补丁版发布解析迭代器 Cython 绑定、调度缓存死锁与 f2py 解析等 19 项修复全览NumPy 2.2.5 补丁版发布解析迭代器 Cython 绑定、调度缓存死锁与 f2py 解析等 19 项修复全览 导读 本文基于本仓库 doc/chang后端微服务LoopBack 4 中间件架构深度解析基于 Interceptor 的 Middleware 机制与 REST 序列集成LoopBack 4 中间件架构深度解析基于 Interceptor 的 Middleware 机制与 REST 序列集成 导读 本文以 LoopBack 4后端微服务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考