注解注入的实现

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);

/**
 * MyInject类
 */
#[Attribute(Attribute::TARGET_PROPERTY)] // 声明MyInject类是注解类
class MyInject
{
}

/**
 * Human类
 */
class Human
{
    private string $name; // 姓名

    #[MyInject] // 使用注解注入对象,无需手动创建对象即可直接使用
    private Mobile $mobile; // Mobile对象

    /**
     * 构造方法
     *
     * @param string $name 姓名
     */
    public function __construct(string $name)
    {
        $this->name = $name;
    }

    /**
     * 打电话
     *
     * @param string $num 通话目标号码
     * @return void
     */
    public function call(string $num): void
    {
        echo "{$this->name}即将打电话" . PHP_EOL;
        $this->mobile->dial($num);
    }
}

/**
 * 手机类
 */
class Mobile
{
    /**
     * 拨号
     *
     * @param string $num 号码
     * @return void
     */
    public function dial(string $num): void
    {
        echo "手机拨号:$num" . PHP_EOL;
    }
}

// 要调用方法(类名+方法名)
$class = 'Human';
$method = 'call';

// 相关参数
$name = '张三';
$num = '18888888888';

// 反射Human类
try {
    $reflectionClass = new ReflectionClass($class);
} catch (ReflectionException $e) {
    exit('ReflectionException: ' . $e->getMessage());
}

// 创建Human类实例(对象)
$humanInstance = $reflectionClass->newInstanceArgs([$name]);

$properties = $reflectionClass->getProperties(); // 获取Human类的所有属性并遍历
foreach ($properties as $property) {
    $attributes = $property->getAttributes(); // 获取属性的所有注解(单个属性可以有多个注解),如果属性有注解则处理
    if ($attributes) {
        // 开始遍历该属性的所有注解并执行注解对应操作
        foreach ($attributes as $attribute) {
            // 注解名为MyInject,说明该属性需要注入对象
            if ($attribute->getName() === 'MyInject') {
                // 获取属性的类名
                $typename = $property->getType()->getName(); // string(6) "Mobile"

                // 通过类名创建实例(对象),也是使用反射创建,而不是通过new关键字创建
                try {
                    $rc = new ReflectionClass($typename);
                } catch (ReflectionException $e) {
                    exit('ReflectionException: ' . $e->getMessage());
                }
                $instance = $rc->newInstance();

                // 【注解注入核心代码】设置Human实例(对象)的$mobile属性值
                $property->setValue($humanInstance, $instance);
            }
        }
    }
}

// 通过反射调用方法
try {
    $reflectionMethod = $reflectionClass->getMethod($method);
} catch (ReflectionException $e) {
    exit('ReflectionException: ' . $e->getMessage());
}
$reflectionMethod->invokeArgs($humanInstance, [$num]);
// 张三即将打电话
// 手机拨号:18888888888


//========== 总结 ==========//
// 1、注解注入可以让代码变得很简洁,当一个类有大量依赖类时不再需要每个依赖类都手动创建对象,只要加上注解即可直接使用对象。
// 2、注解因为要使用反射来解析,所以执行效率会稍低。

Copyright © 2024 码农人生. All Rights Reserved