有了上一节的依赖注入的基础之后,现在我们来看Laravel的服务容器。服务容器就是管理类依赖和执行类依赖的容器。
绑定就是将类的依赖注入到容器之中,Laravel的绑定方法
- $this->app->bind()
- $this->app->singleton() 绑定一个单例
Laravel容器更详细的介绍,请参考这边非常详细的文章 www.insp.top/learn-larav…
绑定的方式主要有三种方式
//使用回调函数方法绑定
$this->app->bind("Hello",function (){
return new HelloService();
});
//绑定一个类
$this->app->bind("Hello2",HelloService::class);
//绑定一个已存在的类
$this->app->instance("App\Service\HelloService",new HelloService());
//绑定一个单例
$this->app->singleton("Hello3",HelloService::class);
//绑定一个接口约定的
$config = "database";
if($config == "file") {
$this->app->bind('AppService\Imp\AppendData','App\Service\AppendWrite\DataBaseWrite');
}else{
$this->app->bind('AppService\Imp\AppendData','App\Service\AppendWrite\FileWrite');
}
下面分别介绍这几种绑定方式的实际运用
先创建一个Service类,用来构造实例类
//实例类
class HelloService {
public $name;
public function __construct()
{
}
public function sayHello(){
return "Hello";
}
}
Laravel启动会加AppServiceProvider,我们在此方法里面进行注入
//Providers/AppServiceProvider.php
//使用构造方法绑定
$this->app->bind("Hello",function (){
return new HelloService();
});
//绑定一个类
$this->app->bind("Hello2",HelloService::class);
//绑定一个已存在的类
$this->app->instance("App\Service\HelloService",new HelloService());
//绑定一个单例
$this->app->singleton("Hello3",HelloService::class);
进行上面多种方式的测试
public function index(Request $request) {
//获取到绑定的类
$helloService = app("App\Service\HelloService");
echo $helloService->sayHello();
$helloService2 = app("Hello2");
echo $helloService2->sayHello();
$helloService3 = app("Hello3");
$helloService3Copy = app("Hello3");
//使用单例返回的两个类是同一个类的实力
var_dump($helloService3 instanceof $helloService3Copy);
echo $helloService->sayHello();
}
接下来我们看看bind绑定到接口的运用
//接口类
interface AppendData {
public function write();
}
//database的实现
class DataBaseWrite implements AppendData {
public function write()
{
echo "我是数据库写入的";
}
}
//file的实现
class FileWrite implements AppendData {
public function write()
{
echo "我是文件写入的";
}
}
//使用工厂模式构建类
class WriteFactory {
public static $instance=[];
private function __construct()
{
}
private function __clone()
{
// TODO: Implement __clone() method.
}
//实力的获取
//这里其实没不要使用单例模式,因为app()->singleton 本身就已经是单例模式了
public static function instance(string $name) {
if(empty(self::$instance[$name])) {
$file = [
"database"=>"App\Service\AppendWrite\DataBaseWrite",
"file"=>"App\Service\AppendWrite\FileWrite"
];
//实现接口和类的绑定
if(isset($file[$name])) {
app()->singleton("AppService\Imp\AppendData",$file[$name]);
}else{
new \Exception("不存在这个驱动");
}
self::$instance[$name] = app("AppService\Imp\AppendData");
}
return self::$instance[$name];
}
}
//测试
public function index(Request $request) {
//获取到绑定的类
$config = "file";
$write = WriteFactory::instance($config);
$write->write();
}
通过不同的$config,让工厂方法去获取到类,调用者并不用去关心具体绑定的是哪个类,只用去调用接口中已经存在的方法即可,将调用者和实现者进行解耦,以上用到了单例模式,工厂模式,策略模式
通过以上简单的了解bind绑定,我们已经了解到Laravl处理依赖的方式,接下来我们看看Laravel的源码,是如何实现服务的注册和服务的获取的
//Illuminate/Container/Container.php
public function bind($abstract, $concrete = null, $shared = false)
{
// If no concrete type was given, we will simply set the concrete type to the
// abstract type. After that, the concrete type to be registered as shared
// without being forced to state their classes in both of the parameters.
//删除instances和aliases的当前数据
$this->dropStaleInstances($abstract);
if (is_null($concrete)) {
$concrete = $abstract;
}
// If the factory is not a Closure, it means it is just a class name which is
// bound into this container to the abstract type and we will just wrap it
// up inside its own Closure to give us more convenience when extending.
//如果不是回调函数的话,包装成回调函数
if (!$concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
//赋值变量,并且绑定到bindings中
$this->bindings[$abstract] = compact('concrete', 'shared');
// If the abstract type was already resolved in this container we'll fire the
// rebound listener so that any objects which have already gotten resolved
// can have their copy of the object updated via the listener callbacks.
//如果存在resolved和instances中再处理
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
//Illuminate/Container/Container.php
protected function resolve($abstract, $parameters = [])
{
//递归调用别名
$abstract = $this->getAlias($abstract);
$needsContextualBuild = !empty($parameters) || !is_null(
$this->getContextualConcrete($abstract)
);
// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
// so the developer can keep using the same objects instance every time.
//如果存在单例数组中,则直接返回
if (isset($this->instances[$abstract]) && !$needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
$concrete = $this->getConcrete($abstract);
// We're ready to instantiate an instance of the concrete type registered for
// the binding. This will instantiate the types, as well as resolve any of
// its "nested" dependencies recursively until all have gotten resolved.
//判断是否可以构造
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
}
//使用上一节所说的反射构造对象
public function build($concrete)
{
// If the concrete type is actually a Closure, we will just execute it and
// hand back the results of the functions, which allows functions to be
// used as resolvers for more fine-tuned resolution of these objects.
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}
$reflector = new ReflectionClass($concrete);
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface of Abstract Class and there is
// no binding registered for the abstractions so we need to bail out.
if (!$reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}
$this->buildStack[] = $concrete;
$constructor = $reflector->getConstructor();
// If there are no constructors, that means there are no dependencies then
// we can just resolve the instances of the objects right away, without
// resolving any other types or dependencies out of these containers.
if (is_null($constructor)) {
array_pop($this->buildStack);
return new $concrete;
}
$dependencies = $constructor->getParameters();
// Once we have all the constructor's parameters we can create each of the
// dependency instances and then use the reflection instances to make a
// new instance of this class, injecting the created dependencies in.
$instances = $this->resolveDependencies(
$dependencies
);
array_pop($this->buildStack);
return $reflector->newInstanceArgs($instances);
}
上面我们在源码中粗略的分析了bind构造依赖类的方法,下面我们看看Laravel是如何进行Make获取依赖类的
//Illuminate/Container/Container.php
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
//Illuminate/Container/Container.php
protected function resolve($abstract, $parameters = [])
{
//获取到$abstract的别名
$abstract = $this->getAlias($abstract);
//
$needsContextualBuild = !empty($parameters) || !is_null(
$this->getContextualConcrete($abstract)
);
// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
// so the developer can keep using the same objects instance every time.
//如果是单例,直接返回
if (isset($this->instances[$abstract]) && !$needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
//获取接口对应的实现
$concrete = $this->getConcrete($abstract);
// We're ready to instantiate an instance of the concrete type registered for
// the binding. This will instantiate the types, as well as resolve any of
// its "nested" dependencies recursively until all have gotten resolved.
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
//如果是借口实现的绑定,在执行一次make,满足上面的判断
$object = $this->make($concrete);
}
// If we defined any extenders for this type, we'll need to spin through them
// and apply them to the object being built. This allows for the extension
// of services, such as changing configuration or decorating the object.
//如果定义了扩展,循环执行扩展
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
// If the requested type is registered as a singleton we'll want to cache off
// the instances in "memory" so we can return it later without creating an
// entirely new instance of an object on each subsequent request for it.
//如果服务是以单例方式注册进来的则,把构建好的服务对象放到$instances里
if ($this->isShared($abstract) && !$needsContextualBuild) {
$this->instances[$abstract] = $object;
}
//执行构建完成的一些回调处理
$this->fireResolvingCallbacks($abstract, $object);
// Before returning, we will also set the resolved flag to "true" and pop off
// the parameter overrides for this build. After those two things are done
// we will be ready to return back the fully constructed class instance.
//将当前类放入到获取完成
$this->resolved[$abstract] = true;
array_pop($this->with);
//返回当前获取的实力
return $object;
}
通过上面的build构建和make获取,我们应该对Laravel服务容器有个大致的了解了, 接下来我们就需要对服务容器进行管理,比如进行统一的注册,统一的初始化,下一章我们将来讲解服务容器的管理--服务提供者