Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
koatty_container
Advanced tools
Typescript中IOC容器的实现,支持DI(依赖注入)以及 AOP (切面编程)。参考Spring IOC的实现机制,用Typescript实现了一个IOC容器,在应用启动的时候,自动分类装载组件,并且根据依赖关系,注入相应的依赖。它解决了一个最主要的问题:将组件的创建+配置与组件的使用相分离,并且,由IoC容器负责管理组件的生命周期。
IoC全称Inversion of Control,直译为控制反转。不是什么技术,而是一种设计思想。在OO开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
传统OO程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
听着比较难以理解是不是,我们来举例说明,我们假定一个在线书店,通过BookService获取书籍:
export class BookService {
private config: DataConfig = new DataConfig();
private dataSource: DataSource = new MysqlDataSource(config);
protected constructor() {
}
public getBook(long bookId): Book {
try {
const conn = this.dataSource.getConnection();
...
return book;
} catch (err){
throw Error("message");
}
}
}
为了从数据库查询书籍,BookService持有一个DataSource。为了实例化一个DataSource,又不得不实例化一个DataConfig。
现在,我们继续编写UserService获取用户:
export class UserService {
private config: DataConfig = new DataConfig();
private dataSource: DataSource = new MysqlDataSource(config);
public getUser(userId: number):User {
try {
const conn = this.dataSource.getConnection();
...
return user;
} catch (err){
throw Error("message");
}
}
}
因为UserService也需要访问数据库,因此,我们不得不也实例化一个DataSource。
在处理用户购买的CartController中,我们需要实例化UserService和BookService:
export class CartController extends {
private bookService = new BookService();
private userService = new UserService();
...
}
类似的,在购买历史HistoryController中,也需要实例化UserService和BookService:
export class HistoryController extends {
private bookService = new BookService();
private userService = new UserService();
...
}
上述每个组件都采用了一种简单的通过new创建实例并持有的方式。仔细观察,会发现以下缺点:
实例化一个组件,要先实例化依赖的组件,强耦合
每个组件都需要实例化一个依赖组件,没有复用
很多组件需要销毁以便释放资源,例如DataSource,但如果该组件被多个组件共享,如何确保它的使用方都已经全部被销毁
随着更多的组件被引入,需要共享的组件写起来会更困难,这些组件的依赖关系会越来越复杂
如果一个系统有大量的组件,其生命周期和相互之间的依赖关系如果由组件自身来维护,不但大大增加了系统的复杂度,而且会导致组件之间极为紧密的耦合,继而给测试和维护带来了极大的困难。
因此,核心问题是:
解决这一问题的核心方案就是IoC。
根据组件的不同应用场景,Koatty把Bean分为 'COMPONENT' | 'CONTROLLER' | 'MIDDLEWARE' | 'SERVICE' 四种类型。
COMPONENT 扩展类、第三方类属于此类型,例如 Plugin,ORM持久层等
CONTROLLER 控制器类
MIDDLEWARE 中间件类
SERVICE 逻辑服务类
随着项目规模的扩大,很容易出现循环依赖。koatty_container解决循环依赖的思路是延迟加载。koatty_container在 app
上绑定了一个 appReady
事件,用于延迟加载产生循环依赖的bean, 在使用IOC的时候需要进行处理:
//
app.emit("appReady");
注意:虽然延迟加载能够解决大部分场景下的循环依赖,但是在极端情况下仍然可能装配失败,解决方案:
1、尽量避免循环依赖,新增第三方公共类来解耦互相依赖的类
2、使用IOC容器获取类的原型(getClass),自行实例化
通过组件加载的Loader,在项目启动时,会自动分析并装配Bean,自动处理好Bean之间的依赖问题。IOC容器提供了一系列的API接口,方便注册以及获取装配好的Bean。
注册Bean到IOC容器。
从容器中获取Bean。
从容器中获取类的原型。
根据class类获取容器中的实例
Koatty基于IOC容器实现了一套切面编程机制,利用装饰器以及内置特殊方法,在bean装载到IOC容器内的时候,通过嵌套函数的原理进行封装,简单而且高效。
通过@Before、@After、@BeforeEach、@AfterEach装饰器声明的切点
声明方式 | 依赖Aspect切面类 | 能否使用类作用域 | 入参依赖切点方法 |
---|---|---|---|
装饰器声明 | 依赖 | 不能 | 依赖 |
依赖Aspect切面类: 需要创建对应的Aspect切面类才能使用
能否使用类作用域: 能不能使用切点所在类的this指针
入参依赖切点方法: 装饰器声明切点所在方法的入参同切面共享
例如:
@Controller('/')
export class TestController extends BaseController {
app: App;
ctx: KoattyContext;
@Autowired()
protected TestService: TestService;
@Before(TestAspect) //依赖TestAspect切面类, 能够获取path参数
async test(path: string){
}
}
使用koatty_cli
进行创建:
koatty aspect test
自动生成的模板代码:
import { Aspect } from "koatty";
import { App } from '../App';
@Aspect()
export class TestAspect {
app: App;
run() {
console.log('TestAspect');
}
}
装饰器名称 | 参数 | 说明 | 备注 |
---|---|---|---|
@Aspect() | identifier 注册到IOC容器的标识,默认值为类名。 | 声明当前类是一个切面类。切面类在切点执行,切面类必须实现run方法供切点调用 | 仅用于切面类 |
@BeforeEach() | aopName 切点执行的切面类名 | 为当前类声明一个切面,在当前类每一个方法("constructor", "init", "__before", "__after"除外)执行之前执行切面类的run方法。 | |
@AfterEach() | aopName 切点执行的切面类名 | 为当前类声明一个切面,在当前每一个方法("constructor", "init", "__before", "__after"除外)执行之后执行切面类的run方法。 | |
装饰器名称 | 参数 | 说明 | 备注 |
---|---|---|---|
@Autowired() | identifier 注册到IOC容器的标识,默认值为类名 cType 注入bean的类型 constructArgs 注入bean构造方法入参。如果传递该参数,则返回request作用域的实例 isDelay 是否延迟加载。延迟加载主要是解决循环依赖问题 | 从IOC容器自动注入bean到当前类 | |
@Values() | val 属性值, 值类型同属性类型一致 defaultValue 被定义时,当val值为undefined、null、NaN时取值defaultValue型 | val值可以是一个函数,取值函数结果 |
装饰器名称 | 参数 | 说明 | 备注 |
---|---|---|---|
@Before(aopName: string) | aopName 切点执行的切面类名 | 为当前方法声明一个切面,在当前方法执行之前执行切面类的run方法。 | |
@After() | aopName 切点执行的切面类名 | 为当前方法声明一个切面,在当前方法执行之后执行切面类的run方法。 |
装饰器名称 | 参数 | 说明 | 备注 |
---|---|---|---|
@Inject() | paramName 构造方法入参名(形参) cType 注入bean的类型 | 该装饰器使用类构造方法入参来注入依赖, 如果和 @Autowired() 同时使用, 可能会覆盖autowired注入的相同属性 | 仅用于构造方法(constructor)的入参 |
FAQs
IOC Container for Koatty.
We found that koatty_container demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.