What is dependency injection?
Dependency Injection (Dependency injection) is one of the design patterns. The name is more abstract, but the problem to be solved is quite clear. For a given application, it is necessary to use some relatively separate components to complete the function. In general, the process of using these components is included in the application's logical statements. The problem is that when these components want to make plug-in functionality similar to the application's business logic, they can arbitrarily change the implementation of the component. This flexibility depends on how the application assembles these components. If an application relies on these components, dependency injection is a way of separating these dependencies from the logic of the application and putting them into the implementation of the plug-in.
A simple example is a user class table class that relies on a finder class to generate this list. And this finder class relies on a db to get the data. The program may write this, regardless of the dependency injection:
1 $db = new \yii\db\connection ([' DSN ' = ' ... ' 2 $finder = new userfinder ( $db ); 3 $lister = new userlister ( $finder );
However, when the implementation of the DB changes, it causes the application to change, and when the implementation of the Finder class changes, it causes the application to change. If Db,finder is considered a plug-in, a mechanism is needed to tell the application that Lister relies on the Finder and DB.
The basic rationale for dependency injection is to use a separate object as the assembler, which is provided to the application using an appropriate instance . There are three types of dependency injection,
- Constructor injection: The implementation of the component is provided in the parameters of the constructor, and the implementation is cured into the application in the constructor.
- Setter injection: Specifically through the setter function to achieve the above injection,
- Interface injection: Through the implementation of the interface, to achieve dependency injection.
For more information on dependency injection, please refer to Martin Flower 2004 article Inversion of Control Containers and the Dependency injection pattern
Dependency Injection in the YII2
In Yii, we can find the first and second types of applications, namely constructor injection and setter injection. The assembler in YII2 is the container class (Container). Dependency declarations and dependency injection are implemented by invoking the set and get methods of the container class.
The whole process is divided into three steps,
- The first step is the declaration of the class, in the constructor of the class, declaring the dependency relationship.
- The second part is the loading of the container parameters, which tells the container what parameters to use to generate an object, and what classes to implement. This process is done by calling set.
- Acquisition of the third instance. Call the container's get function to get the newly created instance.
This is probably the case with containers to implement the above list of users.
1 //Create a container2 $container=NewContainer;3 4 //Specifies how yii\db\connection is generated, where Yii\db\connection is the name of the class5 $container->set (' Yii\db\connection ', [6' DSN ' = ' ... ',7 ]);8 9 //Specifies how app\models\userfinderinterface is generated, and the class used is UserfinderTen $container->set (' App\models\userfinderinterface ', [ One' Class ' = ' App\models\userfinder ', A ]); - - //Specifies how Userlister is generated the $container->set (' Userlister ', ' app\models\userlister '); - - //Generate Lister - $lister=$container->get (' Userlister ');
It seems that the last generation of Lister has nothing to do with the previous DB and Userfinderinterface. The secret here is that each class's constructor implicitly declares a dependency between classes.
//first, declare an interface class InterfaceUserfinderinterface {functionFinduser (); } //Userfinder implements this interface class classUserfinderextends Object ImplementsUserfinderinterface { Public $db; //Note that the first argument is a connection $db that contains the name of the class Public function__construct (Connection$db,$config= []) { $this->db =$db; Parent:: __construct ($config); } Public functionFinduser () {}}//The Userlists class, the first parameter of a constructor declares that it relies on userfinderinterface. classUserlisterextends Object { Public $finder; Public function__construct (userfinderinterface$finder,$config= []) { $this->finder =$finder; Parent:: __construct ($config); } }
The role of the container is
-
- Set, which indicates the generation class and parameters of the instance
- In Get, the dependency is analyzed according to the constructor, and the corresponding object is generated according to the dependency.
internal implementation of the container class:
Data structures: Container classes have several more important internal arrays
/** * @var array Singleton objects indexed by their types*/ Private $_singletons= []; /** * @var Array object definitions indexed by their types*/ Private $_definitions= []; /** * @var Array constructor parameters indexed by object types*/ Private $_params= []; /** * @var Array cached Reflectionclass objects indexed by class/interface names*/ Private $_reflections= []; /** * @var Array cached dependencies indexed by class/interface names. Each class name is associated with a list of constructor parameter types or default values. */ Private $_dependencies= [];
important functions of the container:
Public function set ($class, $definition = [], array $params = [])
Public function Get ($class, $params = [], $config = [])
the function of the SET function is to register a class in a container so that the container knows how to find dependent information when it generates an instance of a class. The SET function can register a class name, an interface name, and an alias. When registering, you can also specify the appropriate configuration information (the purpose of the configuration information is to assign this information to the generated class instance)
Basically, the first parameter is the key value, which can be the name of the class, the name of the interface, and an alias. The second parameter is a definition, which can be a string, such as the name of a class, the name of an interface, or an alias, an array that represents the appropriate configuration information, or a PHP callable object whose parameters are function ($container, $params, config). The object is executed at the time of the Get () call. $params is the parameter of the constructor, $config is the configuration information of the object, which is often the array of numbers. And $container is the container object. The return value is the generated object, returned by Get (). The third parameter of the SET function is the argument supplied to the constructor when the object is generated.
As can be seen from the source code of the Set function, the first parameter, the class, is the key value of several key arrays mentioned earlier. The definition is placed in the _definitions, the parameters are placed in the _params,
Public functionSet$class,$definition= [],Array $params= []) { $this->_definitions[$class] =$this->normalizedefinition ($class,$definition); $this->_params[$class] =$params; unset($this->_singletons[$class]); return $this; }
The get function is responsible for generating an object instance of the response based on the dependencies set by the set. Where the first parameter class is used to access the key values of the different arrays in the container, the second parameter is $params, which is supplied to the class's constructor after merging with the params provided in set. The third parameter, config, is the configuration information (the configuration is to assign properties to the newly generated object, and the parameter is the constructor of the class that handles the parameters to be processed)
The Get function first extracts the definition from the _definition, and, depending on its type, does different processing, for example, if it is a function, then after merging the parameters, call the function provided by the set.
The newly created object instance is built by calling the build method. In this build function, the first step is to obtain a dependency. So where does this dependency come from?
relies on reflection analysis from the constructor of a class.
The method is to create a reflection object based on the namespace of the class, get the constructor, and analyze the parameters of the constructor one after the other. When a parameter has a default value, the default value is logged to the dependency. If a parameter has a class, it generates a instance object based on the name of the class, records the name of the class, and prepares for subsequent parsing of dependencies.
Generated reflection information put to _ Reflection Array, the dependency is placed in the _dependency array. Dependency needs to be resolved, the process of parsing is the process of generating an instance, recursively calling get.
processed dependent arrays, as parameters, To the newinstanceargs of the reflected object, thus generating an instance of the class. The essence of this is to instantiate a parameter with the instruction of the class, and the instantiation is a predefined method based on the set function.
The whole process described above is the process of using container to implement dependency injection in Yii2. As you can see, the injection of dependency is implemented by analyzing the constructor parameters of the class.
Dependency Injection usage:
In Yii, the configuration of the main program uses this container-based dependency injection.
The component in the config array in yii/config/web.php is the definition of the specified element. In application's preinit approach, the user-specified components are fused with the core components of the system.
These components can be accessed through \yii:: $app->componentid, such as \yii:: $app->cache. ( This is part of the service locator concept ) when the component is instantiated at first access through the Yii::createobject static function, the instantiation process follows the process of dependency injection as stated above.
Of course, if you specify that you want to bootstrap a component, such as the following. This will instantiate the log component when each request comes in.
' Bootstrap ' = [ ' log ', ],),
The following is an example of what is in components in the configuration.
' Components ' = [ ' Request ' = [ //!!! Insert a secret key in the following (if it was empty)-this is required by cookie validation' Cookievalidationkey ' = ' ... ', ], ' cache ' = [ ' Class ' = ' Yii\caching\filecache ', ], ' user ' = [ ' Identityclass ' = ' app\models\user ', ' Enableautologin 'true, ], ' errorhandler ' = [ ' ErrorAction ' = ' site/error ', ], ' mailer ' = [ ' Class ' = ' Yii\swiftmailer\mailer ',//send all mails to a file by default. You has to set//' Usefiletransport ' to false and configure a transport// For the mailer to send real emails.' Usefiletransport ' =true, ], ' log ' = [ ' TraceLevel ' = Yii_debug? 3:0, ' targets ' = [ [ ' Class ' = ' yii\log\filetarget ', ' levels ' and ' = ' error ', ' warning ', ' profile ', ], ], ], ' db ' = =require(__dir__. '/db.php '),
YII2 Learning Note 01--Application of dependency injection in YII2