make解析

服务容器对对象的自动解析是服务容器的核心功能,make 函数、build 函数是实例化对象重要的核心,先大致看一下代码:

  1. public function make($abstract)
  2. {
  3. $abstract = $this->getAlias($abstract);
  4.  
  5. if (isset($this->deferredServices[$abstract])) {
  6. $this->loadDeferredProvider($abstract);
  7. }
  8.  
  9. return parent::make($abstract);
  10. }
  11. public function make($abstract)
  12. {
  13. return $this->resolve($abstract);
  14. }
  15.  
  16. public function resolve($abstract, $parameters = [])
  17. {
  18. $abstract = $this->getAlias($abstract);
  19.  
  20. $needsContextualBuild = ! empty($parameters) || ! is_null(
  21. $this->getContextualConcrete($abstract)
  22. );
  23.  
  24. // If an instance of the type is currently being managed as a singleton we'll
  25. // just return an existing instance instead of instantiating new instances
  26. // so the developer can keep using the same objects instance every time.
  27. if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
  28. return $this->instances[$abstract];
  29. }
  30.  
  31. $concrete = $this->getConcrete($abstract);
  32.  
  33. // We're ready to instantiate an instance of the concrete type registered for
  34. // the binding. This will instantiate the types, as well as resolve any of
  35. // its "nested" dependencies recursively until all have gotten resolved.
  36. if ($this->isBuildable($concrete, $abstract)) {
  37. $object = $this->build($concrete);
  38. } else {
  39. $object = $this->make($concrete);
  40. }
  41.  
  42. // If we defined any extenders for this type, we'll need to spin through them
  43. // and apply them to the object being built. This allows for the extension
  44. // of services, such as changing configuration or decorating the object.
  45. foreach ($this->getExtenders($abstract) as $extender) {
  46. $object = $extender($object, $this);
  47. }
  48.  
  49. // If the requested type is registered as a singleton we'll want to cache off
  50. // the instances in "memory" so we can return it later without creating an
  51. // entirely new instance of an object on each subsequent request for it.
  52. if ($this->isShared($abstract) && ! $needsContextualBuild) {
  53. $this->instances[$abstract] = $object;
  54. }
  55.  
  56. $this->fireResolvingCallbacks($abstract, $object);
  57.  
  58. $this->resolved[$abstract] = true;
  59.  
  60. return $object;
  61. }

  

在讲解解析流程之前,我们先说说使用make函数进行解析的分类:

我们详细的讲一下上图。这里我把使用make函数进行解析的情况分为大致两种:

  • 解析对象没有绑定过任何类,例如:

  1. $app->make('App\Http\Controllers\HomeController');
  • 解析对象绑定过实现类

对于绑定过实现类的对象可以分为两种:

  • 绑定的是类名,例如:

  1. $app->when('App\Http\Controllers\HomeController')
  2. ->needs('App\Http\Requests\InRequest')
  3. ->give('App\Http\Requests\TestsRequest');
  • 绑定的是闭包

对于绑定的是闭包的又可以分为:

  • 用户绑定闭包,例如:

  1. $app->singleton('auth',function($app){
  2. return new AuthManager($app)
  3. });//对象类直接实现方法
  4.  
  5. $app->singleton(EloquentFactory::class, function ($app) {
  6. return EloquentFactory::construct(
  7. $app->make(FakerGenerator::class), database_path('factories')
  8. );//对象类依赖注入
  9. });
  • 服务容器外包一层闭包函数(make/build),例如:

  1. $app->singleton(
  2. Illuminate\Contracts\Http\Kernel::class,
  3. App\Http\Kernel::class
  4. );//包装make
  5.  
  6. $app->singleton(
  7. App\ConSole\Kernel::class,
  8. );//包装build

  

我们在这里先大致讲一下服务容器解析的流程,值得注意的是其中 build 函数有可能会递归调用 make:

  1. 获取服务名称。

  2. 加载延迟服务。判断当前的接口是否是延迟服务提供者,若是延迟服务提供者,那么还要对服务提供者进行注册与启动操作。

  3. 解析单例。如果接口服务是已经被解析过的单例对象,而且并非上下文绑定,那么直接取出对象。

  4. 获取注册的实现。实现方式可能是上下文绑定的,也可能是 binding 数组中的闭包,也有可能就是接口本身。

  5. build 解析。首先判断是否需要递归。是,则递归 make;否,则调用 build 函数;直到调用 build 为止

  6. 执行扩展。若当前解析对象存在扩展,运行扩展函数。

  7. 创造单例对象。若 shared 为真,且不存在上下文绑定,则放入单例数组中

  8. 回调

  9. 标志解析

