场景
- service container是laravel的核心内容, 那么它到底是怎么实现的呢?
分析
- 使用service container的方式有很多, 下面罗列几种
- app(‘Illuminate\Hashing\BcryptHasher’)->make($password_source)
- app(‘Illuminate\Contracts\Hashing\Hasher’)->make($password_source)
- app(‘hash’)->make($password_source)
- resolve(‘hash’)->make($password_source)
- 其实本质都是调用Illuminate\Container\Container
app(‘Illuminate\Hashing\BcryptHasher’)具体实现
- 从请求开始
-
index.php 实例化了Illuminate\Foundation\Application 它的__construct初始化几个比较重要的属性
- protected $instances = []; The container’s shared instances.
- protected $$aliases= []; The registered type aliases.
- 结构 i n s t a n c e s [ instances[ instances[alias] = $abstract;
- protected $abstractAliases = []; The registered aliases keyed by the abstract name.
- 结构 a b s t r a c t A l i a s e s [ abstractAliases[ abstractAliases[abstract][] = $alias
- app(‘Illuminate\Hashing\BcryptHasher’) 简单的跟踪下源码,就会发现调用Illuminate\Container\Container resolve方法;那它做了哪些工作呢?
- $abstract =
t
h
i
s
−
>
g
e
t
A
l
i
a
s
(
this->getAlias(
this−>getAlias(abstract);
- Get the alias for an abstract if available. 这一步是为了获取别名(key)
- 此时 $abstract = ‘Illuminate\Hashing\BcryptHasher’
- if (isset(
t
h
i
s
−
>
i
n
s
t
a
n
c
e
s
[
this->instances[
this−>instances[abstract]) && ! $needsContextualBuild) {}
- $this->instances(第一步已经初始化过的) 中看看是否有值
- 继续
- $concrete =
t
h
i
s
−
>
g
e
t
C
o
n
c
r
e
t
e
(
this->getConcrete(
this−>getConcrete(abstract);
- Get the concrete type for a given abstract.
这段话很有意思, 如果我们没有提前去注册abstract 则我们将abstract变量当成要实例化的类的名称。 If we don't have a registered resolver or concrete for the type, we'll just assume each type is a concrete name and will attempt to resolve it as is since the container should be able to resolve concretes automatically.
- 那么concrete到底是什么呢? 一般是service provider 使用 singleton bind注册的匿名函数;否则向上面所说的一样$abstract 本身假设成class name
- 此时 $concrete = ‘Illuminate\Hashing\BcryptHasher’
-
t
h
i
s
−
>
i
s
B
u
i
l
d
a
b
l
e
(
this->isBuildable(
this−>isBuildable(concrete, $abstract)
- Determine if the given concrete is buildable.
- 此时返回true
$object = $this->build($concrete);
- Instantiate a concrete instance of the given type.
这个方法里面利用ReflectionClass 返回了 Illuminate\Hashing\BcryptHasher实例化对象
- 当然如果有依赖关系的话, 会实现依赖的加载
- if (
t
h
i
s
−
>
i
s
S
h
a
r
e
d
(
this->isShared(
this−>isShared(abstract) && ! $needsContextualBuild) {}
- 判断是否要进行单例存储
-
t
h
i
s
−
>
f
i
r
e
R
e
s
o
l
v
i
n
g
C
a
l
l
b
a
c
k
s
(
this->fireResolvingCallbacks(
this−>fireResolvingCallbacks(abstract, $object);
- Fire all of the resolving callbacks.
这一步执行的是service provider boot方法
-
t
h
i
s
−
>
r
e
s
o
l
v
e
d
[
this->resolved[
this−>resolved[abstract] = true;
- 标记成已经解析过来的
- $abstract =
t
h
i
s
−
>
g
e
t
A
l
i
a
s
(
this->getAlias(
this−>getAlias(abstract);
-
加载依赖
$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);
laravel内置的aliase表
[
'app' => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
'auth.driver' => [\Illuminate\Contracts\Auth\Guard::class],
'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class],
'config' => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class],
'cookie' => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class],
'encrypter' => [\Illuminate\Encryption\Encrypter::class, \Illuminate\Contracts\Encryption\Encrypter::class],
'db' => [\Illuminate\Database\DatabaseManager::class],
'db.connection' => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class],
'events' => [\Illuminate\Events\Dispatcher::class, \Illuminate\Contracts\Events\Dispatcher::class],
'files' => [\Illuminate\Filesystem\Filesystem::class],
'filesystem' => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
'filesystem.disk' => [\Illuminate\Contracts\Filesystem\Filesystem::class],
'filesystem.cloud' => [\Illuminate\Contracts\Filesystem\Cloud::class],
'hash' => [\Illuminate\Contracts\Hashing\Hasher::class],
'translator' => [\Illuminate\Translation\Translator::class, \Illuminate\Contracts\Translation\Translator::class],
'log' => [\Illuminate\Log\Writer::class, \Illuminate\Contracts\Logging\Log::class, \Psr\Log\LoggerInterface::class],
'mailer' => [\Illuminate\Mail\Mailer::class, \Illuminate\Contracts\Mail\Mailer::class, \Illuminate\Contracts\Mail\MailQueue::class],
'auth.password' => [\Illuminate\Auth\Passwords\PasswordBrokerManager::class, \Illuminate\Contracts\Auth\PasswordBrokerFactory::class],
'auth.password.broker' => [\Illuminate\Auth\Passwords\PasswordBroker::class, \Illuminate\Contracts\Auth\PasswordBroker::class],
'queue' => [\Illuminate\Queue\QueueManager::class, \Illuminate\Contracts\Queue\Factory::class, \Illuminate\Contracts\Queue\Monitor::class],
'queue.connection' => [\Illuminate\Contracts\Queue\Queue::class],
'queue.failer' => [\Illuminate\Queue\Failed\FailedJobProviderInterface::class],
'redirect' => [\Illuminate\Routing\Redirector::class],
'redis' => [\Illuminate\Redis\RedisManager::class, \Illuminate\Contracts\Redis\Factory::class],
'request' => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
'router' => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],
'session' => [\Illuminate\Session\SessionManager::class],
'session.store' => [\Illuminate\Session\Store::class, \Illuminate\Contracts\Session\Session::class],
'url' => [\Illuminate\Routing\UrlGenerator::class, \Illuminate\Contracts\Routing\UrlGenerator::class],
'validator' => [\Illuminate\Validation\Factory::class, \Illuminate\Contracts\Validation\Factory::class],
'view' => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
]
- resove 源码
/**
* Resolve the given type from the container.
*
* @param string $abstract
* @param array $parameters
* @return mixed
*/
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);
}
// 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) && ! $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;
}