因为Laravel 哲学的一个重要组成部分就是 IoC 容器,也可以称为服务容器。它是一个 Laravel 应用的核心部分,因此理解并使用 IoC 容器是我们必须掌握的一项重要技能。IoC 容器是一个非常强大的类管理工具。它可以自动解析类。
一、为什么我们需要Laravel IoC容器
因为Laravel 哲学的一个重要组成部分就是 IoC 容器,也可以称为服务容器。它是一个 Laravel 应用的核心部分,因此理解并使用 IoC 容器是我们必须掌握的一项重要技能。IoC 容器是一个非常强大的类管理工具。它可以自动解析类。
首先,我想先谈下依赖反转原则,对它的了解会有助于我们更好地理解 IoC 容器的重要性。
该原则规定:
高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。
一言以蔽之:依赖于抽象而非具体
class MySQLConnection
{
/**
* 数据库连接
*/
public function connect()
{
var_dump(‘MYSQL Connection’);
}
}
class PasswordReminder
{
/**
* @var MySQLConnection
*/
private $dbConnection;
public function __construct(MySQLConnection $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
大家常常会有一个误解,那就是依赖反转就只是依赖注入的另一种说法。但其实二者是不同的。在上面的代码示例中,尽管在 PasswordReminder 类中注入了 MySQLConnection 类,但它还是依赖于 MySQLConnection 类。
然而,高层次模块 PasswordReminder 是不应该依赖于低层次模块 MySQLConnection 的。
如果我们想要把 MySQLConnection 改成 MongoDBConnection,那我们就还得手动修改 PasswordReminder 类构造函数里的依赖。
PasswordReminder 类应该依赖于抽象接口,而非具体类。那我们要怎么做呢?请看下面的例子:
interface ConnectionInterface
{
public function connect();
}
class DbConnection implements ConnectionInterface
{
/**
* 数据库连接
*/
public function connect()
{
var_dump(‘MYSQL Connection’);
}
}
class PasswordReminder
{
/**
* @var DBConnection
*/
private $dbConnection;
public function __construct(ConnectionInterface $dbConnection)
{
$this->dbConnection = $dbConnection;
}
}
通过上面的代码,如果我们想把 MySQLConnection 改成 MongoDBConnection,根本不需要去修改 PasswordReminder 类构造函数里的依赖。因为现在 PasswordReminder 类依赖的是接口,而非具体类。
现在我要讲下 IoC 容器里到底发生了什么。我们可以把 IoC 容器简单地理解为就是一个容器,里面装的是类的依赖。
OrderRepositoryInterface 接口:
namespace App\Repositories;
interface OrderRepositoryInterface
{
public function getAll();
}
DbOrderRepository 类:
namespace App\Repositories;
class DbOrderRepository implements OrderRepositoryInterface
{
function getAll()
{
return ‘Getting all from mysql’;
}
}
OrdersController 类:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Repositories\OrderRepositoryInterface;
class OrdersController extends Controller
{
protected $order;
function __construct(OrderRepositoryInterface $order)
{
$this->order = $order;
}
public function index()
{
dd($this->order->getAll());
return View::make(orders.index);
}
}
路由:
Route::resource(‘orders’, ‘OrdersController’);
现在,在浏览器中输入这个地址 <http://localhost:8000/orders>
报错了吧,错误的原因是服务容器正在尝试去实例化一个接口,而接口是不能被实例化的。解决这个问题,只需把接口绑定到一个具体的类上。
把下面这行代码加在路由文件里就搞定了:
App::bind(‘App\Repositories\OrderRepositoryInterface’, ‘App\Repositories\DbOrderRepository’)
延伸阅读:
二、控制反转和依赖注入是什么
控制反转,是指对象的创建和配置的控制权从调用方转移给容器。好比在家自己做菜,菜的味道全部由自己控制;去餐馆吃饭,菜的味道则是交由餐馆控制。IoC 容器就担任了餐馆的角色。
有了 IoC 容器,我们可以将对象交由容器管理,交由容器管理后的对象称之为 Bean。调用方不再负责组件的创建,要使用组件时直接获取 Bean 即可:
@Component
public class UserServiceImpl implements UserService{
@Autowired // 获取 Bean
private UserDao userDao;
}
调用方只需按照约定声明依赖项,所需要的 Bean 就自动配置完毕了,就好像在调用方外部注入了一个依赖项给其使用,所以这种方式称之为 依赖注入(Dependency Injection,缩写为 DI)。控制反转和依赖注入是一体两面,都是同一种开发模式的表现形式。