下面我们开始详细分解代码逻辑。由于 getAlias 函数已经在 上一篇 讲过,这里不会再说。而loadDeferredProvider 函数作用是加载延迟服务,与容器解析关系不大,我们放在以后再说。

获取注册的实现

获取解析类的真正实现,函数优先去获取上下文绑定的实现,否则获取 binding 数组中的实现,获取不到就是直接返回自己作为实现:

  1. protected function getConcrete($abstract)
  2. {
  3. if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
  4. return $concrete;
  5. }
  6.  
  7. if (isset($this->bindings[$abstract])) {
  8. return $this->bindings[$abstract]['concrete'];
  9. }
  10.  
  11. return $abstract;
  12. }

  

一般来说,上下文绑定的服务是通过依赖注入来实现的:

  1. $this->app->when(PhotoController::class)
  2. ->needs(Filesystem::class)
  3. ->give(function () {
  4. return Storage::disk('local');
  5. });
  6.  
  7. class PhotoController{
  8. protected $file;
  9.  
  10. public function __construct(Filesystem $file){
  11. $this->file = $file;
  12. }
  13. }

  

服务容器会在解析 PhotoController 的时候,通过放射获取参数类型 Filesystem,并且会把 Filesystem 自动解析为 Storage::disk('local')。如何实现的呢?首先,从 上一篇 文章我们知道,当进行上下文绑定的时候,实际上是维护 contextual 数组,通过上下文绑定,这个数组中存在:

  1. contextual[PhotoController][Filesystem] = function () { return Storage::disk('local'); }

  

若是服务容器试图构造 PhotoController 类,那么由于其构造函数依赖于 Filesystem,所以容器必须先生成 Filesystem 类,然后再注入到 PhotoController 中。

在构造 Filesystem 之前,服务容器会先把 PhotoController 放入 buildStack 中,继而再去解析 Filesystem。

解析 Filesystem 时,运行 getContextualConcrete 函数:

  1. protected function getContextualConcrete($abstract)
  2. {
  3. if (! is_null($binding = $this->findInContextualBindings($abstract))) {
  4. return $binding;
  5. }
  6.  
  7. if (empty($this->abstractAliases[$abstract])) {
  8. return;
  9. }
  10.  
  11. foreach ($this->abstractAliases[$abstract] as $alias) {
  12. if (! is_null($binding = $this->findInContextualBindings($alias))) {
  13. return $binding;
  14. }
  15. }
  16. }
  17.  
  18. protected function findInContextualBindings($abstract)
  19. {
  20. if (isset($this->contextual[end($this->buildStack)][$abstract])) {
  21. return $this->contextual[end($this->buildStack)][$abstract];
  22. }
  23. }

  

从上面可以看出,getContextualConcrete 函数把当前解析的类(Filesystem)作为 abstract,buildStack 最后一个类(PhotoController)作为 concrete,寻找 this->contextual[concrete] [abstract] (contextual[PhotoController] [Filesystem])中的值,在这个例子里面这个数组值就是那个匿名函数。

build 解析

对于服务容器来说,绑定是可以递归的,例如:

  1. $app->bind('a','b');
  2. $app->bind('b','c');
  3. $app->bind('c',function(){
  4. return new C;
  5. })

  

遇到这样的情况,bind 绑定中 getClosure 函数开始发挥作用,该函数会给类包一层闭包,闭包内调用 make 函数,服务容器会不断递归调用 make 函数,直到最后一层,也就是绑定 c 的匿名函数。但是另一方面,有一些绑定方式并没有调用 bind 函数,例如上下文绑定 context:

  1. $this->app->when(E::class)
  2. ->needs(F::class)
  3. ->give(A::class);

  

当make(E::class)的时候,getConcrete 返回 A 类,而不是调用 make 函数的闭包,所以并不会启动递归流程得到 C 的匿名函数,所以造成 A 类完全无法解析,isBuildable 函数就是解决这种问题的,当发现需要解析构造的对象很有可能是递归的,那么就递归调用 make 函数,否则才会调用build。

  1. ...
  2. if ($this->isBuildable($concrete, $abstract)) {
  3. $object = $this->build($concrete);
  4. } else {
  5. $object = $this->make($concrete);
  6. }
  7. ...
  8.  
  9. protected function isBuildable($concrete, $abstract)
  10. {
  11. return $concrete === $abstract || $concrete instanceof Closure;
  12. }

  

