swoft2.0源碼剖析01-注解器
前言
本人認(rèn)為學(xué)習(xí)一個(gè)框架正確的方式是先看框架的文檔丐重,再進(jìn)入實(shí)戰(zhàn)動(dòng)手寫(xiě)代碼寫(xiě)一些demo,然后就是看框架的源碼杆查,深入了解框架是怎么啟動(dòng)運(yùn)行的扮惦,整個(gè)框架的流程最好用debug跑一遍,看一下有哪些可以值得學(xué)習(xí)和擴(kuò)展的地方亲桦,比如框架源碼中用到的一些設(shè)計(jì)模式崖蜜,思考為什么用這個(gè)設(shè)計(jì)模式;比如框架源碼中有一些可擴(kuò)展的方法豫领,是文檔中沒(méi)有提及到的二跋;比如想要寫(xiě)框架的擴(kuò)展庫(kù)衫哥,必須要深入了解框架源碼和運(yùn)行流程互例。
下面主要是對(duì)swoft這個(gè)框架注解器組件方面做一個(gè)流程剖析议双。
swoft注解
注解(Annotations) 是Swoft里面很多重要功能的基礎(chǔ)赔蒲。特別是AOP焕刮,IoC容器畸冲、事件監(jiān)聽(tīng)的基礎(chǔ)褪子。
注解的定義是: "附加在數(shù)據(jù)/代碼上的元數(shù)據(jù)(metadata)"
swoft框架可以基于這些元數(shù)據(jù)為代碼提供各種額外功能。
注解 VS 注釋
一般而言紧唱,在編程屆中注解是一種和注釋平行的概念。
- 注釋提供對(duì)可執(zhí)行代碼的說(shuō)明像捶,單純用于開(kāi)發(fā)人員閱讀,不影響代碼的執(zhí)行殴穴;
- 而注解往往充當(dāng)著對(duì)代碼的聲明和配置的作用磨取,為可執(zhí)行代碼提供機(jī)器可用的額外信息东臀,在特定的環(huán)境下會(huì)影響程序的執(zhí)行;
php注解與swoft注解
目前PHP沒(méi)有對(duì)注解的官方實(shí)現(xiàn),主流的PHP框架中使用的注解都是借用T_DOC_COMMENT型注釋塊(/**型注釋*/)
中的@Tag,定義自己的注解機(jī)制。
Swoft沒(méi)有重新造輪子粟矿,搞一個(gè)新的的注解方案,而是選擇使用Doctrine的注解引擎
Doctrine的注解方案也是基于T_DOC_COMMENT型注釋的福压,Doctrine使用反射獲取代碼的T_DOC_COMMENT型注釋掏秩,并將注釋中的特定類型@Tag映射到對(duì)應(yīng)注解類
如果使用
在過(guò)源碼流程之前或舞,先說(shuō)一下作為swoft核心之一的注解器是怎么使用的,就像我們?nèi)粘i_(kāi)發(fā)寫(xiě)注解一樣蒙幻,只需在類映凳、方法或成員變量上方按規(guī)則添加注解即可,如定義一個(gè)監(jiān)聽(tīng)器:
namespace SwoftTest\Event;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
/**
* Class SendMessageListener
*
* @Listener("order.created")
*/
class SendMessageListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*/
public function handle(EventInterface $event): void
{
$pos = __METHOD__;
echo "handle the event '{$event->getName()}' on the: $pos\n";
}
}
和laravel必須要寫(xiě)事件監(jiān)聽(tīng)類綁定不一樣邮破,swoft只需要在類注解寫(xiě)上@Listener這個(gè)注解就能將這個(gè)類定義為一個(gè)監(jiān)聽(tīng)器诈豌,并綁定到具體的事件。
order.created是這個(gè)監(jiān)聽(tīng)類綁定的具體事件抒和,任何地方調(diào)用Swoft::trigger("order.created")
創(chuàng)建訂單事件能觸發(fā)到這個(gè)發(fā)送消息的監(jiān)聽(tīng)器矫渔,執(zhí)行handle()方法。
實(shí)現(xiàn)注解
那么怎么使得這些注解附有意義呢摧莽?這時(shí)候要引出強(qiáng)大的ReflectionClass反射類庙洼,通過(guò)反射類的getDocComment()方法能獲取某個(gè)類的類注釋,方法和屬性的注釋范嘱。
/**
* Gets doc comment
* @link https://php.net/manual/en/reflectionproperty.getdoccomment.php
* @return string|bool The doc comment if it exists, otherwise <b>FALSE</b>
* @since 5.1.0
*/
public function getDocComment () {}
不妨設(shè)想一下送膳,程序在開(kāi)始運(yùn)行的時(shí)候(php bin/swoft http start)應(yīng)該是把全部文件都掃描一遍,通過(guò)反射獲取類注解丑蛤,如果類注解為Swoft\Event\Annotation\Mapping\Listener
就把該類(SwoftTest\Event\SendMessageListener
)和對(duì)應(yīng)的事件名("order.created")綁定到事件管理器叠聋,然后觸發(fā)事件就能找到對(duì)應(yīng)的監(jiān)聽(tīng)器。其實(shí)就是1受裹、先識(shí)別注解 ==> 再到2碌补、讓注解起作用兩個(gè)步驟。
下面來(lái)看一下程序啟動(dòng)的運(yùn)行流程棉饶,可以一邊看代碼一邊看文章厦章,看一下注解是怎么起作用的。
先看命令啟動(dòng)的入口文件 bin/swoft
// Bootstrap
require_once __DIR__ . '/bootstrap.php';
Swoole\Coroutine::set([
'max_coroutine' => 300000,
]);
// 啟動(dòng)App
(new \App\Application())->run();
主要就是程序的入口照藻,實(shí)例化Application袜啃,并執(zhí)行run()方法,我們知道Application是繼承與SwoftApplication幸缕,Application沒(méi)有構(gòu)造方法群发,自然就會(huì)調(diào)用父類SwoftApplication的構(gòu)造方法,父類的構(gòu)造方法如下:
/**
* Class constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
......
// Init application
$this->init();
......
}
前半部分主要是程序初始化的一些校驗(yàn)发乔,日志器的初始化熟妓,省略掉,主要是$this->init()
初始化方法
protected function init(): void
{
......
$processors = $this->processors();
$this->processor = new ApplicationProcessor($this);
$this->processor->addFirstProcessor(...$processors);
}
......
/**
* swoft 六大處理器
* @return ProcessorInterface[]
*/
protected function processors(): array
{
return [
new EnvProcessor($this), // 處理環(huán)境變量
new ConfigProcessor($this), // 處理配置
new AnnotationProcessor($this), // 處理注解(本文章重點(diǎn))
new BeanProcessor($this), // 處理容器對(duì)象
new EventProcessor($this), // 處理事件
new ConsoleProcessor($this), // 處理命令
];
}
初始化里面主要是把swoft六大處理器添加到應(yīng)用程序的主處理器ApplicationProcessor中栏尚,本文章主要解讀的是AnnotationProcessor注解處理器起愈,通過(guò)這個(gè)處理器實(shí)現(xiàn)對(duì)類注解的解析。添加處理器后,Application初始化結(jié)束抬虽。下面再來(lái)調(diào)用的run()方法官觅。
/**
* Run application
*/
public function run(): void
{
try {
if (!$this->beforeRun()) {
return;
}
$this->processor->handle();
} catch (Throwable $e) {
......
}
}
run()方法主要執(zhí)行了應(yīng)用程序的主處理器ApplicationProcessor的handle()方法,handle()方法把前面添加的六大處理器的handle()方法都執(zhí)行了一遍(代碼省略)阐污,下面主要看AnnotationProcessor注解處理器中的handle()方法缰猴。
/**
* Handle annotation
*
* @return bool
* @throws Exception
*/
public function handle(): bool
{
......
$app = $this->application;
AnnotationRegister::load([
'inPhar' => IN_PHAR,
'basePath' => $app->getBasePath(),
'notifyHandler' => [$this, 'notifyHandle'],
'disabledAutoLoaders' => $app->getDisabledAutoLoaders(),
'disabledPsr4Prefixes' => $app->getDisabledPsr4Prefixes(),
]);
......
}
注解處理器handle()方法中調(diào)用了load方法,AnnotationRegister::load()
方法調(diào)用了AnnotationResource實(shí)例化對(duì)象的load()方法疤剑,AnnotationResource類用途就是收集整合注解資源的,load方法的主要作用是:
- 查找目錄中的AutoLoader.php文件闷堡,如果沒(méi)有的話就不解析(所以swoft擴(kuò)展庫(kù)的src目錄必須有AutoLoader.php文件隘膘,否則注解器等功能不能生效)。
- 解析每個(gè)目錄下每個(gè)文件并收集帶有解析的類或?qū)傩苑椒ā?/li>
/**
* 遍歷查找目錄中的AutoLoader.php文件杠览,如果沒(méi)有就不解析弯菊,如果有就根據(jù)這個(gè)文件指定的目錄文件解析
*
* @throws AnnotationException
* @throws ReflectionException
*/
public function load(): void
{
// 獲取composer里面autoload的psr-4映射目錄,包括了app目錄和vendor第三方庫(kù)的目錄
$prefixDirsPsr4 = $this->classLoader->getPrefixesPsr4();
foreach ($prefixDirsPsr4 as $ns => $paths) {
......
// 循環(huán)每個(gè)目錄踱阿,查找Autoloader.php文件
foreach ($paths as $path) {
$loaderFile = $this->getAnnotationClassLoaderFile($path);
.......
$loaderClass = $this->getAnnotationLoaderClassName($ns);
$isEnabled = true;
$autoLoader = new $loaderClass();
.......
// 如果Autoloader類沒(méi)有允許加載管钳,則不能加載注解,通過(guò)isEnable()控制
if (isset($this->disabledAutoLoaders[$loaderClass]) || !$autoLoader->isEnable()) {
$isEnabled = false;
$this->notify('disabledLoader', $loaderFile);
} else {
AnnotationRegister::addAutoLoaderFile($loaderFile);
$this->notify('addLoaderClass', $loaderClass);
// 加載并收集注解類(核心)
$this->loadAnnotation($autoLoader);
}
// Storage autoLoader instance to register
AnnotationRegister::addAutoLoader($ns, $autoLoader, $isEnabled);
}
}
}
load()方法已經(jīng)完成了Autoloader.php文件的發(fā)現(xiàn)软舌,這就找到了允許被swoft解析注解的目錄才漆,有了目錄,接下來(lái)就是遍歷目錄中的每個(gè)文件佛点,收集并解析注解醇滥,這一核心流程交給了$this->loadAnnotation($autoLoader)
方法去實(shí)現(xiàn)。
/**
* 循環(huán)解析目錄下每個(gè)文件的注解
*
* @param LoaderInterface $loader
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function loadAnnotation(LoaderInterface $loader): void
{
// 獲取Autoloader類中設(shè)置的目錄
$nsPaths = $loader->getPrefixDirs();
foreach ($nsPaths as $ns => $path) {
// 迭代生成目錄下文件迭代器超营,然后遍歷每個(gè)文件
$iterator = DirectoryHelper::recursiveIterator($path);
foreach ($iterator as $splFileInfo) {
......
$suffix = sprintf('.%s', $this->loaderClassSuffix);
$pathName = str_replace([$path, '/', $suffix], ['', '\\', ''], $filePath);
$className = sprintf('%s%s', $ns, $pathName);
// 解析某個(gè)類鸳玩,查看某個(gè)類有沒(méi)有類注解,方法注解演闭,屬性注解等不跟。
$this->parseAnnotation($ns, $className);
}
}
}
/**
* 解析某個(gè)類的注解
*
* @param string $namespace
* @param string $className
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function parseAnnotation(string $namespace, string $className): void
{
// **核心**:實(shí)例化某類的ReflectionClass類,比如說(shuō)上面的SwoftTest\Event\SendMessageListener類
$reflectionClass = new ReflectionClass($className);
// Fix ignore abstract
if ($reflectionClass->isAbstract()) {
return;
}
// 根據(jù)反射類ReflectionClass解析某個(gè)類并查找某個(gè)類注解米碰,返回了某個(gè)類整合完的注解(數(shù)組形式)
$oneClassAnnotation = $this->parseOneClassAnnotation($reflectionClass);
// 如果某個(gè)類整合完的注解不為空窝革,就注冊(cè)到AnnotationRegister類中
if (!empty($oneClassAnnotation)) {
AnnotationRegister::registerAnnotation($namespace, $className, $oneClassAnnotation);
}
}
核心流程:實(shí)例化了某類的ReflectionClass類,比如說(shuō)上面的SwoftTest\Event\SendMessageListener
類见间,然后把反射類傳遞給parseOneClassAnnotation()方法去處理聊闯。
/**
* 解析某個(gè)類的注解
*
* @param ReflectionClass $reflectionClass
*
* @return array
* @throws AnnotationException
* @throws ReflectionException
*/
private function parseOneClassAnnotation(ReflectionClass $reflectionClass): array
{
// Annotation reader 注解閱讀器
$reader = new AnnotationReader();
$className = $reflectionClass->getName();
$oneClassAnnotation = [];
// $reader獲取類注解
$classAnnotations = $reader->getClassAnnotations($reflectionClass);
// Register annotation parser 注冊(cè)注解的解析器,這里的解析器不是getDocComment()米诉,而是把上面例子中監(jiān)聽(tīng)器和時(shí)間綁定的解析器菱蔬。通常在src\Annotation\Parser目錄中。
foreach ($classAnnotations as $classAnnotation) {
if ($classAnnotation instanceof AnnotationParser) {
// * 如果是AnnotationParser解析類,則把該類注冊(cè)到AnnotationRegister類的$parsers(解析器)屬性中拴泌,這個(gè)解析類后文作用重大魏身,用來(lái)讓注解真正起作用的,一個(gè)注解類對(duì)應(yīng)一個(gè)解析類
$this->registerParser($className, $classAnnotation);
return [];
}
}
// Class annotation
if (!empty($classAnnotations)) {
$oneClassAnnotation['annotation'] = $classAnnotations;
$oneClassAnnotation['reflection'] = $reflectionClass;
}
// 獲取類屬性 => 遍歷類屬性 => 獲取屬性注解
$reflectionProperties = $reflectionClass->getProperties();
foreach ($reflectionProperties as $reflectionProperty) {
$propertyName = $reflectionProperty->getName();
// $reader獲取屬性注解
$propertyAnnotations = $reader->getPropertyAnnotations($reflectionProperty);
if (!empty($propertyAnnotations)) {
$oneClassAnnotation['properties'][$propertyName]['annotation'] = $propertyAnnotations;
$oneClassAnnotation['properties'][$propertyName]['reflection'] = $reflectionProperty;
}
}
// 獲取類方法 => 遍歷類方法 => 獲取方法注解
$reflectionMethods = $reflectionClass->getMethods();
foreach ($reflectionMethods as $reflectionMethod) {
$methodName = $reflectionMethod->getName();
// $reader獲取方法注解
$methodAnnotations = $reader->getMethodAnnotations($reflectionMethod);
if (!empty($methodAnnotations)) {
$oneClassAnnotation['methods'][$methodName]['annotation'] = $methodAnnotations;
$oneClassAnnotation['methods'][$methodName]['reflection'] = $reflectionMethod;
}
}
$parentReflectionClass = $reflectionClass->getParentClass();
if ($parentReflectionClass !== false) {
$parentClassAnnotation = $this->parseOneClassAnnotation($parentReflectionClass);
if (!empty($parentClassAnnotation)) {
$oneClassAnnotation['parent'] = $parentClassAnnotation;
}
}
return $oneClassAnnotation;
}
AnnotationParser解析類蚪腐,與注解類一一對(duì)應(yīng)箭昵,有Swoft\Event\Annotation\Mapping\Listener
注解類就有對(duì)應(yīng)的Swoft\Event\Annotation\Parser\ListenerParser
解析類,一般都存放在app或者庫(kù)src目錄下的Annotation目錄中回季。
$reader = new AnnotationReader()
注解閱讀器是引用了Doctrine注解引擎這個(gè)包里面的類家制。
parseOneClassAnnotation()方法解析某個(gè)類的注解,并根據(jù)類泡一、屬性颤殴、方法整合到了$oneClassAnnotation數(shù)組中,并返回鼻忠。AnnotationReader類能解析(類涵但、屬性、方法)注解帖蔓,下面看看這個(gè)類是怎么通過(guò)getClassAnnotations()方法獲取注解的矮瘟。
/**
* {@inheritDoc}
*/
public function getClassAnnotations(ReflectionClass $class)
{
$this->parser->setTarget(Target::TARGET_CLASS);
$this->parser->setImports($this->getClassImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
使用ReflectionClass的getDocComment()方法獲取類注釋,再傳遞給this->parser->parse()`方法根據(jù)@關(guān)鍵字把注解格式化為數(shù)組形式澈侠,數(shù)組里面是具體的注解類實(shí)例化對(duì)象例如:
/**
* Class SendMessageListener
*
* @since 2.0
*
* @Listener(DbEvent::MODEL_SAVED)
*/
class SendMessageListener{
......
}
經(jīng)過(guò)parser()處理后變成如下形式,由于只有一個(gè)注解@Listener埋酬,所以數(shù)組只有一個(gè)元素埋涧。
array(1) {
[0]=>
object(Swoft\Event\Annotation\Mapping\Listener)#1479 (2) {
["event":"Swoft\Event\Annotation\Mapping\Listener":private]=>
string(17) "swoft.model.saved"
["priority":"Swoft\Event\Annotation\Mapping\Listener":private]=>
int(0)
}
}
同樣的方式獲取屬性的注解,方法的注解奇瘦,并組裝成oneClassAnnotation注冊(cè)到然后通過(guò)AnnotationRegister類的$annotations屬性中。
整個(gè)目錄文件掃描的流程完成后次坡,最終會(huì)把所有的注解(格式化后 => 注解類實(shí)例化對(duì)象)添加在AnnotationRegister類的$annotations屬性中呼猪。
/**
* Class AnnotationRegister
*
* @since 2.0.0
*/
final class AnnotationRegister
{
/**
* @var array
*
* @example
* [
* // 命名空間 SwoftTest\Event
* 'loadNamespace' => [
* // 類名,例子中的SwoftTest\Event\SendMessageListener
* 'className' => [
* // 類注解
* 'annotation' => [
* // 例子中的Swoft\Event\Annotation\Mapping\Listener注解類對(duì)象
* new ClassAnnotation(),
* new ClassAnnotation(),
* new ClassAnnotation(),
* ]
* 'reflection' => new ReflectionClass(),
* // 屬性注解
* 'properties' => [
* 'propertyName' => [
* 'annotation' => [
* new PropertyAnnotation(),
* new PropertyAnnotation(),
* new PropertyAnnotation(),
* ]
* 'reflection' => new ReflectionProperty(),
* ]
* ],
* // 方法注解
* 'methods' => [
* 'methodName' => [
* 'annotation' => [
* new MethodAnnotation(),
* new MethodAnnotation(),
* new MethodAnnotation(),
* ]
* 'reflection' => new ReflectionFunctionAbstract(),
* ]
* ]
* ]
* ]
* ]
*/
private static $annotations = [];
}
一般來(lái)說(shuō)砸琅,每個(gè)不同的注解類都會(huì)有不同的屬性宋距,比如Swoft\Event\Annotation\Mapping\Listener
注解類就保存了事件名和優(yōu)先級(jí)屬性,而Swoft\Bean\Annotation\Mapping\Bean
這個(gè)注解類就保存了名稱症脂、作用域谚赎、別名等屬性淫僻,這些屬性時(shí)在解析類中獲取的,解析類的作用下文說(shuō)壶唤。
至此雳灵,AnnotationProcessor任務(wù)完成。但是有個(gè)疑問(wèn)的是闸盔,上面的流程只是把注解格式化出來(lái)(步驟1識(shí)別注解)悯辙,具體怎么樣讓注解起作用好像是還沒(méi)有涉及到,那得繼續(xù)看下一個(gè)處理器BeanProcessor的handle()方法了迎吵。
先說(shuō)一下BeanProcessor處理器的功能躲撰,這個(gè)處理器的功能如下:
- Bean 就是一個(gè)類的一個(gè)對(duì)象實(shí)例。 容器Container就是一個(gè)巨大的工廠击费,用于存放和管理 Bean 生命周期茴肥。所以這個(gè)處理器是用來(lái)生成Bean并放入容器中的。
- 把a(bǔ)pp目錄下的bean.php文件的數(shù)組和AutoLoader的beans()方法返回的數(shù)組合并再實(shí)例化后放入容器Container中荡灾。
- 把用了
@Bean注解
的類實(shí)例化后放入Container中,所以必須要讓注解起作用后才能進(jìn)行Bean的實(shí)例化瞬铸。
/**
* Class BeanProcessor
*
* @since 2.0
*/
class BeanProcessor extends Processor
{
/**
* Handle bean
*
* @return bool
* @throws ReflectionException
* @throws AnnotationException
*/
public function handle(): bool
{
......
$handler = new BeanHandler();
// 獲取bean.php文件的數(shù)組和AutoLoader的beans()方法返回的數(shù)組
$definitions = $this->getDefinitions();
// 獲取$parsers(解析類)
$parsers = AnnotationRegister::getParsers();
// 獲取$annotations(所有格式化后注解的結(jié)果批幌,注解類)
$annotations = AnnotationRegister::getAnnotations();
// 把上面獲取到的都添加到BeanFactory的屬性中
BeanFactory::addDefinitions($definitions);
BeanFactory::addAnnotations($annotations);
BeanFactory::addParsers($parsers);
BeanFactory::setHandler($handler);
// Bean工廠初始化
BeanFactory::init();
......
}
......
}
/**
* Class BeanFactory
*
* @since 2.0
*/
class BeanFactory
{
/**
* Init bean container
*
* @return void
* @throws AnnotationException
* @throws ReflectionException
*/
public static function init(): void
{
// 調(diào)用容器的初始化方法
Container::getInstance()->init();
}
......
}
/**
* Class Container
*/
class Container implements ContainerInterface
{
/**
* Init
*
* @throws AnnotationException
* @throws ReflectionException
*/
public function init(): void
{
// 解析注解
$this->parseAnnotations();
// 解析Bean對(duì)象
$this->parseDefinitions();
// 實(shí)例化Bean對(duì)象
$this->initializeBeans();
}
}
我們重點(diǎn)看$this->parseAnnotations()
這個(gè)方法,這個(gè)方法是解析注解類的嗓节,讓注解起作用的(步驟2)荧缘,下面的是解析和實(shí)例化Bean,和注解類怎么起作用的無(wú)關(guān)拦宣,就不理它了截粗。至于為什么把解析注解類的流程放在BeanProcessor處理器上而不放在AnnotationProcessor處理器上,可能是解析類處理完要返回一個(gè)結(jié)果提供給Bean鸵隧,為什么這么做绸罗,猜想是要兼容很多解析類有關(guān)吧,具體我也不太明白豆瘫,繼續(xù)看代碼珊蟀。
/**
* Class Container
*/
class Container implements ContainerInterface
{
/**
* Parse annotations
*
* @throws AnnotationException
*/
private function parseAnnotations(): void
{
$annotationParser = new AnnotationObjParser(
$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases
);
// 實(shí)例化AnnotationObjParser對(duì)象,用這個(gè)對(duì)象去解析注解類
$annotationData = $annotationParser->parseAnnotations($this->annotations, $this->parsers);
[$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $annotationData;
}
}
/**
* Class AnnotationParser
*
* @since 2.0
*/
class AnnotationObjParser extends ObjectParser
{
/**
* Parse annotations
*
* @param array $annotations
* @param array $parsers
*
* @return array
* @throws AnnotationException
*/
public function parseAnnotations(array $annotations, array $parsers): array
{
$this->parsers = $parsers;
$this->annotations = $annotations;
foreach ($this->annotations as $loadNameSpace => $classes) {
foreach ($classes as $className => $classOneAnnotations) {
$this->parseOneClassAnnotations($className, $classOneAnnotations);
}
}
return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}
}
AnnotationObjParser類的parseAnnotations方法循環(huán)了所有的注解className(
SwoftTest\Event\SendMessageListener
)和這個(gè)類的所有注解類$classOneAnnotations (包括類注解,方法注解昵宇,屬性注解)作為參數(shù)傳遞給了這個(gè)方法磅崭。
/**
* 解析某類的所有注解
*
* @param string $className
* @param array $classOneAnnotations
*
* @throws AnnotationException
*/
private function parseOneClassAnnotations(string $className, array $classOneAnnotations): void
{
......
// Parse class annotations
$classAnnotations = $classOneAnnotations['annotation'];
$reflectionClass = $classOneAnnotations['reflection'];
$classAry = [
$className,
$reflectionClass,
$classAnnotations
];
// 解析類注解
$objectDefinition = $this->parseClassAnnotations($classAry);
// 解析屬性注解
$propertyInjects = [];
$propertyAllAnnotations = $classOneAnnotations['properties'] ?? [];
foreach ($propertyAllAnnotations as $propertyName => $propertyOneAnnotations) {
$proAnnotations = $propertyOneAnnotations['annotation'] ?? [];
$propertyInject = $this->parsePropertyAnnotations($classAry, $propertyName, $proAnnotations);
if ($propertyInject) {
$propertyInjects[$propertyName] = $propertyInject;
}
}
// 解析方法注解
$methodInjects = [];
$methodAllAnnotations = $classOneAnnotations['methods'] ?? [];
foreach ($methodAllAnnotations as $methodName => $methodOneAnnotations) {
$methodAnnotations = $methodOneAnnotations['annotation'] ?? [];
$methodInject = $this->parseMethodAnnotations($classAry, $methodName, $methodAnnotations);
if ($methodInject) {
$methodInjects[$methodName] = $methodInject;
}
}
......
}
我們只看怎么解析類注解$this->parseClassAnnotations($classAry)
。
/**
* @param array $classAry
*
* @return ObjectDefinition|null
*/
private function parseClassAnnotations(array $classAry): ?ObjectDefinition
{
[, , $classAnnotations] = $classAry;
$objectDefinition = null;
foreach ($classAnnotations as $annotation) {
$annotationClass = get_class($annotation);
if (!isset($this->parsers[$annotationClass])) {
continue;
}
// 去解析類數(shù)組里面根據(jù)注解類名找到對(duì)應(yīng)的解析類名(前面說(shuō)了注解類和解析類一一對(duì)應(yīng)的)
$parserClassName = $this->parsers[$annotationClass];
// 根據(jù)解析類名獲取示例化后的解析類(例子中是Swoft\Event\Annotation\Parser\ListenerParser)
$annotationParser = $this->getAnnotationParser($classAry, $parserClassName);
// 調(diào)用解析類的parse()
$data = $annotationParser->parse(Parser::TYPE_CLASS, $annotation);
......
}
return $objectDefinition;
}
調(diào)用解析類的parse()方法瓦哎,例子中就是調(diào)用Swoft\Event\Annotation\Parser\ListenerParser
這個(gè)解析類的parse()方法砸喻,這個(gè)方法就是實(shí)現(xiàn)了步驟2讓注解起作用柔逼,具體看一下parse()方法。
class ListenerParser extends Parser
{
/**
* @param int $type
* @param Listener $annotation
*
* @return array
* @throws AnnotationException (注解類恩够,例子中的Swoft\Event\Annotation\Mapping\Listener)
*/
public function parse(int $type, $annotation): array
{
if ($type !== self::TYPE_CLASS) {
throw new AnnotationException('`@Listener` must be defined on class!');
}
// 注冊(cè)監(jiān)聽(tīng)器卒落,key為為SwoftTest\Event\SendMessageListener,值為["order.created" => 0]
ListenerRegister::addListener($this->className, [
// event name => listener priority
$annotation->getEvent() => $annotation->getPriority()
]);
return [$this->className, $this->className, Bean::SINGLETON, ''];
}
}
ListenerParser類的parse()方法把注解類(Swoft\Event\Annotation\Mapping\Listener
)實(shí)例化對(duì)象中保存的事件名和優(yōu)先級(jí)注冊(cè)到了ListenerRegister類的$listeners屬性中蜂桶,從而使得SwoftTest\Event\SendMessageListener和"order.created"綁定了關(guān)系儡毕,后續(xù)程序中觸發(fā)了order.created事件就能找到對(duì)應(yīng)的監(jiān)聽(tīng)器了。
通過(guò)識(shí)別注解 ==> 到解析注解這么一個(gè)流程扑媚,類注解成功綁定了事件和監(jiān)聽(tīng)器了腰湾。
自定義注解
swoft框架的注解流程講解完了,如果要自定義一個(gè)注解怎么做應(yīng)該也清晰多了
主要是編寫(xiě)注解類和解析類:
注解類寫(xiě)在App\Annotation\Mapping目錄疆股,比如編寫(xiě)要編寫(xiě)一個(gè)門(mén)面Facades的注解類App\Annotation\Mapping\FacadesAnnotation
類费坊,讓類變成門(mén)面類(不了解門(mén)面的可以看一下laravel門(mén)面的文檔)。
namespace App\Annotation\Mapping;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
/**
* Class Facades
*
* @since 2.0
*
* @Annotation //聲明這是一個(gè)注解類
* @Target("CLASS") //聲明這個(gè)注解只可用在class級(jí)別的注釋中
* @Attributes({
* @Attribute("alias",type="string")
* })
*/
class Facades
{
/**
* @var string
*/
private $alias = '';
/**
* StringType constructor.
*
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->message = $values['value'];
}
if (isset($values['alias'])) {
$this->alias = $values['alias'];
}
}
/**
* @return string
*/
public function getAlias(): string
{
return $this->alias;
}
}
在程序任何有Autoload.php文件的目錄下的類都能在類注解上按格式寫(xiě)上自定義的注解類(記得要use 注解類旬痹,phpstorm有注解的插件可以直接引入)比如:
use App\Annotation\Mapping\Facades
/**
* Class Calculate
*
* @Facades()
*/
class Calculate{
/**
* 執(zhí)行
*
* @return mixed
* @throws Exception
*/
public function execute()
{
......
}
}
解析類寫(xiě)在App\Annotation\Parser目錄下附井,編寫(xiě)App\Annotation\Parser\FacadesParser
類:
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Annotation\Parser;
use ReflectionException;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use App\Annotation\Mapping\Facades;
use Swoft\Validator\Exception\ValidatorException;
use Swoft\Validator\ValidatorRegister;
/**
* Class FacadesParser
*
* @AnnotationParser(annotation=Facades::class) // 參數(shù)值寫(xiě)上注解類
*/
class FacadesParser extends Parser
{
/**
* @param int $type
* @param object $annotationObject
*
* @return array
* @throws ReflectionException
* @throws ValidatorException
*/
public function parse(int $type, $annotationObject): array
{
// 可以獲取到目標(biāo)類Calculate,用$this->className獲取
// 可以獲取到注解類對(duì)象$annotationObject
// 這里是把目標(biāo)類Calculate怎么變成門(mén)面的流程两残,我也沒(méi)有實(shí)現(xiàn)永毅,有興趣的可以自己寫(xiě)一個(gè)
return [];
}
}
以上就是Swoft注解器的源碼流程剖析。