PHP Swoft2 框架精华系列:Annotation 注解机制详解

Annotation 注解机制

概述

说到注解,这里不得不提到注释。写过程序的人都知道,注释,是用来给类、接口、方法、变量等做解释用的一种机制,用来给阅读程序的人提供提醒备忘的作用。那么注解又是用来干什么的呢。注解的作用,是在保持原注释格式的基础之上,实现了一种更高级的用法或者说作用。其具体作用如下:

  1. 功能的复用

    类似于函数,就是为了将部分重复的逻辑代码进行复用,以减少代码的冗余,提供程序修改的便利。注解,也是,同样的注解,提供的同样的功能作用

  2. 提供元数据

    注解运行的结果,主要是给业务系统(框架)提供必需的元数据,以便框架能够正常运行

  3. 解耦合

    注解在解析过程中,可以提供其他的一些组件所必须的数据,这样不用提供硬性编码,通过注解功能,将不同的组件的依赖关系进行解耦合。

运行阶段

注解的解析主要出于框架的启动阶段,启动阶段不同于运行阶段,虽然也执行代码,但是启动过程中,整个业务系统所需要的必须组件并未初始化完成,还不足以使业务系统能够正常运行,处理业务请求。

  • 程序启动阶段

    获取系统元数据,根据元数据的依赖关系,组件依赖关系,依次初始化业务系统运行所需要的必须组件。

  • 程序运行阶段

    提供正常的业务请求处理能力。

  • 程序结束阶段

    停止接收新的业务请求,并处理完当前系统中已经接收的所有请求。处理完毕后,依次回收(释放)系统组件所占用的资源。

注解的解析,就处在程序启动阶段,启动过程中,注解核心组件先进行实例化,然后对系统中的所有代码进行扫描,将代码存在的所有注解信息一一收集,并且依次对注解进行解析。解析过程中,有些注解可能会注册一些其他组件依赖的一些关键数据(如:@RequestMapping 在解析过程中提供了路由信息)有些注解会收集部分数据,并且形成一个对象的实例(如:@Controller 注解会生成控制器的单例)存储到(IOC支持的)容器中。以上是注解的一般用法。

注解参数

注解同样可以使用参数,使有限的逻辑能够最大化的复用,效果也类似带参数的函数。

注解作用域

与原注释功能基本保持一致,一般有:

  • 类注解(针对类的注解)
  • 属性注解(针对类属性的注解)
  • 方法注解(针对方法的注解)

源码解析

注解组成
  • 注解对象
  • 注解解析器对象
  • 注解解析器核心组件 src/Definition/Parser/AnnotationObjParser.php
注解对象

需要包含三个注解:

  • @Annotation:表示此类是一个注解对象
  • @Target:可以跟多个,表示注解可以标注的位置为多个
    • CLASS:表示类注解
    • METHOD: 表示成员方法注解
    • PROPERTY:表示属性注解
    • FUNCTION:暂无说明
    • ANNOTATION:暂无说明
  • @Attributes:表示此注解对象中含有的属性,可以同时有多个
    • @Attribute:表示注解对象的一个属性
      • name:属性名
      • type:属性的类型
      • required:是否必须
Parser 与核心解析器源码分析

所有的注解解析器,必须通过类注解:@AnnotationParser(Object::class) 来标识,说明此解析器用来解析哪个注解对象。

解析器必须继承 \Swoft\Annotation\Annotation\Parser\Parser 父类,并且实现 parse 接口。parse 方法中,可以用来给其他的组件提供数据或者其他操作,parse 的返回值也是一些元数据,不同类型的注解返回的数据的要求也不同。

类注解解析器

要求可以返回一个四个元素的数组,如果返回了这个,表示被此注解标识的类,会生成一个自定义的 Bean,并且存储到(IOC)容器中。

php 复制代码
$data = $annotationParser->parse(Parser::TYPE_CLASS, $annotation);
if (empty($data)) {
    continue;
}

if (count($data) !== 4) {
    throw new InvalidArgumentException(sprintf('%s annotation parse must be 4 size', $annotationClass));
}
// 此处为注解解析器返回的数据
[$name, $className, $scope, $alias] = $data;
$name = empty($name) ? $className : $name;

if (empty($className)) {
    throw new InvalidArgumentException(sprintf('%s with class name can not be empty', $annotationClass));
}

// Multiple coverage
$objectDefinition = new ObjectDefinition($name, $className, $scope, $alias);
属性注解解析器

