详解如何实现Laravel的服务容器的方法示例(3)
上面的代码用singleton绑定了一个名为anonymous的服务,回调函数里返回了一个匿名类的实例。这个匿名类在被实例化时会输出一段文字。无论我们make多少次anonymous,这个回调函数只会被执行一次,匿名类也只会被实例化一次。
5. 自动注入
自动注入是Ioc容器的核心,没有自动注入就无法做到控制反转。
自动注入就是指,在实例化一个类时,用反射类来获取__construct所需要的参数,然后根据参数的类型,从容器中找到已绑定的服务。我们只要有了__construct方法所需的所有参数,就能自动实例化该类,实现自动注入。
现在,我们增加一个build方法,它只接收一个参数,就是类名。build方法会用反射类来获取__construct方法所需要的参数,然后返回实例化结果。
另外一点就是,我们之前在调用make方法时,如果传的是一个未绑定的类,我们直接new了这个类。现在我们把未绑定的类交给build方法来构建,因为它支持自动注入。
class InjectionContainer extends SingletonContainer
{
// 获取服务
public function make($name)
{
if (isset($this->instances[$name])) {
return $this->instances[$name];
}
if (isset($this->bindings[$name])) {
// 执行回调函数并返回
$instance = call_user_func($this->bindings[$name]['callback']);
if ($this->bindings[$name]['shared']) {
// 标记为单例时,存储到服务中
$this->instances[$name] = $instance;
}
} else {
// 使用build方法构建此类
$instance = $this->build($name);
}
return $instance;
}
// 构建一个类,并自动注入服务
public function build($class)
{
$reflector = new ReflectionClass($class);
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
// 没有构造函数,直接new
return new $class();
}
$dependencies = [];
// 获取构造函数所需的参数
foreach ($constructor->getParameters() as $dependency) {
if (is_null($dependency->getClass())) {
// 参数类型不是类时,无法从容器中获取依赖
if ($dependency->isDefaultValueAvailable()) {
// 查找参数的默认值,如果有就使用默认值
$dependencies[] = $dependency->getDefaultValue();
} else {
// 无法提供类所依赖的参数
throw new Exception('找不到依赖参数:' . $dependency->getName());
}
} else {
// 参数类型是类时,就用make方法构建该类
$dependencies[] = $this->make($dependency->getClass()->name);
}
}
return $reflector->newInstanceArgs($dependencies);
}
}
// ----------- ↓↓↓↓示例代码↓↓↓↓ ----------- //
class Redis
{
}
class Cache
{
protected $redis;
// 构造函数中依赖Redis服务
public function __construct(Redis $redis)
{
$this->redis = $redis;
}
}
$container = new InjectionContainer();
// 绑定Redis服务
$container->singleton(Redis::class, function () {
return new Redis();
});
// 构建Cache类
$cache = $container->make(Cache::class);
var_dump($cache);
内容版权声明:除非注明,否则皆为本站原创文章。
