x2nx / webman-annotation
Webman Annotation plugin - Production-ready annotation system for webman framework with security, performance and reliability
Installs: 13
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/x2nx/webman-annotation
Requires
- php: >=8.1
- webman/cache: ^2.0
- workerman/webman-framework: ^2.0
Requires (Dev)
- phpunit/phpunit: ^10.0
Suggests
- psr/container: ^2.0
- webman/channel: ^1.0
- webman/event: ^1.0
- webman/log: ^2.0
- workerman/crontab: ^1.0
README
一个功能完整、生产就绪的 Webman 框架注解插件,支持路由、中间件、依赖注入、定时任务、事件监听和自定义注解等功能。
📋 目录
✨ 功能特性
核心功能
- ✅ 路由注解 - 支持 8 种 HTTP 方法注解(GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, HEAD)
- ✅ 中间件注解 - 支持类级别和方法级别的中间件配置
- ✅ 依赖注入 - 支持
#[Inject]和#[Value]注解自动注入 - ✅ Bean 管理 - 使用
#[Bean]注解管理单例对象 - ✅ 定时任务 - 使用
#[Cron]注解定义定时任务,支持分布式锁 - ✅ 事件监听 - 使用
#[Event]注解注册事件监听器 - ✅ 自定义注解 - 支持用户自定义注解和处理器
高级特性
- ✅ 循环依赖自动处理 - 自动检测并处理循环依赖,用户无感
- ✅ 懒加载支持 -
#[Inject]支持懒加载,延迟实例化 - ✅ 注解白名单/黑名单 - 只解析已实现和配置的注解,提高性能
- ✅ 性能优化 - 静态缓存、扫描优化,减少反射开销
- ✅ 多进程安全 - 定时任务支持分布式锁,确保多进程环境下不重复执行
- ✅ 程序内调用 - 支持在代码中手动执行自定义注解
🚀 安装
使用 Composer 安装
composer require x2nx/webman-annotation
系统要求
- PHP >= 8.1
- Webman Framework >= 2.0
- Workerman Crontab >= 1.0
可选依赖
webman/log>= 2.0 - 用于日志记录webman/event>= 1.0 - 用于事件监听功能webman/channel>= 1.0 - 用于定时任务动态注册webman/cache>= 2.0 - 用于缓存和分布式锁(已包含在 require 中)
🎯 快速开始
1. 安装插件
composer require x2nx/webman-annotation
2. 配置文件
安装后,配置文件会自动复制到 config/plugin/x2nx/webman-annotation/ 目录。
3. 创建第一个注解路由
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\GetMapping; class IndexController { #[GetMapping('/')] public function index() { return json(['message' => 'Hello Webman Annotation!']); } }
4. 重启服务
php start.php restart
访问 http://localhost:8787/ 即可看到响应。
⚙️ 配置说明
主配置文件 (app.php)
配置文件位置:config/plugin/x2nx/webman-annotation/app.php
<?php return [ // ========== 基础配置 ========== // 是否启用注解功能 'enable' => true, // 是否启用缓存(生产环境建议开启) 'enable_cache' => false, // ========== 扫描配置 ========== // 扫描目录(递归扫描子目录) 'scan_dirs' => [ app_path(), ], // 排除目录(这些目录不会被扫描) 'exclude_dirs' => [ 'vendor', 'runtime', 'config', 'public', ], // ========== 自动注册配置 ========== // 是否自动注册路由 'auto_register_routes' => true, // 是否自动注册中间件 'auto_register_middleware' => true, // 是否自动注册 Bean 'auto_register_beans' => true, // 是否自动注册定时任务 'auto_register_crons' => true, // 是否自动注册事件监听器 'auto_register_events' => true, // 是否启用值注入 'enable_value_injection' => true, // ========== 自定义注解配置 ========== // 自定义注解映射(格式:'AnnotationClass' => 'HandlerClass') 'annotations' => [ // \app\annotation\MyAnnotation::class => \app\annotation\MyAnnotationHandler::class, ], // ========== 黑名单配置 ========== // 排除特定注解、类或命名空间(提高扫描性能) 'blacklist' => [ // 排除特定注解类(即使它们在白名单中) 'annotations' => [ // \Some\Package\UnwantedAnnotation::class, ], // 排除特定类(不扫描这些类) 'classes' => [ // \app\legacy\OldController::class, ], // 排除整个命名空间(该命名空间下的所有类都会被跳过) 'namespaces' => [ // 'app\legacy', // 'app\deprecated', ], ], // ========== 缓存配置 ========== // 缓存存储名称(对应 config/cache.php 中的 stores,空值使用默认存储) 'cache_store' => '', // 缓存键前缀 'cache_prefix' => 'annotation:', // 缓存过期时间(秒),默认 24 小时 'cache_ttl' => 86400, // ========== 日志配置 ========== // 日志通道(对应 config/log.php 中的配置) 'log_channel' => 'default', // ========== 定时任务监控配置 ========== 'cron_monitor' => [ // 是否启用定时任务监控进程 'enable' => true, // 健康检查间隔(秒) 'check_interval' => 60, // 是否启用自动恢复 'auto_recovery' => true, // 最大连续失败次数 'max_failures' => 3, ], // ========== Channel 配置 ========== // webman/channel 配置(用于定时任务动态注册) 'channel' => [ 'host' => '127.0.0.1', 'port' => 2206, ], ];
进程配置 (process.php)
定时任务监控进程配置(已自动配置,通常无需修改):
<?php return [ 'cron-monitor' => [ 'handler' => \X2nx\WebmanAnnotation\Process\CronMonitor::class, 'count' => 1, 'reloadable' => false, ], ];
中间件配置 (middleware.php)
自定义注解中间件配置(已自动配置):
<?php return [ '' => [ \X2nx\WebmanAnnotation\Middleware\AnnotationsMiddleware::class, ], ];
🛣️ 路由注解
支持的 HTTP 方法注解
本包支持以下 HTTP 方法注解:
#[GetMapping]- GET 请求#[PostMapping]- POST 请求#[PutMapping]- PUT 请求#[PatchMapping]- PATCH 请求#[DeleteMapping]- DELETE 请求#[OptionsMapping]- OPTIONS 请求#[TraceMapping]- TRACE 请求#[Route]- 支持多种 HTTP 方法
基础用法
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\GetMapping; use X2nx\WebmanAnnotation\Attributes\PostMapping; use X2nx\WebmanAnnotation\Attributes\PutMapping; use X2nx\WebmanAnnotation\Attributes\PatchMapping; use X2nx\WebmanAnnotation\Attributes\DeleteMapping; use X2nx\WebmanAnnotation\Attributes\OptionsMapping; use X2nx\WebmanAnnotation\Attributes\TraceMapping; use X2nx\WebmanAnnotation\Attributes\Route; class UserController { // GET 请求 #[GetMapping('/users')] public function list() { return json(['code' => 0, 'data' => []]); } // POST 请求 #[PostMapping('/users')] public function create() { return json(['code' => 0, 'msg' => 'success']); } // PUT 请求 #[PutMapping('/users/{id}')] public function update($id) { return json(['code' => 0, 'msg' => 'updated', 'id' => $id]); } // PATCH 请求 #[PatchMapping('/users/{id}')] public function partialUpdate($id) { return json(['code' => 0, 'msg' => 'partially updated', 'id' => $id]); } // DELETE 请求 #[DeleteMapping('/users/{id}')] public function delete($id) { return json(['code' => 0, 'msg' => 'deleted', 'id' => $id]); } // OPTIONS 请求(用于 CORS 预检) #[OptionsMapping('/users/{id}')] public function options($id) { return response('', 200) ->withHeaders([ 'Access-Control-Allow-Methods' => 'GET, POST, PUT, PATCH, DELETE, OPTIONS', ]); } // TRACE 请求(用于调试) #[TraceMapping('/users/trace')] public function trace() { return json(['method' => 'TRACE', 'message' => 'Trace request']); } // 使用 Route 注解(需要指定 HTTP 方法) #[Route('GET', '/users/{id}')] public function show($id) { return json(['code' => 0, 'data' => ['id' => $id]]); } // 支持多种 HTTP 方法(使用多个注解) #[GetMapping('/users/{id}/info')] #[PostMapping('/users/{id}/info')] public function info($id) { return json(['code' => 0, 'data' => ['id' => $id]]); } }
路由前缀和分组
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\RoutePrefix; use X2nx\WebmanAnnotation\Attributes\RouteGroup; // RoutePrefix 的别名 use X2nx\WebmanAnnotation\Attributes\Controller; // 另一种写法 use X2nx\WebmanAnnotation\Attributes\GetMapping; use X2nx\WebmanAnnotation\Attributes\PostMapping; // 方式 1: 使用 RoutePrefix #[RoutePrefix('/api/v1/user')] class UserController { #[GetMapping('/list')] // 实际路径: /api/v1/user/list public function list() { } #[PostMapping('/create')] // 实际路径: /api/v1/user/create public function create() { } } // 方式 2: 使用 RouteGroup(RoutePrefix 的别名) #[RouteGroup('/api/v2/user')] class UserV2Controller { #[GetMapping('/list')] // 实际路径: /api/v2/user/list public function list() { } } // 方式 3: 使用 Controller #[Controller(prefix: '/api/v3/user')] class UserV3Controller { #[GetMapping('/list')] // 实际路径: /api/v3/user/list public function list() { } }
路由命名
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\GetMapping; use X2nx\WebmanAnnotation\Attributes\Route; class UserController { // 为路由指定名称,便于反向生成 URL #[GetMapping('/users/{id}', name: 'user.show')] public function show($id) { return json(['id' => $id]); } // 使用 Route 注解也可以指定名称 #[Route('GET', '/users/{id}/edit', name: 'user.edit')] public function edit($id) { return json(['id' => $id]); } }
路由参数
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\GetMapping; class UserController { // 路径参数 #[GetMapping('/users/{id}')] public function show($id) { return json(['id' => $id]); } // 多个路径参数 #[GetMapping('/users/{userId}/posts/{postId}')] public function showPost($userId, $postId) { return json(['user_id' => $userId, 'post_id' => $postId]); } // 可选参数(Webman 路由特性) #[GetMapping('/users/{id?}')] public function listOrShow($id = null) { if ($id) { return json(['id' => $id]); } return json(['list' => []]); } }
🛡️ 中间件注解
类级别中间件
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Middleware; use X2nx\WebmanAnnotation\Attributes\GetMapping; use app\middleware\AuthMiddleware; use app\middleware\LogMiddleware; // 类级别的中间件会应用到所有方法 #[Middleware([AuthMiddleware::class, LogMiddleware::class])] class UserController { #[GetMapping('/profile')] public function profile() { // 会先执行 AuthMiddleware 和 LogMiddleware return json(['code' => 0, 'data' => []]); } }
方法级别中间件
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Middleware; use X2nx\WebmanAnnotation\Attributes\GetMapping; use app\middleware\AuthMiddleware; use app\middleware\RateLimitMiddleware; #[Middleware([AuthMiddleware::class])] class UserController { // 方法级别的中间件会合并类级别的中间件 // 执行顺序:AuthMiddleware (类) -> RateLimitMiddleware (方法) #[GetMapping('/profile')] #[Middleware([RateLimitMiddleware::class])] public function profile() { return json(['code' => 0, 'data' => []]); } // 只执行类级别的中间件 #[GetMapping('/info')] public function info() { return json(['code' => 0, 'data' => []]); } }
多个中间件
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Middleware; use X2nx\WebmanAnnotation\Attributes\PostMapping; class OrderController { #[PostMapping('/orders')] #[Middleware([ \app\middleware\AuthMiddleware::class, \app\middleware\RateLimitMiddleware::class, \app\middleware\ValidationMiddleware::class, ])] public function create() { // 按顺序执行:AuthMiddleware -> RateLimitMiddleware -> ValidationMiddleware return json(['code' => 0, 'msg' => 'success']); } }
💉 依赖注入
#[Value] 注解 - 配置值注入
#[Value] 注解用于注入配置值或环境变量。
基础用法
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Value; class IndexController { // 注入配置值 #[Value(key: 'app.name', default: 'MyApp')] protected string $appName; // 注入环境变量 #[Value(key: 'env:APP_DEBUG', default: false)] protected bool $debug; // 注入嵌套配置 #[Value(key: 'database.default.host', default: 'localhost')] protected string $dbHost; public function index() { return json([ 'app_name' => $this->appName, 'debug' => $this->debug, 'db_host' => $this->dbHost, ]); } }
支持的键格式
<?php namespace app\service; use X2nx\WebmanAnnotation\Attributes\Value; class ConfigService { // 配置键(使用点号分隔) #[Value(key: 'app.name')] protected string $appName; // 环境变量(使用 env: 前缀) #[Value(key: 'env:APP_DEBUG')] protected bool $debug; // 数组配置 #[Value(key: 'database.default')] protected array $database; // 带默认值 #[Value(key: 'app.timezone', default: 'Asia/Shanghai')] protected string $timezone; // 环境变量带默认值 #[Value(key: 'env:APP_ENV', default: 'production')] protected string $env; }
类型转换
<?php namespace app\service; use X2nx\WebmanAnnotation\Attributes\Value; class ConfigService { // 自动类型转换 #[Value(key: 'app.port', default: 8080)] protected int $port; // 自动转换为 int #[Value(key: 'app.debug', default: false)] protected bool $debug; // 自动转换为 bool #[Value(key: 'app.allowed_hosts', default: [])] protected array $allowedHosts; // 自动转换为 array }
#[Inject] 注解 - 服务注入
#[Inject] 注解用于注入服务依赖。
基础用法(类型提示注入)
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Inject; use app\service\UserService; class UserController { // 通过类型提示自动注入 #[Inject] protected UserService $userService; public function index() { $users = $this->userService->getAll(); return json(['code' => 0, 'data' => $users]); } }
命名注入
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Inject; class UserController { // 通过名称注入(从容器中获取名为 'userService' 的服务) #[Inject(name: 'userService')] protected $userService; // 也可以指定类型 #[Inject(name: 'logger')] protected \Psr\Log\LoggerInterface $logger; }
懒加载
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Inject; use app\service\HeavyService; class UserController { // 懒加载:只有在第一次访问时才创建实例 #[Inject(lazy: true)] protected HeavyService $heavyService; public function index() { // 此时 heavyService 还没有被创建 // ... // 第一次访问时才会创建实例 $result = $this->heavyService->process(); return json(['code' => 0, 'data' => $result]); } }
注意:懒加载仅适用于属性类型为 object 或 mixed,或者没有类型提示的情况。
循环依赖自动处理
本包自动检测并处理循环依赖,用户无需修改代码:
<?php namespace app\service; use X2nx\WebmanAnnotation\Attributes\Inject; // ServiceA 依赖 ServiceB class ServiceA { #[Inject] protected ServiceB $serviceB; public function getName(): string { return 'ServiceA'; } public function getServiceB(): ServiceB { return $this->serviceB; } } // ServiceB 依赖 ServiceA(循环依赖) class ServiceB { #[Inject] protected ServiceA $serviceA; public function getName(): string { return 'ServiceB'; } public function getServiceA(): ServiceA { return $this->serviceA; } } // 在控制器中使用 class UserController { #[Inject] protected ServiceA $serviceA; public function index() { // 循环依赖已自动处理,可以直接使用 $serviceB = $this->serviceA->getServiceB(); $serviceA = $serviceB->getServiceA(); // $serviceA === $this->serviceA (true) return json(['success' => true]); } }
工作原理:
- 系统自动检测循环依赖
- 使用正在构建的实例打破循环
- 不抛出异常,用户无感
- 记录警告日志(用于调试)
组合使用
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Inject; use X2nx\WebmanAnnotation\Attributes\Value; use app\service\UserService; class UserController { // 同时使用 Value 和 Inject #[Value(key: 'app.name')] protected string $appName; #[Inject] protected UserService $userService; #[Value(key: 'env:APP_DEBUG', default: false)] protected bool $debug; public function index() { return json([ 'app_name' => $this->appName, 'users' => $this->userService->getAll(), 'debug' => $this->debug, ]); } }
🏭 Bean 管理
使用 #[Bean] 注解将类注册为单例对象到容器中。
基础用法
<?php namespace app\service; use X2nx\WebmanAnnotation\Attributes\Bean; // 注册为单例,使用类名作为服务名 #[Bean] class UserService { public function getAll() { return ['user1', 'user2']; } } // 在其他地方使用 use support\Container; $userService = Container::get(\app\service\UserService::class); $users = $userService->getAll();
命名 Bean
<?php namespace app\service; use X2nx\WebmanAnnotation\Attributes\Bean; // 注册为命名 Bean #[Bean('userService')] class UserService { public function getAll() { return ['user1', 'user2']; } } // 在其他地方使用 use support\Container; $userService = Container::get('userService'); $users = $userService->getAll();
Bean 与依赖注入结合
<?php namespace app\service; use X2nx\WebmanAnnotation\Attributes\Bean; use X2nx\WebmanAnnotation\Attributes\Inject; use X2nx\WebmanAnnotation\Attributes\Value; #[Bean('orderService')] class OrderService { #[Value(key: 'app.name')] protected string $appName; #[Inject] protected UserService $userService; public function createOrder() { // 可以使用注入的依赖 $users = $this->userService->getAll(); return ['order' => 'created', 'app' => $this->appName]; } }
⏰ 定时任务
使用 #[Cron] 注解定义定时任务。
基础用法
<?php namespace app\task; use X2nx\WebmanAnnotation\Attributes\Cron; use X2nx\WebmanAnnotation\Attributes\Value; class CleanupTask { #[Value(key: 'app.name')] protected string $appName; /** * 每5秒执行一次(每次创建新实例) */ #[Cron(expression: '*/5 * * * * *', singleton: false)] public function cleanup() { echo "[{$this->appName}] Cleanup task executed at " . date('Y-m-d H:i:s') . "\n"; // 执行清理逻辑 } /** * 每天凌晨2点执行(使用单例模式) */ #[Cron(expression: '0 2 * * *', singleton: true)] public function dailyReport() { echo "Daily report generated\n"; } }
Cron 表达式格式
格式:秒 分 时 日 月 周
常用示例:
// 每5秒执行 #[Cron(expression: '*/5 * * * * *')] // 每10分钟执行 #[Cron(expression: '0 */10 * * * *')] // 每小时执行 #[Cron(expression: '0 0 * * * *')] // 每天凌晨2点执行 #[Cron(expression: '0 0 2 * * *')] // 每周一凌晨3点执行 #[Cron(expression: '0 0 3 * * 1')] // 每月1号凌晨4点执行 #[Cron(expression: '0 0 4 1 * *')] // 工作日上午9点执行 #[Cron(expression: '0 0 9 * * 1-5')]
参数说明
#[Cron(
expression: '*/5 * * * * *', // Cron 表达式(必填)
singleton: true // 是否使用单例模式(默认:true)
)]
- expression: Cron 表达式(秒级精度),格式:
秒 分 时 日 月 周 - singleton:
true- 使用单例模式,所有执行共享同一个实例(默认)false- 每次执行创建新实例
注意:multiProcess 参数已移除,定时任务默认使用分布式锁确保只在一个进程中执行。如果需要多进程执行,请使用动态注册方式。
依赖注入支持
定时任务支持 #[Value] 和 #[Inject] 注解:
<?php namespace app\task; use X2nx\WebmanAnnotation\Attributes\Cron; use X2nx\WebmanAnnotation\Attributes\Value; use X2nx\WebmanAnnotation\Attributes\Inject; use app\service\UserService; class ReportTask { #[Value(key: 'app.name')] protected string $appName; #[Inject] protected UserService $userService; #[Cron(expression: '0 0 * * * *')] public function hourlyReport() { $users = $this->userService->getAll(); echo "[{$this->appName}] Hourly report: " . count($users) . " users\n"; } }
动态注册定时任务
除了使用注解,还可以动态注册定时任务:
<?php use X2nx\WebmanAnnotation\Helper\CronHelper; // 注册类方法 $taskId = CronHelper::register( expression: '*/10 * * * * *', class: \app\task\MyTask::class, method: 'execute', name: 'MyTask::execute', singleton: true, multiProcess: false ); // 注册回调函数 $taskId = CronHelper::registerCallable( expression: '0 * * * * *', callback: function() { echo "Task executed\n"; }, name: 'my-callback-task' ); // 取消注册 CronHelper::unregister($taskId); // 获取所有任务 $tasks = CronHelper::getAll();
多进程和分布式锁
定时任务默认使用分布式锁(基于 webman/cache)确保在多进程环境下不会重复执行:
- 首先尝试使用 Cache(支持 file、redis 等驱动)
- 如果 Cache 不可用,任务将跳过执行
锁的 TTL 为 300 秒(5分钟),确保即使进程异常退出,锁也会自动释放。
📢 事件监听
使用 #[Event] 注解注册事件监听器。
基础用法
<?php namespace app\listener; use X2nx\WebmanAnnotation\Attributes\Event; class UserListener { // 监听 user.created 事件 #[Event('user.created')] public function handleUserCreated($user) { // 处理用户创建事件 echo "User created: {$user['name']}\n"; } // 监听 user.updated 事件,设置优先级 #[Event('user.updated', priority: 10)] public function handleUserUpdated($user) { // 处理用户更新事件(优先级 10,数字越小优先级越高) echo "User updated: {$user['name']}\n"; } }
触发事件
<?php use Webman\Event\Event; // 触发事件 Event::emit('user.created', ['name' => 'John', 'email' => 'john@example.com']); // 或者使用 dispatch(如果支持) Event::dispatch('user.updated', ['name' => 'Jane', 'email' => 'jane@example.com']);
一个方法监听多个事件
<?php namespace app\listener; use X2nx\WebmanAnnotation\Attributes\Event; class LogListener { // 一个方法可以监听多个事件 #[Event('user.created')] #[Event('user.updated')] #[Event('user.deleted')] public function handleUserEvents($data, $eventName) { // $data 是事件数据 // $eventName 是事件名称 echo "Event {$eventName} triggered with data: " . json_encode($data) . "\n"; } }
优先级
<?php namespace app\listener; use X2nx\WebmanAnnotation\Attributes\Event; class UserListener { // 优先级 1(最高优先级,最先执行) #[Event('user.created', priority: 1)] public function validateUser($user) { // 验证用户数据 } // 优先级 10(较低优先级,后执行) #[Event('user.created', priority: 10)] public function sendWelcomeEmail($user) { // 发送欢迎邮件 } // 无优先级(默认优先级) #[Event('user.created')] public function logUserCreation($user) { // 记录日志 } }
依赖注入支持
事件监听器支持 #[Value] 和 #[Inject] 注解:
<?php namespace app\listener; use X2nx\WebmanAnnotation\Attributes\Event; use X2nx\WebmanAnnotation\Attributes\Value; use X2nx\WebmanAnnotation\Attributes\Inject; use app\service\EmailService; class UserListener { #[Value(key: 'app.name')] protected string $appName; #[Inject] protected EmailService $emailService; #[Event('user.created')] public function handleUserCreated($user) { // 可以使用注入的依赖 $this->emailService->send( $user['email'], "Welcome to {$this->appName}!" ); } }
🎨 自定义注解
支持用户自定义注解和处理器。
1. 创建注解类
<?php namespace app\annotation; use Attribute; #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] class Cache { public function __construct( public int $ttl = 3600, public string $key = '' ) { } }
2. 创建处理器
<?php namespace app\annotation; use X2nx\WebmanAnnotation\Contracts\AnnotationsHandlerInterface; use ReflectionClass; use ReflectionMethod; use ReflectionProperty; class CacheHandler implements AnnotationsHandlerInterface { public function handle( object $attribute, ReflectionClass $class, ?ReflectionMethod $method, ?ReflectionProperty $property ): void { // 处理注解逻辑 if ($method) { echo "Cache annotation on method: {$class->getName()}::{$method->getName()}\n"; echo "TTL: {$attribute->ttl}, Key: {$attribute->key}\n"; } elseif ($class) { echo "Cache annotation on class: {$class->getName()}\n"; } } }
3. 配置注解映射
在 config/plugin/x2nx/webman-annotation/app.php 中配置:
return [ 'annotations' => [ \app\annotation\Cache::class => \app\annotation\CacheHandler::class, ], ];
4. 使用自定义注解
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\GetMapping; use app\annotation\Cache; class UserController { // 在方法上使用自定义注解 #[GetMapping('/users')] #[Cache(ttl: 3600, key: 'users:list')] public function list() { return json(['code' => 0, 'data' => []]); } }
5. 程序内调用自定义注解
<?php use X2nx\WebmanAnnotation\Helper\AnnotationsExecutor; // 执行类上的所有自定义注解 $result = AnnotationsExecutor::executeClass(\app\controller\UserController::class); // 执行方法上的自定义注解 $result = AnnotationsExecutor::executeMethod(\app\controller\UserController::class, 'list'); // 执行属性上的自定义注解 $result = AnnotationsExecutor::executeProperty(\app\controller\UserController::class, 'userService'); // 检查执行结果 if ($result['success']) { echo "成功执行了 {$result['handled']} 个自定义注解\n"; } else { echo "执行失败: " . implode(', ', $result['errors']) . "\n"; }
使用场景:
- 在定时任务中手动触发注解
- 在事件监听器中执行注解
- 在命令行脚本中执行注解
- 在单元测试中验证注解行为
⚡ 性能优化
注解白名单/黑名单
本包实现了注解白名单和黑名单机制,只解析已实现和配置中注册的注解,大幅提高扫描性能。
白名单(自动包含)
以下注解会自动包含在白名单中:
- 路由注解:
Route,RoutePrefix,RouteGroup,Controller,HttpGet,PostMapping,PutMapping,PatchMapping,DeleteMapping,OptionsMapping,TraceMapping - 中间件:
Middleware - 依赖注入:
Value,Inject - Bean:
Bean - 定时任务:
Cron - 事件:
Event - 自定义注解:配置在
annotations中的注解
黑名单配置
return [ 'blacklist' => [ // 排除特定注解类(即使它们在白名单中) 'annotations' => [ \Some\Package\UnwantedAnnotation::class, ], // 排除特定类(不扫描这些类) 'classes' => [ \app\legacy\OldController::class, ], // 排除整个命名空间 'namespaces' => [ 'app\legacy', 'app\deprecated', ], ], ];
缓存机制
return [ // 启用缓存(生产环境建议开启) 'enable_cache' => true, // 缓存配置 'cache_store' => 'redis', // 使用 Redis 缓存 'cache_prefix' => 'annotation:', 'cache_ttl' => 86400, // 24 小时 ];
扫描优化
- 自动跳过没有白名单注解的类
- 静态缓存反射结果
- 减少重复扫描
🔧 高级功能
循环依赖自动处理
本包自动检测并处理循环依赖,用户无需修改代码。详见 CIRCULAR_DEPENDENCY.md。
懒加载
使用 #[Inject(lazy: true)] 实现懒加载:
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Inject; use app\service\HeavyService; class UserController { // 懒加载:只有在第一次访问时才创建实例 #[Inject(lazy: true)] protected HeavyService $heavyService; public function index() { // 第一次访问时才会创建实例 return $this->heavyService->process(); } }
注意:懒加载仅适用于属性类型为 object 或 mixed,或者没有类型提示的情况。
动态任务注册
<?php use X2nx\WebmanAnnotation\Helper\CronHelper; // 在运行时动态注册任务 $taskId = CronHelper::register( expression: '*/30 * * * * *', class: \app\task\DynamicTask::class, method: 'execute', name: 'DynamicTask', singleton: true, multiProcess: false ); // 取消注册 CronHelper::unregister($taskId);
❓ 常见问题
Q: 路由注解不生效?
A: 检查以下几点:
- 确保
auto_register_routes配置为true - 确保控制器类在
scan_dirs配置的目录中 - 重启 Webman 服务:
php start.php restart - 检查路由文件
config/plugin/x2nx/webman-annotation/route.php是否存在
Q: 依赖注入失败?
A: 检查以下几点:
- 确保
enable_value_injection配置为true - 确保属性类型提示正确
- 检查容器配置
config/container.php是否正确 - 查看日志文件中的错误信息
Q: 定时任务不执行?
A: 检查以下几点:
- 确保
auto_register_crons配置为true - 确保
cron_monitor.enable配置为true - 检查进程配置
config/plugin/x2nx/webman-annotation/process.php - 查看日志文件中的错误信息
- 确保
workerman/crontab已安装
Q: 事件监听器不执行?
A: 检查以下几点:
- 确保
auto_register_events配置为true - 确保
webman/event已安装 - 检查事件名称是否正确
- 查看日志文件中的错误信息
Q: 循环依赖如何处理?
A: 本包自动检测并处理循环依赖,用户无需修改代码。系统会:
- 自动检测循环依赖
- 使用正在构建的实例打破循环
- 不抛出异常,用户无感
- 记录警告日志(用于调试)
Q: 如何提高扫描性能?
A:
- 启用缓存:
enable_cache => true - 配置黑名单,排除不需要扫描的类
- 使用 Redis 作为缓存驱动
- 减少扫描目录范围
📚 API 参考
注解类
路由注解
X2nx\WebmanAnnotation\Attributes\RouteX2nx\WebmanAnnotation\Attributes\GetMappingX2nx\WebmanAnnotation\Attributes\PostMappingX2nx\WebmanAnnotation\Attributes\PutMappingX2nx\WebmanAnnotation\Attributes\PatchMappingX2nx\WebmanAnnotation\Attributes\DeleteMappingX2nx\WebmanAnnotation\Attributes\OptionsMappingX2nx\WebmanAnnotation\Attributes\TraceMappingX2nx\WebmanAnnotation\Attributes\RoutePrefixX2nx\WebmanAnnotation\Attributes\RouteGroupX2nx\WebmanAnnotation\Attributes\Controller
其他注解
X2nx\WebmanAnnotation\Attributes\MiddlewareX2nx\WebmanAnnotation\Attributes\ValueX2nx\WebmanAnnotation\Attributes\InjectX2nx\WebmanAnnotation\Attributes\BeanX2nx\WebmanAnnotation\Attributes\CronX2nx\WebmanAnnotation\Attributes\Event
工具类
X2nx\WebmanAnnotation\Helper\AnnotationsExecutor- 程序内执行自定义注解X2nx\WebmanAnnotation\Helper\CronHelper- 动态注册定时任务X2nx\WebmanAnnotation\Helper\AnnotationWhitelist- 注解白名单/黑名单管理
接口
X2nx\WebmanAnnotation\Contracts\AnnotationsHandlerInterface- 自定义注解处理器接口
🎯 最佳实践
1. 路由组织
<?php namespace app\controller\api\v1; use X2nx\WebmanAnnotation\Attributes\RoutePrefix; use X2nx\WebmanAnnotation\Attributes\GetMapping; use X2nx\WebmanAnnotation\Attributes\PostMapping; #[RoutePrefix('/api/v1/users')] class UserController { #[GetMapping('')] public function list() { } #[PostMapping('')] public function create() { } #[GetMapping('/{id}')] public function show($id) { } }
2. 依赖注入
<?php namespace app\controller; use X2nx\WebmanAnnotation\Attributes\Inject; use X2nx\WebmanAnnotation\Attributes\Value; use app\service\UserService; class UserController { // 使用类型提示注入(推荐) #[Inject] protected UserService $userService; // 配置值注入 #[Value(key: 'app.name')] protected string $appName; }
3. 定时任务
<?php namespace app\task; use X2nx\WebmanAnnotation\Attributes\Cron; class CleanupTask { // 使用单例模式,减少内存占用 #[Cron(expression: '0 2 * * *', singleton: true)] public function dailyCleanup() { // 清理逻辑 } // 高频任务使用多进程执行 #[Cron(expression: '*/5 * * * * *', singleton: false, multiProcess: true)] public function frequentTask() { // 高频任务逻辑 } }
4. 事件监听
<?php namespace app\listener; use X2nx\WebmanAnnotation\Attributes\Event; class UserListener { // 使用优先级控制执行顺序 #[Event('user.created', priority: 1)] public function validate($user) { } #[Event('user.created', priority: 10)] public function sendEmail($user) { } }
5. 性能优化
// config/plugin/x2nx/webman-annotation/app.php return [ // 生产环境启用缓存 'enable_cache' => true, 'cache_store' => 'redis', // 配置黑名单,排除不需要扫描的类 'blacklist' => [ 'namespaces' => [ 'app\legacy', 'app\deprecated', ], ], ];
📝 更新日志
详见 CHANGELOG.md(如果存在)
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 许可证
MIT License
🔗 相关链接
Made with ❤️ for Webman Framework