执行扩展

获取扩展闭包,并运行扩展函数:

  1. protected function getExtenders($abstract)
  2. {
  3. $abstract = $this->getAlias($abstract);
  4.  
  5. if (isset($this->extenders[$abstract])) {
  6. return $this->extenders[$abstract];
  7. }
  8.  
  9. return [];
  10. }

  

回调

先后启动全局的解析事件回调函数,再启动针对类型的事件回调函数:

  1. protected function fireResolvingCallbacks($abstract, $object)
  2. {
  3. $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
  4.  
  5. $this->fireCallbackArray(
  6. $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
  7. );
  8.  
  9. $this->fireAfterResolvingCallbacks($abstract, $object);
  10. }
  11.  
  12. protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
  13. {
  14. $results = [];
  15.  
  16. foreach ($callbacksPerType as $type => $callbacks) {
  17. if ($type === $abstract || $object instanceof $type) {
  18. $results = array_merge($results, $callbacks);
  19. }
  20. }
  21.  
  22. return $results;
  23. }
  24.  
  25. protected function fireAfterResolvingCallbacks($abstract, $object)
  26. {
  27. $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
  28.  
  29. $this->fireCallbackArray(
  30. $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
  31. );

  

build 解析


make 函数承担了解析的大致框架,build 主要的职责就是利用反射将类构造出来,先看看主要代码:

  1. public function build($concrete)
  2. {
  3. // If the concrete type is actually a Closure, we will just execute it and
  4. // hand back the results of the functions, which allows functions to be
  5. // used as resolvers for more fine-tuned resolution of these objects.
  6. if ($concrete instanceof Closure) {
  7. return $concrete($this, $this->getLastParameterOverride());
  8. }
  9.  
  10. $reflector = new ReflectionClass($concrete);
  11.  
  12. // If the type is not instantiable, the developer is attempting to resolve
  13. // an abstract type such as an Interface of Abstract Class and there is
  14. // no binding registered for the abstractions so we need to bail out.
  15. if (! $reflector->isInstantiable()) {
  16. return $this->notInstantiable($concrete);
  17. }
  18.  
  19. $this->buildStack[] = $concrete;
  20.  
  21. $constructor = $reflector->getConstructor();
  22.  
  23. // If there are no constructors, that means there are no dependencies then
  24. // we can just resolve the instances of the objects right away, without
  25. // resolving any other types or dependencies out of these containers.
  26. if (is_null($constructor)) {
  27. array_pop($this->buildStack);
  28.  
  29. return new $concrete;
  30. }
  31.  
  32. $dependencies = $constructor->getParameters();
  33.  
  34. // Once we have all the constructor's parameters we can create each of the
  35. // dependency instances and then use the reflection instances to make a
  36. // new instance of this class, injecting the created dependencies in.
  37. $instances = $this->resolveDependencies(
  38. $dependencies
  39. );
  40.  
  41. array_pop($this->buildStack);
  42.  
  43. return $reflector->newInstanceArgs($instances);
  44. }

  

我们下面详细的说一下各个部分:

闭包函数执行

  1. if ($concrete instanceof Closure) {
  2. return $concrete($this, $this->getLastParameterOverride());
  3. }

  

这段代码很简单,但是作用很大。前面说过闭包函数有很多种类:

  • 用户绑定时提供的直接提供实现类的方式:

  1. $app->singleton('auth',function($app){
  2. return new AuthManager($app)
  3. });//对象类直接实现方法

  

这种情况 concrete(this) 直接就可以解析构造出具体实现类,服务容器解析完毕。

  • 用户绑定时提供的带有依赖注入的实现:

  1. $app->singleton(EloquentFactory::class, function ($app) {
  2. return EloquentFactory::construct(
  3. $app->make(FakerGenerator::class), database_path('factories')
  4. );//对象类依赖注入

  

这种情况下,concrete(this) 会转而去解析 FakerGenerator::class,递归调用 make 函数。

  • bind函数使用 getClosure 包装而来:

  1. function($container, $parameters = []){
  2. method = make/build;
  3. return $container->$method($concrete, $parameters);
  4. }

  

这种情况,concrete(this) 将会继续递归调用 make 或者 build。

反射

当 build 的参数是类名而不是闭包的时候,就要利用反射构建类对象,如果构建的类对象不需要依赖任何其他参数,那么:

  1. $reflector = new ReflectionClass($concrete);
  2. $constructor = $reflector->getConstructor();
  3. if (is_null($constructor)) {
  4. return new $concrete;
  5. }

  

如果需要依赖注入,那么就要用反射机制来获取 __construct 函数所需要注入的依赖,如果在make的时候带入参数值,那么直接利用传入的参数值;如果依赖是类对像,那么递归调用 make 函数;如果依赖是变量值,那么就从上下文中或者参数默认值中去获取:

  1. ...
  1. $dependencies = $constructor->getParameters();
  2. $instances = $this->resolveDependencies($dependencies);
  3. ...
  4.  
  5. protected function resolveDependencies(array $dependencies)
  6. {
  7. $results = [];
  8.  
  9. foreach ($dependencies as $dependency) {
  10.  
  11. if ($this->hasParameterOverride($dependency)) {
  12. $results[] = $this->getParameterOverride($dependency);
  13.  
  14. continue;
  15. }
  16.  
  17. $results[] = is_null($class = $dependency->getClass())
  18. ? $this->resolvePrimitive($dependency)
  19. : $this->resolveClass($dependency);
  20. }
  21.  
  22. return $results;
  23. }

  

解析变量值参数,如果变量值在上下文绑定中设置过,则去取上下文绑定的值,否则通过反射去取参数默认值,如果没有默认值,那么就要终止报错:

  1. protected function resolvePrimitive(ReflectionParameter $parameter)
  2. {
  3. if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->name))) {
  4. return $concrete instanceof Closure ? $concrete($this) : $concrete;
  5. }
  6.  
  7. if ($parameter->isDefaultValueAvailable()) {
  8. return $parameter->getDefaultValue();
  9. }
  10.  
  11. $this->unresolvablePrimitive($parameter);
  12. }
  13.  
  14. protected function hasParameterOverride($dependency)
  15. {
  16. return array_key_exists(
  17. $dependency->name, $this->getLastParameterOverride()
  18. );
  19. }
  20.  
  21. protected function getParameterOverride($dependency)
  22. {
  23. return $this->getLastParameterOverride()[$dependency->name];
  24. }
  25.  
  26. protected function getLastParameterOverride()
  27. {
  28. return count($this->with) ? end($this->with) : [];
  29. }

  

