laravel里,当我们在controller 的构造方法,或者实例方法中用类型指定一个参数时,系统可以自动帮你把该参数的实例注入进去。
那么内部是如何实现的呢?
先列出几个技术点
1.类ReflectionClass
该类用于对指定类进行反射,提取类信息。
2.ReflectionClass.getConstructor
获取指定类的构造方法,返回ReflectionMethod
3.ReflectionMethod.getParameters
获取方法的参数信息
4.ReflectionClass.newInstanceArgs
创建类实例
当,laravel通过路由寻找到controller的类名时,会先创建实例,如果构造函数里包含指定了类型的参数,并且router设定里并没有传递时,
就会创建这个参数的实例,当然这个参数的构造方法如果还包含指定类型的参数时,还是会创建,一直递归到没有参数指定为止。
入口代码在类Illuminate\Container\Containerd的方法make里
public function make($abstract, array $parameters = []) { $abstract = $this->getAlias($this->normalize($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])) { return $this->instances[$abstract]; } $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, $parameters); } else { $object = $this->make($concrete, $parameters); } // 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. if ($this->isShared($abstract)) { $this->instances[$abstract] = $object; } $this->fireResolvingCallbacks($abstract, $object); $this->resolved[$abstract] = true; return $object; }
这个地方的核心代码是在类Illuminate\Container\Containerd的方法build里
public function build($concrete, array $parameters = []) { // 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, $parameters); } $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()) { if (! empty($this->buildStack)) { $previous = implode(', ', $this->buildStack); $message = "Target [$concrete] is not instantiable while building [$previous]."; } else { $message = "Target [$concrete] is not instantiable."; } throw new BindingResolutionException($message); } $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. $parameters = $this->keyParametersByArgument( $dependencies, $parameters ); $instances = $this->getDependencies( $dependencies, $parameters ); array_pop($this->buildStack); return $reflector->newInstanceArgs($instances); }