要求可以返回一个两个元素的数组,如果返回表示有数据要注入被标注的属性中。

  • 属性值(一般为两种)
    • 引用数据,可以为bean实例,或者配置信息,如:.config.keybeanName
    • 固定值,属性值为数据本身,如:一个固定字符串
  • 是否是引用数据(布尔类型)
    • true: 表示是引用数据,会去查找配置,或者查找bean实例
    • false:表示是一个固定值
php 复制代码
$data = $annotationParser->parse(Parser::TYPE_PROPERTY, $propertyAnnotation);

if (empty($data)) {
    continue;
}

if (count($data) !== 2) {
    throw new InvalidArgumentException('Return array with property annotation parse must be 2 size');
}

$definitions = $annotationParser->getDefinitions();
if ($definitions) {
    $this->definitions = $this->mergeDefinitions($this->definitions, $definitions);
}
// 此处为注解解析器返回的数据
// Multiple coverage
[$propertyValue, $isRef] = $data;
$propertyInjection = new PropertyInjection($propertyName, $propertyValue, $isRef);
方法解析器

要求不返回数据,当然如果有数据返回,并且 definitions 有数据,则会进一步解析关联的 Bean 的定义,表示此方法的注解,可以生成关联的 bean 实体。如果只提供 bean 名称,则可以给 bean 注入相关数据。

需要注意的是,swoft2 中同一个注解解析器的 definitions ,在每次解析后会通过 merge 进行合并,这就导致 bean 定义中的同一个属性,会被反复覆盖,只保留最后一次的结果。导致无法根据注解的方法,反复给 bean 的定义注入不同的数据。

由于注解解析阶段 bean 一般都没有被实例化,除了通过 bean 定义的方式进行数据注入,还可以仿照框架中各种注册器的方法,就是注解标签绑定的相关元数据,先放入某个目标的 static 静态属性中(不用实例化),后续可以让 bean 直接读取,或者让其在某个(已经有bean实例的)阶段把 static 属性中的数据在转移到目标 bean 中即可,当然,如果嫌费事,直接用 static 属性中的数据也没有问题。

注意:注解中一般不用 BeanFactory 获取 Bean 实体,因为此阶段,Bean 还未进行实例化。

php 复制代码
// 返回的数据没有用到
$data = $annotationParser->parse(Parser::TYPE_METHOD, $methodAnnotation);

// 如果有数据返回会跳过此处
if (empty($data)) {
    continue;
}
// 如果有更多的定义数据数据会进一步解析
$definitions = $annotationParser->getDefinitions();
if ($definitions) {
    $this->definitions = $this->mergeDefinitions($this->definitions, $definitions);
}

PHP Swoft2 开源框架系列教程专栏推荐

Swoft2 框架精华教程:Validator 校验器详解

Swoft 框架精华教程:Devtool 详解

Swoft2 框架精华教程:Controller 组件解析,使用说明

Swoft2 框架精华教程:Config 配置解析,使用说明

Swoft2 框架精华教程:CLog 使用篇

Swoft2 框架精华教程:数据库 Migration

Swoft2 框架精华教程:数据库操作

Swoft2 框架精华教程: Swoft 组件开发单元测试

Swoft2 框架精华教程:面向切面编程(Aspect)

Swoft2 框架精华教程:Annotation 注解机制详解

相关推荐
非凡的世界5 小时前
PHP 中的动态函数调用
php·动态函数
Okailon14 小时前
Debian12上安裝免费开源的CMS Drupal 11 机顶盒实例
开源·php·cms
一个儒雅随和的男子17 小时前
Redis连接超时排查与优化指南
redis·bootstrap·php
我叫汪枫17 小时前
【刷机分享】解决K20Pro刷入PixelOS后“网络连接”受限问题(附详细ADB命令)
开发语言·adb·php
老程序员刘飞17 小时前
hardhat 搭建智能合约
开发语言·php·智能合约
前端世界21 小时前
当网络里混入“假网关”:用 Scapy 写一个 DHCP 欺骗检测器(附完整代码与讲解)
开发语言·网络·php
KevinLyu1 天前
PHP内核详解· 内存管理篇(三)· 分配大块内存
php
星光一影1 天前
快递比价寄件系统技术解析:基于PHP+Vue+小程序的高效聚合配送解决方案
vue.js·mysql·小程序·php
JaguarJack1 天前
开发者必看的 15 个困惑的 Git 术语(以及它们的真正含义)
后端·php·laravel
落落鱼20131 天前
Dompdf库html生成pdf时editor编辑器中文本长度被截断不会自动换行问题处理
pdf·编辑器·php·html生成pdf