解析类参数,利用服务容器进行依赖注入:

  1. protected function resolveClass(ReflectionParameter $parameter)
  2. {
  3. try {
  4. return $this->make($parameter->getClass()->name);
  5. }
  6. catch (BindingResolutionException $e) {
  7. if ($parameter->isOptional()) {
  8. return $parameter->getDefaultValue();
  9. }
  10.  
  11. throw $e;
  12. }
  13. }

  

buildstack 解析栈

值的注意的是服务容器里面有个 buildStack,每次利用反射对参数进行依赖注入的时候,都要向这个数组中压入当前的解析对象,前面说过这部分是为了上下文绑定而设计的:

  1. ...
  2. $this->buildStack[] = $concrete;//压入数组栈中
  3. ...
  4. $instances = $this->resolveDependencies($dependencies);//解析依赖注入的参数
  5. array_pop($this->buildStack);//弹出数组栈
  6. ...

  

解析标签


使用标签绑定的类,将会使用 tagged 来解析:

  1. public function tagged($tag)
  2. {
  3. $results = [];
  4.  
  5. if (isset($this->tags[$tag])) {
  6. foreach ($this->tags[$tag] as $abstract) {
  7. $results[] = $this->make($abstract);
  8. }
  9. }
  10.  
  11. return $results;
  12. }

  

call方法注入


服务容器中,我们直接使用或者间接的使用 make 来构造服务对象,但是在实际的应用场景中,会有这样的需求:我们拥有一个对象或者闭包函数,想要调用它的一个函数,但是它函数里面却有其他类的参数,这个就需要进行 call 方法注入

  1. public function call($callback, array $parameters = [], $defaultMethod = null)
  2. {
  3. return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
  4. }

  

在 上一篇 文章中,我们说过,call 函数中的 callback 参数有以下几种形式:

  • 闭包 Closure

  • class@method

  • 类静态函数,class::method

  • 类静态函数: [ className/classObj, method ];类非静态函数: [ classObj, method ]

  • 若 defaultMethod 不为空,className
    首先,我们先看看 call 方法注入的流程图:

从流程图中我们可以看出来,虽然调用 call 的形式有 5 种,但是实际最终的形式是三种,第二种和第五种被转化为了第四种。
接下来,我们详细的解析源码:

call

先看一下 call 方法的主体:

  1. public static function call($container, $callback, array $parameters = [], $defaultMethod = null)
  2. {
  3. if (static::isCallableWithAtSign($callback) || $defaultMethod) {
  4. return static::callClass($container, $callback, $parameters, $defaultMethod);
  5. }
  6.  
  7. return static::callBoundMethod($container, $callback, function () use ($container, $callback, $parameters) {
  8. return call_user_func_array(
  9. $callback, static::getMethodDependencies($container, $callback, $parameters)
  10. );
  11. });
  12. }

  

可以看出来,call 方法注入主要有 4 个大的步骤:

  1. 对于 className@method 和 className-defaultMethod,实例化 className 为类对象,转化为 [ classObj, method ]。

  2. 判断 [ classObj / classname, method ] 是否存在被绑定的方法,如果有则调用。

  3. 利用服务容器解析依赖的参数。

  4. 调用 call_user_func_array。

实例化类对象

在这里 className@method 和 className-defaultMethod 两种情况被转化为 [ classObj, method ], className会被实例化为类对象,并重新调用 call:

  1. protected static function isCallableWithAtSign($callback)
  2. {
  3. return is_string($callback) && strpos($callback, '@') !== false;
  4. }
  5.  
  6. protected static function callClass($container, $target, array $parameters = [], $defaultMethod = null)
  7. {
  8. $segments = explode('@', $target);
  9.  
  10. $method = count($segments) == 2
  11. ? $segments[1] : $defaultMethod;
  12.  
  13. if (is_null($method)) {
  14. throw new InvalidArgumentException('Method not provided.');
  15. }
  16.  
  17. return static::call(
  18. $container, [$container->make($segments[0]), $method], $parameters
  19. );
  20. }

  

执行绑定方法

针对 [ className/classObj, method ], 调用被绑定的方法:

  1. protected static function callBoundMethod($container, $callback, $default)
  2. {
  3. if (! is_array($callback)) {
  4. return value($default);
  5. }
  6.  
  7. $method = static::normalizeMethod($callback);
  8.  
  9. if ($container->hasMethodBinding($method)) {
  10. return $container->callMethodBinding($method, $callback[0]);
  11. }
  12.  
  13. return value($default);
  14. }
  15.  
  16. protected static function normalizeMethod($callback)
  17. {
  18. $class = is_string($callback[0]) ? $callback[0] : get_class($callback[0]);
  19.  
  20. return "{$class}@{$callback[1]}";
  21. }
  22.  
  23. public function hasMethodBinding($method)
  24. {
  25. return isset($this->methodBindings[$method]);
  26. }
  27.  
  28. public function callMethodBinding($method, $instance)
  29. {
  30. return call_user_func($this->methodBindings[$method], $instance, $this);
  31. }

  

那么这个被绑定的方法 methodBindings 从哪里来呢,就是 上一篇 文章提的 bindMethod:

  1. public function bindMethod($method, $callback)
  2. {
  3. $this->methodBindings[$method] = $callback;
  4. }

  

从上面可以看出来,methodBindings 中 callback 参数一定是 classname@method 形式的。

实例化依赖

这一步就要通过反射来获取函数方法需要注入的参数类型,然后利用服务容器对参数类型进行解析构建:

  1. protected static function getMethodDependencies($container, $callback, array $parameters = [])
  2. {
  3. $dependencies = [];
  4.  
  5. foreach (static::getCallReflector($callback)->getParameters() as $parameter) {
  6. static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
  7. }
  8.  
  9. return array_merge($dependencies, $parameters);
  10. }

  

getCallReflector 函数利用反射来获取参数类型,值得注意的是class::method是需要拆分处理的:

  1. protected static function getCallReflector($callback)
  2. {
  3. if (is_string($callback) && strpos($callback, '::') !== false) {
  4. $callback = explode('::', $callback);
  5. }
  6.  
  7. return is_array($callback)
  8. ? new ReflectionMethod($callback[0], $callback[1])
  9. : new ReflectionFunction($callback);
  10. }

  

利用传入的参数,利用服务容器构建解析参数类型,或者获取参数默认值:

  1. protected static function addDependencyForCallParameter($container, $parameter,
  2. array &$parameters, &$dependencies)
  3. {
  4. if (array_key_exists($parameter->name, $parameters)) {
  5. $dependencies[] = $parameters[$parameter->name];
  6.  
  7. unset($parameters[$parameter->name]);
  8. } elseif ($parameter->getClass()) {
  9. $dependencies[] = $container->make($parameter->getClass()->name);
  10. } elseif ($parameter->isDefaultValueAvailable()) {
  11. $dependencies[] = $parameter->getDefaultValue();
  12. }
  13. }

  

