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 注解机制详解

相关推荐
木子金光军3 小时前
BeikeShop - 一个开源、用户友好的跨境电子商务平台
开源·php·laravel
喵叔哟3 小时前
第11章:Neo4j实际应用案例
服务器·php·neo4j
万岳科技程序员小金4 小时前
2025餐饮供应链趋势:一套系统源码如何打通食堂采购全流程?
开源·php·源码·食堂采购系统源码·供应链管理平台
kali-Myon6 小时前
攻防世界[level7]-Web_php_wrong_nginx_config
前端·nginx·安全·php·web·ctf·攻防世界
Q_Q5110082857 小时前
python题库及试卷管理系统
开发语言·spring boot·python·django·flask·node.js·php
(:满天星:)7 小时前
使用 Kubernetes 部署 PHP 留言板应用(含 Redis 架构)
redis·kubernetes·php
神仙别闹1 天前
基于 Matlab 和 Truetime 的网络控制系统仿真
网络·matlab·php
Joomla中文网1 天前
在2023年ChatGPT如何谈joomla和wordpress
php·joomla
机灵小和尚2 天前
腾讯云 Teo H5直传CDN空间
后端·云计算·php·腾讯云·html5