控制反转的思考

我认为,控制反转和模版模式以及框架的概念是类似的,而控制正转类同于策略模式,库。

反转的是依赖关系,对框架代码来说,实现的就是模版模式。使用框架的代码,其实被反转调用了。而库则是主动去调用的,是一种策略模式。

因为控制反转是一种设计模式,因此所有语言都存在。

例如C,JS是以回调函数实现。而Python作为一种动态语言,这方面的支持更为强大。

对于OOP的静态语言(Cpp,Java,Golang等等)来说,通过继承的实现更为自然(Golang通过桥接模式变相实现)。

Java作为工程化支持最成熟鉴权的语言,这一块更是玩出了花来

Java Spring

Spring可以算是Java工程化的代表了,它用依赖注入的形式完成了控制反转。

对重型的类(DB,RPC框架,轻量类的工厂)都通过反射的方式注册在了BeanFactory,当想调用的时候,由BeanFactory完成依赖的生成。

这种设计思想和中介者模式是类似的,然而好处不仅仅只是松耦合。

在Java的动态代理出现以后,大名鼎鼎的AOP在这个基础上应运而生。

Java的动态代理

Java的动态代理最早是由Jdk提供的支持,提供了动态扩展类方法的功能。

总的来说就是通过编译期间生成代理的字节码并且调用目标类的方法来对方法进行扩展。

叫做代理模式是希望使用这种技术时,新加入的逻辑对原逻辑透明吧。

否则应该叫动态装饰,Python就这么叫。

Spring AOP的实现原理

Spring AOP 实现原理与 CGLIB 应用

AOP

面向切片编程是一种逻辑上的分层,能更好的分离不同的业务逻辑,是动态代理的延伸。

对C系的开发者来说,是一个很大的痛点。

例如你的代码已经上线了,此时几百个接口要添加鉴权逻辑,或者只是打印一个调用时间。

这种业务逻辑本身不相关的东西,带来了很多重复的代码,影响了业务逻辑的可读性。

最糟糕的是,这种代码是对修改开放的,需要小心测试是否一不小心污染了原本的代码。

AOP完美的解决了这个问题

每个切面(Aspect)定义为"一个关注点的模块化,这个关注点可能会横切多个对象",

它包含连接点(Joinpoint)和通知(Advice)

连接点是指和业务逻辑不相关的这一部分代码,通知是指具体使用的方式(before,after,around等等)

而切入点(Pointcut)就是需要添加这些代码的类的方法

具体使用看下面两篇文章

Spring AOP在鉴权和日志中的应用

基于注解的Spring AOP的配置和使用

在Spring依赖注入的基础上,所有的类都可以从BeanFactory中拿出,这样才能被AOP依赖的动态代理技术掉包成代理类。

golang的控制反转

golang的aop有其他曲线救国的方式:

aspectgo

这个项目利用了ast库分析已有代码,生成静态代理代码在另外一个临时工作目录。这个工作目录会替换你的调用代码来完成控制反转。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"fmt"
)

func sayHello(s string) {
fmt.Println("hello " + s)
}

func main() {
sayHello("world")
}

临时工作目录代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Automatically generated by AspectGo (github.com/AkihiroSuda/aspectgo)
// DO NOT EDIT MANUALLY

package main

import aspectrt "github.com/AkihiroSuda/aspectgo/aspect/rt"
import "agaspect"

import (
"fmt"
)

func sayHello(s string) {
fmt.Println("hello " + s)
}

func main() {
(_ag_pgen_ag_proxy_0())("world")
}

func _ag_proxy_0(s string) {
_ag_res := (&agaspect.ExampleAspect{}).Advice(&aspectrt.ContextImpl{XArgs: []interface {
}{s}, XFunc: func(_ag_args []interface {
}) []interface {
} {
_ag_arg0 := _ag_args[0].(string)
sayHello(_ag_arg0)
_ag_res := []interface {
}{}
return _ag_res
}, XReceiver: nil})
_ = _ag_res
return
}

func _ag_pgen_ag_proxy_0() func(string) {
return func(s string) {
_ag_proxy_0(s)
}
}

参考资料

控制反转 (IoC) 和依赖注入 (DI)

Spring的IOC和AOP

长话短说Spring(1)之IoC控制反转

GO 语言学习的五个阶段(带例子)

go generate介绍

Golang Generate命令说明与使用