注解是Swoft的特色之一,Swoft我的项目中简直所有的业务代码中都离不开注解.
AnnotationProcessor处理器就是Swoft能在业务中应用组件的外围依赖.

先看AnnotationProcessor的入口办法:

public function handle(): bool{     // 注解加载前回调     // 零碎未做解决 间接返回的true     if (!$this->application->beforeAnnotation()) {         CLog::warning('Stop annotation processor by beforeAnnotation return false');         return false;      }          // 获取以后利用实例,这个实例是在利用初始化处理器时传递给     // 每个处理器的     $app = $this->application;          // 找到AutoLoader类.解析和采集注解.     // Find AutoLoader classes. Parse and collect annotations.     AnnotationRegister::load([         'inPhar' => IN_PHAR,         'basePath' => $app->getBasePath(),         'notifyHandler' => [$this, 'notifyHandle'],         // TODO force load framework components: bean, error, event, aop         'disabledAutoLoaders' => $app->getDisabledAutoLoaders(),         'excludedPsr4Prefixes' => $app->getDisabledPsr4Prefixes(),     ]);          //      $stats = AnnotationRegister::getClassStats();          // 控制台输入注解的采集和加载数据     CLog::info(         'Annotations is scanned(autoloader %d, annotation %d, parser %d)',         $stats['autoloader'],         $stats['annotation'],         $stats['parser']     );          // 注解解决实现     return $this->application->afterAnnotation();}

这一处理器解决的内容十分多,在我本机环境下执行工夫约1.2秒.

AnnotationRegister::load实现:

public static function load(array $config = []): void{     // 初始化和配置注解资源     $resource = new AnnotationResource($config);          // 执行注解的加载办法     $resource->load();}

AnnotationResource的构造方法:

public function __construct(array $config = []){     // 设置默认不蕴含的PSR4前缀     // 这里有'Psr\\','Monolog\\','PHPUnit\\','Symfony\\'     // Init $excludedPsr4Prefixes     $this->excludedPsr4Prefixes = self::DEFAULT_EXCLUDED_PSR4_PREFIXES;          // 将传递进来的config设置到以后对象上     // 后附$config的打印后果     // 打印后果可见:未额定设置生效的loader和不引入的psr4前缀     // 设置了以后是否在phar环境,我的项目根目录和告诉回调函数     // Can set property by array     ObjectHelper::init($this, $config);          // 向Doctrine组件注册了一个查看类是否存在的回调     $this->registerLoader();          // 获取composer的classLoader     $this->classLoader = ComposerHelper::getClassLoader();          // 获取已加载的文件     $this->includedFiles = get_included_files();}

$config的打印后果:

array(5) {  // 以后不是在phar中执行 所以此处为false  ["inPhar"]=>  bool(false)  // 我的项目根目录  ["basePath"]=>  string(54) "/Volumes/Samsung_T5/hmqr/phpProgram/fulian/AuthService"  // 告诉的回调函数,是AnnotationProcessor处理器上的notifyHandle办法  ["notifyHandler"]=>  array(2) {    [0]=>    object(Swoft\Processor\AnnotationProcessor)#10 (1) {      ["application":protected]=>      object(App\Application)#3 (12) {        ["basePath":protected]=>        string(54) "/Volumes/Samsung_T5/hmqr/phpProgram/fulian/AuthService"        ["envFile":protected]=>        string(10) "@base/.env"        ["beanFile":protected]=>        string(13) "@app/bean.php"        ["appPath":protected]=>        string(9) "@base/app"        ["configPath":protected]=>        string(12) "@base/config"        ["runtimePath":protected]=>        string(13) "@base/runtime"        ["resourcePath":protected]=>        string(14) "@base/resource"        ["processor":"Swoft\SwoftApplication":private]=>        object(Swoft\Processor\ApplicationProcessor)#14 (2) {          ["processors":"Swoft\Processor\ApplicationProcessor":private]=>          array(6) {            [0]=>            object(Swoft\Processor\EnvProcessor)#8 (1) {              ["application":protected]=>              *RECURSION*            }            [1]=>            object(Swoft\Processor\ConfigProcessor)#9 (1) {              ["application":protected]=>              *RECURSION*            }            [2]=>            *RECURSION*            [3]=>            object(Swoft\Processor\BeanProcessor)#11 (1) {              ["application":protected]=>              *RECURSION*            }            [4]=>            object(Swoft\Processor\EventProcessor)#12 (1) {              ["application":protected]=>              *RECURSION*            }            [5]=>            object(Swoft\Processor\ConsoleProcessor)#13 (1) {              ["application":protected]=>              *RECURSION*            }          }          ["application":protected]=>          *RECURSION*        }        ["startConsole":protected]=>        bool(true)        ["disabledProcessors":"Swoft\SwoftApplication":private]=>        array(0) {        }        ["disabledAutoLoaders":"Swoft\SwoftApplication":private]=>        array(0) {        }        ["disabledPsr4Prefixes":"Swoft\SwoftApplication":private]=>        array(0) {        }      }    }    [1]=>    string(12) "notifyHandle"  }  // 有效的AutoLoaders  ["disabledAutoLoaders"]=>  array(0) {  }  // 不引入的Psr4前缀  ["excludedPsr4Prefixes"]=>  array(0) {  }}

registerLoader代码:

private function registerLoader(): void{     // 此处看到swoft的注解依赖了Doctrine框架的注解性能     // 这里给Doctrine\Common\Annotations\AnnotationRegistry     // 注册了回调函数,回调的性能是依据传递进来的类返回该类是否存在     // 这是Doctrine框架的用法,如有疑难,可先尝试应用Doctrine     // 所以为什么不间接 return class_exists($class);     /** @noinspection PhpDeprecationInspection */     AnnotationRegistry::registerLoader(function (string $class) {         if (class_exists($class)) {            return true;         }         return false;     });}

初始化实现后调用load办法:

public function load(): void{     // 获取命名空间和源码目录的映射     $prefixDirsPsr4 = $this->classLoader->getPrefixesPsr4();          // 遍历获取的映射     foreach ($prefixDirsPsr4 as $ns => $paths) {         // 如果遍历到的命名空间是在被排除的命名空间内         // 则发送回调音讯,不再持续加载这个命名空间         // 继续执行下一个命名空间         // Only scan namespaces         if ($this->onlyNamespaces && !in_array($ns, $this->onlyNamespaces, true)) {             $this->notify('excludeNs', $ns);             continue;          }                  // 如果以后命名空间是被定义在不引入的命名空间前缀         // 则发送告诉,跳过执行以后命名空间的加载         // It is excluded psr4 prefix         if ($this->isExcludedPsr4Prefix($ns)) {             AnnotationRegister::addExcludeNamespace($ns);             $this->notify('excludeNs', $ns);             continue;          }                  // 遍历命名空间的源码根目录,寻找loader的类文件         // 未经设置的话,此处寻找的是源码包目录下的         // AutoLoader.php文件         // Find package/component loader class         foreach ($paths as $path) {                      // 拼接loader文件的全门路             $loaderFile = $this->getAnnotationClassLoaderFile($path);                          // 如果包目录内不存在AutoLoader.php文件             if (!file_exists($loaderFile)) {                 $this->notify('noLoaderFile', $this->clearBasePath($path), $loaderFile);                 continue;              }                          // 拼接命名空间和loader的类名             $loaderClass = $this->getAnnotationLoaderClassName($ns);                          // 如果类不存在             // 发送告诉,跳过以后主动加载类             if (!class_exists($loaderClass)) {                 $this->notify('noLoaderClass', $loaderClass);                 continue;              }                          $isEnabled = true;             // 实例化主动加载类             $autoLoader = new $loaderClass();                          // 如果类不是LoaderInterface的实现者             // 跳过以后加载             if (!$autoLoader instanceof LoaderInterface) {                 $this->notify('invalidLoader', $loaderFile);                 continue;              }                          // 告诉查找loader胜利音讯             $this->notify('findLoaderClass', $this->clearBasePath($loaderFile));                          // 如果以后loader不是一个可用的loader             // 则告诉并批改enabled状态             // If is disable, will skip scan annotation classes             if (isset($this->disabledAutoLoaders[$loaderClass]) || !$autoLoader->isEnable()) {                 // 批改enabled状态                 $isEnabled = false;                 // 告诉音讯                 $this->notify('disabledLoader', $loaderFile);             } else {                 // 注册loaderFile                 AnnotationRegister::addAutoLoaderFile($loaderFile);                 // 告诉增加loaderFile胜利音讯                 $this->notify('addLoaderClass', $loaderClass);                                  // 获取并收集类bean                 // Scan and collect class bean s                 $this->loadAnnotation($autoLoader);             }             // Storage autoLoader instance to register             // 注册loader             AnnotationRegister::addAutoLoader($ns, $autoLoader, $isEnabled);         }     } }

loadAnnotation实现:

private function loadAnnotation(LoaderInterface $loader): void{     // 获取命名空间和对应根目录的映射关系     $nsPaths = $loader->getPrefixDirs();          // 遍历失去命名空间和目录     foreach ($nsPaths as $ns => $path) {         // 调用spl的RecursiveIteratorIterator迭代器         // 获取指定目录内的所有文件         $iterator = DirectoryHelper::recursiveIterator($path);                  // 遍历迭代器         /* @var SplFileInfo $splFileInfo */         foreach ($iterator as $splFileInfo) {             // 获取以后文件全门路             $filePath = $splFileInfo->getPathname();             // $splFileInfo->isDir();                          // 如果是目录,则跳过,因为迭代器是以             // RecursiveIteratorIterator::LEAVES_ONLY             // 模式关上的,所以不会存在目录的状况             // 然而会返回.和..文件             if (is_dir($filePath)) {                continue;             }                          // 获取文件的文件名,不蕴含门路             $fileName = $splFileInfo->getFilename();                          // 获取文件的后缀名             $extension = $splFileInfo->getExtension();                          // 如果后缀和定义的loader后缀不同             // 或者文件名中不蕴含.则跳过             if ($this->loaderClassSuffix !== $extension || strpos($fileName, '.') === 0) {                continue;             }                          // 如果是不引入的文件             // 则告诉并跳过             // It is exclude filename             if (isset($this->excludedFilenames[$fileName])) {                 AnnotationRegister::addExcludeFilename($fileName);                 continue;              }                          // 拼接loader带.的后缀             $suffix = sprintf('.%s', $this->loaderClassSuffix);                          // 获取文件所在目录             $pathName = str_replace([$path, '/', $suffix], ['', '', ''], $filePath);                          // 获取类的全名(蕴含命名空间)             $className = sprintf('%s%s', $ns, $pathName);             // autoload标识以后文件是否曾经存在于             // 曾经引入的文件汇合内             // Fix repeat included file bug             $autoload = in_array($filePath, $this->includedFiles, true);                          // 如果文件曾经被引入过,类依然不存在,则告诉并跳过             // 如果文件未被引入过,则调用类的__autoload引入文件后,再判断类是否存在,如果依然不存在,则告诉并跳过             // Will filtering: interfaces and traits             if (!class_exists($className, !$autoload)) {                 $this->notify('noExistClass', $className);                 continue;             }                          // 解析注解             // Parse annotation             $this->parseAnnotation($ns, $className);         }     } }

parseAnnotation实现:

private function parseAnnotation(string $namespace, string $className): void{     // 创立反射类     // Doctine须要用到     // Annotation reader     $reflectionClass = new ReflectionClass($className);          // 如果是一个abstract类,则不持续解析注解     // Fix ignore abstract     if ($reflectionClass->isAbstract()) {        return;     }               $oneClassAnnotation = $this->parseOneClassAnnotation($reflectionClass);     if (!empty($oneClassAnnotation)) {        AnnotationRegister::registerAnnotation($namespace, $className, $oneClassAnnotation);     }}

parseOneClassAnnotation实现:

private function parseOneClassAnnotation(ReflectionClass $reflectionClass): array {     // 实例化Doctrine的reader     // Annotation reader     $reader = new AnnotationReader();          // 获取类名     $className = $reflectionClass->getName();          // 返回后果     $oneClassAnnotation = [];          // 获取类的注解     $classAnnotations = $reader->getClassAnnotations($reflectionClass);          // 遍历类的注解     // Register annotation parser      foreach ($classAnnotations as $classAnnotation) {         // 如果是AnnotationParser的实例         if ($classAnnotation instanceof AnnotationParser)  {             // 将此注解对象注册到注解parser             $this->registerParser($className, $classAnnotation);             // 不再持续遍历以后类的其它注解,并返回[]             return [];         }     }          // 如果存在类注解,则将所有的类注解和反射类自身放入返回值中     // Class annotation     if (!empty($classAnnotations)) {         $oneClassAnnotation['annotation'] = $classAnnotations;         $oneClassAnnotation['reflection'] = $reflectionClass;     }          // Property annotation     // 获取类的所有属性     $reflectionProperties = $reflectionClass->getProperties();          // 遍历类的属性     foreach ($reflectionProperties as $reflectionProperty) {         // 获取属性名         $propertyName = $reflectionProperty->getName();         // 获取属性的注解         $propertyAnnotations = $reader->getPropertyAnnotations($reflectionProperty);         // 如果属性的注解非空,则将属性的注解和属性的反射对象放入返回值中         if (!empty($propertyAnnotations)) {             $oneClassAnnotation['properties'][$propertyName]['annotation'] = $propertyAnnotations;             $oneClassAnnotation['properties'][$propertyName]['reflection'] = $reflectionProperty;         }     }          // Method annotation     // 获取反射类的所有办法     $reflectionMethods = $reflectionClass->getMethods();     // 遍历反射类的所有办法     foreach ($reflectionMethods as $reflectionMethod) {         // 获取办法名         $methodName = $reflectionMethod->getName();         // 获取办法的注解对象         $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; }

总结:

1.swoft的注解底层依赖于Doctrine的annotations包.2.注解处理器的工作流程:    (1).run()调用AnnotationProsser的handle办法.    (2).handle()调用Swoft\Annotation\AnnotationRegister::load办法.    (3).第(2)步的load办法调用Swoft\Annotation\Resource\AnnotationResource的load办法.    (4).第(3)步的load办法通过composer的autoloader获取到所有的命名空间和源码目录的映射.        而后遍历获取目录内的AutoLoader类,而后调用loadAnnotation传入autoloader实例.        同时将这个命名空间下的autoloader注册到Swoft\Annotation\AnnotationRegister.    (5).loadAnnotation依据传进来的loader获取到其对应目录内的所有文件.        而后遍历这些文件找到非法的库类文件,同时将目录内未加载的类加载.        而后将命名空间和类名传给parseAnnotation办法解决.    (6).parseAnnotation首先创立传进来的类的反射对象,而后调用parseOneClassAnnotation办法.        parseOneClassAnnotation办法调用Doctrine的reader办法.        获取以后类的类注解、属性注解、办法注解.        而后递归调用本身,获取父类的注解对象.    (7).parseAnnotation调用Swoft\Annotation\AnnotationRegister::registerAnnotation办法,将这个类的注解进行注册.3.通过下面的步骤,注解处理器的工作就全副实现.  注解处理器的工作就是获取所有非法的注解对象,而后进行注册,并未应用获取的注解对象.