guanhui07 / blog

把博客迁移到这了
https://guanhui07.github.io/blog/
98 stars 31 forks source link

再看 laravel 容器和依赖注入 #814

Open guanhui07 opened 1 year ago

guanhui07 commented 1 year ago

Laravel 容器(Container)是 Laravel 中一个核心组件,它负责管理应用程序中 所有的类的实例。Laravel 容器使用依赖注入(Dependency Injection)的方式 来管理类的实例,它能够自动根据类的依赖关系来创建和注入实例。

在 Laravel 中,容器是一个全局的服务容器,它可以在应用程序 的任何地方被访问。它有两种使用方式: 第一 使用 app() 函数访问容器:

第二种 使用容器类型提示和自动注入

$user = app('App\User'); public function store(Request $request, User $user) { // 使用 $user 变量 }

容器的实现原理主要是通过 Reflection API 和注解来实现的。 Reflection API 是 PHP 的反射 API,它可以获取类的定义信息, 比如类的属性、方法、构造函数等。Laravel 容器会使用 Reflection API 来分析类的定义,并根据定义来创建实例。

依赖注入(Dependency Injection)是一种软件设计模式,它允许一个类或函数(称为客户端)在运行时指定它所依赖的类或函数(称为服务)。这样做的好处是可以让客户端与服务之间解耦,提高了程序的可测试性和可维护性。

举个例子,假设你要开发一个用户管理系统,那么你可能会有一个 UserController 类,用来处理用户的添加、删除、查询等操作。如果你不使用依赖注入,你可能会这样写:

友好的服务容器

class UserController
{
    public function addUser()
    {
        // 为了添加用户,我们需要实例化一个 UserRepository 类
        $userRepository = new UserRepository();

        // 添加用户
        $userRepository->add(...);
    }
}

上面的代码中,UserController 类直接依赖于 UserRepository 类,并且在类内部通过实例化一个对象来使用 UserRepository 类的方法。这样做的问题是,如果我们要修改用户管理系统,比如将 UserRepository 类替换为一个新的类,那么我们就需要更改 UserController 类的代码,这样就会导致客户端与服务之间的耦合度变高。

如果你使用依赖注入,你可以这样写

使用依赖注入的好处是可以让客户端与服务之间解耦,提高了程序的可测试性和可维护性。另外,使用依赖注入可以让你的代码更加灵活,因为你可以在运行时指定客户端所依赖的服务,而不是在编写代码时就确定下来。这样做的好处是可以让客户端与服务之间解耦,提高了程序的可测试性和可维护性。

依赖注入一般是通过构造函数或者 setter 方法来实现的。例如,如果你要实现一个 UserController 类,并通过构造函数注入 UserRepository 类,你可以这样写:

class UserController
{
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function addUser()
    {
        // 添加用户
        $this->userRepository->add(...);
    }
}

上面的代码中,我们通过构造函数来注入 UserRepository 类,并将其存储在一个私有属性中。这样做的好处是可以让客户端与服务之间解耦,提高了程序的可测试性和可维护性。

总之,依赖注入是一种软件设计模式,它允许客户端在运行时指定它所依赖的服务,从而提高程序的可测试

性和可维护性。依赖注入一般是通过构造函数或者 setter 方法来实现的,这样做的好处是可以让客户端与服务之间解耦,提高了程序的可测试性和可维护性。

举个例子,假设你有一个名为 UserService 的类,它用于处理用户的登录、注册等操作,你可以这样定义这个类:

class UserService
{
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function login($username, $password)
    {
        // 使用 UserRepository 类来处理用户登录操作
        $user = $this->userRepository->findByUsername($username);
        if (!$user) {
            throw new \Exception('用户不存在');
        }
        if ($user->password != $password) {
            throw new \Exception('密码错误');
        }
        return $user;
    }

    public function register($username, $password)
    {
        // 使用 UserRepository 类来处理用户注册操作
        $user = $this->userRepository->findByUsername($username);
        if ($user) {
            throw new \Exception('用户名已被占用');
        }
        $user = new User();
        $user->username = $username;
        $user->password = $password;
        $this->userRepository->add($user);
        return $user;
    }
}

上面的代码中,我们通过构造函数注入了 UserRepository 类,并将其存储在私有属性中。然后,在 login 方法和 register 方法中,我们分别使用 UserRepository 类来处理用户登录和注册操作。这样做的好处是可以让客户端与服务之 间解耦,提高了程序的可测试性和可维护性。

例如,如果你要测试 UserService 类的 login 方法,你可以这样写:

class UserServiceTest extends TestCase
{
    public function testLogin()
    {
        // 创建一个模拟的 UserRepository 类
        $userRepository = Mockery::mock(UserRepository::class);

        // 设置模拟 UserRepository 类的 findByUsername 方法的返回值
        $userRepository->shouldReceive('findByUsername')
                       ->with('foo')
                       ->andReturn(new User('foo', 'bar'));

        // 实例化 UserService 类,并将模拟的 UserRepository 类注入
        $userService = new UserService($userRepository);

        // 测试 login 方法
        $user = $userService->login('foo', 'bar');
        $this->assertEquals('foo', $user->username);
    }
}

上面的代码中,我们创建了一个模拟的 UserRepository 类,并设置其 findByUsername 方法的返回值。然后,我们实例化 UserService 类,并将模拟的 UserRepository 类注入,从而实现了对 UserService 类的测试。

总之,依赖注入是一种软件设计模式,它允许客户端在运行时指定它所依赖的服务,从而提高程序的可测试性和可维护性。它通常是通过构造函数或者 setter 方法来实现的,并且可以让你的代码更加灵活,因为你可以在运行时指定客户端所依赖的服务。

laravel依赖注入原理 用中文 底层是反射

Laravel的依赖注入(Dependency Injection)是一种设计模式,它的目的是通 过将对象的依赖关系通过构造函数或方法参数的方式传递给对象,而不是通过在对象 内部直接实例化依赖对象的方式来实现松耦合。这样做的好处是可以更容易地测试 对象,并且可以更方便地替换和管理对象之间的依赖关系。

依赖注入的具体实现方式是:首先定义一个接口或抽象类,该接口或抽象类定义 了所需依赖对象的一些基本操作,然后在需要该依赖对象的类中通过构造函数或 方法参数的方式将该接口或抽象类作为参数传递给该类,最后在该类中实例化 一个实现了该接口或抽象类的具体对象即可。在 Laravel 中,这些依赖对 象会自动被容器管理,所以可以通过调用容器的 make 方法来自动获取所需的依赖对象。

在 Laravel 中,依赖注入的具体实现方式如下:

首先定义一个数据库连接接口,例如:

interface ConnectionInterface
{
    public function connect();
}

然后在 UserRepository 类的构造函数中通过参数的方式将该接口作为参数传递给 UserRepository 类,例如:

class UserRepository
{
    private $connection;

    public function __construct(ConnectionInterface $connection)
    {
        $this->connection = $connection;
    }
}

实现该接口,并在实现类中实现 connect 方法,例如:

class MySQLConnection implements ConnectionInterface
{
    public function connect()
    {
        // 实现数据库连接的具体操作
    }
}

在使用 UserRepository 类的地方,通过容器的 make 方法获取一个 MySQLConnection 对象,并将其作为参数传递给 UserRepository 类的构造函数,例如:

$userRepository = new UserRepository(app()->make(MySQLConnection::class));

这样,就完成了一个简单的依赖注入实例。在这个例子中,UserRepository 类与 MySQLConnection 类之间解除了耦合,并且 UserRepository 类可以通过构 造函数参数的方式来获取所需的 MySQLConnection 对象,从而更容易测试和维护。

Laravel 依赖注入的基本原理是通过将依赖关系通过构造函数或方法参数的方式传递

Laravel 依赖注入有以下几个优点:

可以更容易地测试对象。通过依赖注入,可以在测试时通过传递模拟依赖对象来测试 对象的行为,而不需要真正连接数据库,这样可以提高测试的速度和准确度。

更容易管理对象之间的依赖关系。通过依赖注入,可以在应用程序的启动时自动将依 赖对象注册到容器中,并在需要时通过容器自动获取依赖对象,这样可以更方便地管 理对象之间的依赖关系。

更容易替换依赖对象。通过依赖注入,可以在不修改依赖对象使用者的情况下替换依 赖对象,比如可以在不同的环境下使用不同的数据库连接对象,这样可以提高应用程序的灵活性和可扩展性。

通过依赖注入,可以更容易地测试对象,更容易管理对象之间的依赖关系,并更容 易替换依赖对象,这些优点都有助于提高应用程序的质量和可维护性。在 Laravel 中,通过依赖注入,

在 Laravel 中,容器负责管理依赖注入,可以通过调用容器的 make 方法来自动获取所需的依赖对象。

容器的 make 方法接受一个类名或接口名作为参数,并返回一个该类或接口的实例。例如,假设我们 已经定义了一个 MySQLConnection 类,可以这样调用容器的 make 方法来获取一个 MySQLConnection 对象:

$connection = app()->make(MySQLConnection::class);

容器的 make 方法还支持传递参数的方式来注入依赖,例如,假设我们要获取 一个 UserRepository 对象,并需要向 UserRepository 的构造函 数传递一个数据库连接对象,则可以这样调用容器的 make 方法来获取 UserRepository 对象:

$userRepository = app()->make(UserRepository::class, [
    'connection' => app()->make(MySQLConnection::class),
]);

通过容器的 make 方法,可以自动获取所需的依赖对象,并将其注入到需要使用的对象中, 这样可以避免手动实例化依赖对象,提高代码的可读性和可维护性。

Laravel 依赖注入的具体实现方式是:首先定义一个接口或抽象类, 然后在需要该依赖对象的类中通过传参,底层反射

Laravel 依赖注入的基本原理是通过将依赖关系通过构造函数或方法参数的方 式传递给对象,而不是通过在对象内部直接实例化依赖对象的方式来实现松耦合。

通过依赖注入,可以更容易地测试对象,更容易管理对象之间的依赖关系,并 更容易替换依赖对象,这些优点都有助于提高应用程序的质量和可维护性。 在 Laravel 中,容器负责管理依赖注入,可以通过调用容器的 make 方法 来自动获取所需的依赖对象,并将其注入到需要使用的对象中。

反射的具体实现方式是通过 PHP 的反射类来实现的。PHP 的反射类主要包括 ReflectionClass、ReflectionMethod、ReflectionProperty 等 几个类,分别用于获取类、方法、属性等程序元素的信息。


<?php
class MyClass
{
    public function myMethod()
    {
        echo "Hello World!";
    }
}

// 代理类
class Proxy
{
    private $target;

    public function __construct($target)
    {
        $this->target = $target;
    }

    public function __call($name, $arguments)
    {
        $method = new ReflectionMethod($this->target, $name);
        $method->invoke($this->target, ...$arguments);
    }
}

// 使用代理
$proxy = new Proxy(new MyClass());
$proxy->myMethod();