call_user_func_array

关于这个函数可以参考 Laravel学习笔记之Callback Type

 
  1. call_user_func_array(
  2. $callback, static::getMethodDependencies($container, $callback, $parameters)
  3. );

  

本文转自:https://segmentfault.com/a/1190000009402317#articleHeader10

Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器解析)的更多相关文章

  1. Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器绑定)

    服务容器的绑定 bind 绑定 bind 绑定是服务容器最常用的绑定方式,在 上一篇文章中我们讨论过,bind 的绑定有三种: 绑定自身 绑定闭包 绑定接口 今天,我们这篇文章主要从源码上讲解 Ioc ...

  2. Laravel开发:Laravel核心——Ioc服务容器

    服务容器 在说 Ioc 容器之前,我们需要了解什么是 Ioc 容器. Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具. 在理解这句话之前,我们需要先了解一下服务容器的来龙去脉:  ...

  3. 核心概念 —— 服务容器

    1.简介 Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具.依赖注入听上去很花哨,其实质是通过构造函数或者某些情况下通过 set 方法将类依赖注入到类中. 让我们看一个简单的例子: ...

  4. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  5. Laravel框架下路由的使用(源码解析)

    本篇文章给大家带来的内容是关于Laravel框架下路由的使用(源码解析),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 我的解析文章并非深层次多领域的解析攻略.但是参考着开发文 ...

  6. 最简 Spring IOC 容器源码分析

    前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...

  7. 小白都能看懂的Spring源码揭秘之IOC容器源码分析

    目录 前言 IOC 只是一个 Map 集合 IOC 三大核心接口 IOC 初始化三大步骤 定位 加载 注册 总结 前言 在 Spring 框架中,大家耳熟能详的无非就是 IOC,DI,Spring M ...

  8. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  9. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

随机推荐

  1. javascript快速入门6--Script标签与访问HTML页面

    Script标签 script标签用于在HTML页面中嵌入一些可执的脚本 <script> //some script goes here </script> script标签 ...

  2. min-height IE6的解决方案

    selector { min-height:500px; height:auto !important; height:500px; } 因为IE6中当内容大于容器高度或宽度时,就会撑破容器.并且在同 ...

  3. IDEA+MAVEN+testNG(reportNG)

    转载:http://www.cnblogs.com/aikachin/p/7765846.html 参考: http://blog.csdn.net/langsand/article/details/ ...

  4. python——异常except语句用法与引发异常

    except: #捕获所有异常 except: <异常名>: #捕获指定异常 except:<异常名1,异常名2):捕获异常1或者异常2 except:<异常名>,< ...

  5. servlet--百度百科

    Servlet(Server Applet),全称Java Servlet, 未有中文译文.是用Java编写的服务器端程序.其主要功能在于交互式地浏览和修改数据,生成动态Web内容.狭义的Servle ...

  6. 一个我用来上传代码到Github的 Shell 脚本

    因为用git老是要敲许多命令.所以写了个小脚本.代码如下: #! /bin/sh echo Going to simpleWebtest... cd ~/softwaredevelopment/wor ...

  7. jumpserverv0.5.0 基于 CentOS7安装部署

    基于 CentOS 7 一步一步安装 Jumpserver 0.5.0 环境 系统: CentOS 7 IP: 192.168.244.144 关闭 selinux和防火墙 # CentOS 7 $ ...

  8. Redis之ZSet命令

    0.前言 Redis有序集合ZSet可以按分数进行排序, 存储结构可能使用ziplist,skiplist和hash表, zset_max_ziplist_entries和zset_max_zipli ...

  9. YUV格式详细解释与FFMPEG的关系

    YUV主要的采样格式 主要的采样格式有YCbCr 4:2:0.YCbCr 4:2:2.YCbCr 4:1:1和 YCbCr 4:4:4.其中YCbCr 4:1:1 比较常用,其含义为:每个点保存一个 ...

  10. IE6下css常见bug处理

    1.双倍边距 如下图所示,一个样式里面既设定了“float:left:”又有“margin-left:100px:”的情况,就呈现了双倍情况.如外边距设置为100px, 而左侧则呈现出200px,解决 ...