从bin/swoft开始,阅读Swoft框架源码(五)--AnnotationProcessor

马尔科夫尼可夫

注解是Swoft的特色之一,Swoft项目中几乎所有的业务代码中都离不开注解.
AnnotationProcessor处理器就是Swoft能在业务中使用组件的核心依赖.

通过注解处理器处理后,会在注解处理器中保存一个这一的数据结构:

/**swoft中关于annotations数组结构的注释:
 * @var array
 * * @example
 * [
 *    'loadNamespace' => [ //注解的命名空间
 *        'className' => [ //调用注解的类的类名
 *             'annotation' => [ //调用的类注解对象集合
 *                  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(), //调用类方法的反射对象
 *                  ] 
 *            ] 
 *        ] 
 *    ] 
 * ] 
 */

先看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.经过上面的步骤,注解处理器的工作就全部完成.
  注解处理器的工作就是获取所有合法的注解对象,然后进行注册,并未使用获取的注解对象.
ps:只有被实际调用过的注解才会被收集,每多一个该注解的调用,注解解析器的parse方法就会多执行一次,如果未被调用,则一次也不会被执行.
阅读 265

酷白发,小酒窝,主角标配的帅小伙~

7 声望
2 粉丝
0 条评论
你知道吗?

酷白发,小酒窝,主角标配的帅小伙~

7 声望
2 粉丝
宣传栏