如何实现IoC容器和服务提供者是什么概念

思考并回答以下问题:

IoC容器和服务提供者

上一节我们的代码还是没有完全达到解耦,假如我们项目里有很多功能用到了这个login功能,我们则在这几个页面反复写。但是突然我们有一天换需求了,觉得数据库记录日志不太好,想要改成文件的,那我们不是每个页面用到login功能的都去替换成new FileLog()吗?那该如何修改呢?

我们可以借助一个容器,提前把log,user都绑定到Ioc容器中。User的创建交给这个容器去做。比如下面这样的,你在任何地方使用login,都不需要关心是用什么记录日志了,哪怕后期需要修改只需要在IoC容器修改绑定其他记录方式日志就行了。

具体代码实现的思路

  • 1、Ioc容器维护binding数组记录bind方法传入的键值对如:log=>FileLog,user=>User。
  • 2、在ioc->make(‘user’)的时候,通过反射拿到User的构造函数,拿到构造函数的参数,发现参数是User的构造函数参数log,然后根据log得到FileLog。
  • 3、这时候我们只需要通过反射机制创建$filelog = new FileLog();
  • 4、通过newInstanceArgs然后再去创建new User($filelog);
1
2
3
4
5
6
// 实例化ioc容器
$ioc = new Ioc();
$ioc->bind('log','FileLog');
$ioc->bind('user','User');
$user = $ioc->make('user');
$user->login();

这里的容器就是指Ioc容器,服务提供者就是User。

上一节遗留一个问题,如果参数是接口该怎么处理,其实就是通过IoC容器提前绑定好。

核心IoC容器代码编写

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
interface log
{
public function write();
}

// 文件记录日志
class FileLog implements Log
{
public function write()
{
echo 'file log write...';
}
}

// 数据库记录日志
class DatabaseLog implements Log
{
public function write()
{
echo 'database log write...';
}
}

class User
{
protected $log;

public function __construct(Log $log)
{
$this->log = $log;
}

public function login()
{
// 登录成功,记录登录日志
echo 'login success...';
$this->log->write();
}
}

class Ioc
{
public $binding = [];

public function bind($abstract, $concrete)
{
// 这里为什么要返回一个closure呢?因为bind的时候还不需要创建User对象,所以采用closure等make的时候再创建FileLog;
$this->binding[$abstract]['concrete'] = function ($ioc) use ($concrete) {
return $ioc->build($concrete);
};

}

public function make($abstract)
{
// 根据key获取binding的值
$concrete = $this->binding[$abstract]['concrete'];
return $concrete($this);
}

// 创建对象
public function build($concrete)
{
$reflector = new ReflectionClass($concrete);

$constructor = $reflector->getConstructor();

if(is_null($constructor))
{
return $reflector->newInstance();
}
else
{
$dependencies = $constructor->getParameters();
$instances = $this->getDependencies($dependencies);

return $reflector->newInstanceArgs($instances);
}
}

// 获取参数的依赖
protected function getDependencies($paramters)
{
$dependencies = [];

foreach ($paramters as $paramter)
{
$dependencies[] = $this->make($paramter->getClass()->name);
}

return $dependencies;
}
}

// 实例化IoC容器
$ioc = new Ioc();
$ioc->bind('log','FileLog');
$ioc->bind('user','User');
$user = $ioc->make('user');
$user->login();

至此,我们的IoC就已经实现了。

Laravel中的服务容器和服务提供者

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
$providers = [
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
...
]
...

// 随便打开一个类比如CacheServiceProvider,这个服务提供者都是通过调用register方法注册到ioc容器中,其中的app就是Ioc容器。
// singleton可以理解成我们的上面例子中的bind方法。只不过这里singleton指的是单例模式。

class CacheServiceProvider
{
public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});

$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});

$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
}
}

可以在config目录找到app.php中providers,这个数组定义的都是已经写好的服务提供者。

具体服务提供者register方法是什么时候执行的,我们到讲Laravel生命周期的时候再详细说。

0%