springbootAI 1.8.1__tar.gz → 1.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {springbootai-1.8.1/springbootAI.egg-info → springbootai-1.8.2}/PKG-INFO +18 -13
- {springbootai-1.8.1 → springbootai-1.8.2}/README.md +17 -12
- {springbootai-1.8.1 → springbootai-1.8.2}/pyproject.toml +1 -1
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/__init__.py +1 -1
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/utils/__init__.py +2 -1
- springbootai-1.8.2/spring/utils/bean_utils.py +314 -0
- {springbootai-1.8.1 → springbootai-1.8.2/springbootAI.egg-info}/PKG-INFO +18 -13
- {springbootai-1.8.1 → springbootai-1.8.2}/springbootAI.egg-info/SOURCES.txt +2 -0
- springbootai-1.8.2/tests/test_bean_utils.py +316 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/LICENSE +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/MANIFEST.in +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/requirements.txt +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/setup.cfg +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/setup.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/advisors.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/autoconfig.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/core.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/etl.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/memory.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/observability.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/providers.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/resilience.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/tools.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/ai/vectorstore.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/annotations/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/annotations/cache.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/annotations/cloud.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/annotations/conditional.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/annotations/core.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/annotations/messaging.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/aop/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/aop/cloud_aop.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/aop/comprehensive_aop.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/aop/method_interceptor.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/aop/proxy_factory.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/discovery.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/feign.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/gateway.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/load_balancer.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/seata.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/sentinel.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/cloud/tracer.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/config/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/config/binding.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/config/config_loader.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/context/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/context/application_context.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/context/bean_definition.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/context/bean_factory.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/context/registry.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/context/scanner.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/core/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/core/graceful_shutdown.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/core/typing_utils.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/converters.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/easy_csv.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/exceptions.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/reader.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/csv/writer.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/data/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/data/page.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/data/repository.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/data/specification.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/datasource/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/datasource/annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/datasource/context.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/datasource/dynamic.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/event/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/event/publisher.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/converters.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/easy_excel.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/exceptions.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/reader.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/style.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/excel/writer.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/accessor.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/auto_config.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/holder.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/locale.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/locale_resolver.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/message_source.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/middleware.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/properties.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/i18n/sources.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/logging/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/logging/loguru_logger.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/main.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/messaging/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/messaging/rabbitmq.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/monitoring/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/monitoring/prometheus.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/database.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/ddl_auto.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/migration.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/mybatis_integration.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/annotations/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/annotations/annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/cache/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/cache/cache.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/cache/redis_cache.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/circuit_breaker/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/configuration.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/core/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/core/sql_session.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/core/sql_session_factory.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/dialect/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/dialect/dialect.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/dynamic_sql/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/interceptor/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/interceptor/interceptor.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/mapper/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/mapper/mapper.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/metrics/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/metrics/metrics.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/pool/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/pool/connection_pool.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/security/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/security/access_control.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/security/password_encoder.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/security/sensitive_data_masker.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/security/sql_injection_detector.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/transaction/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/transaction/transaction.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/type_handler/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/type_handler/type_handler.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/version.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/xml_parser/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/orm/pymybatis/xml_parser/xml_parser.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/retry/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/retry/retry_annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/retry/retry_decorator.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/scheduling/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/scheduling/scheduler.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/security/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/security/jwt_utils.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/security/replay_protection.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/security/secret_manager.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/security/security_aop.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/security/security_context.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/test/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/test/slicing.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/tracing/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/tracing/skywalking.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/tx/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/tx/events.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/tx/synchronization.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/utils/banner.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/utils/logger.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/utils/redis_client.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/validation/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/validation/aop.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/validation/constraints.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/validation/exceptions.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/validation/validator.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/actuator.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/exception_handler.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/health.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/interceptor.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/result.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/swagger.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/web/web_context.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/__init__.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/broker.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/exceptions.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/handler.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/router.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/spring/websocket/session.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/springbootAI.egg-info/dependency_links.txt +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/springbootAI.egg-info/entry_points.txt +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/springbootAI.egg-info/requires.txt +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/springbootAI.egg-info/top_level.txt +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/sql/seata_undo_log.sql +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_actuator.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_ai_module.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_annotation_combinations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_annotations_contract.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_aop_annotations_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_cache_annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_cloud_embedded_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_conditional_annotations.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_config_binding.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_connection_resilience.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_core_annotations_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_csv_module.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_data_repository.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_datasource_routing.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_ddl_auto.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_di_config_event_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_excel_module.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_i18n_module.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_jpa_version_transient.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_new_features.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_orm_pymybatis_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_production_readiness.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_pymybatis_contract.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_security.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_security_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_swagger_module.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_test_slicing.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_transactional_events.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_validation_module.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_web_annotations_full.py +0 -0
- {springbootai-1.8.1 → springbootai-1.8.2}/tests/test_websocket_module.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: springbootAI
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.2
|
|
4
4
|
Summary: Python版Spring Boot框架,集成PyMyBatis ORM
|
|
5
5
|
Author-email: YuConggen <1516933915@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -86,7 +86,7 @@ Dynamic: license-file
|
|
|
86
86
|
|
|
87
87
|
SpringBootAI 是一个借鉴 Spring Boot 编程模型的 Python Web 框架,提供装饰器式组件扫描、依赖注入、FastAPI 路由、配置加载、安全能力、内嵌的 PyMyBatis ORM,以及企业级 AI 模块(对齐 Spring AI 2.0:ChatClient/Advisor/Tools/RAG/Function Calling)。本指南为框架核心综合使用文档;**AI / ORM / Cloud / Excel 等模块的完整注解与功能说明已分离为独立文档**(见下方“模块文档”),本指南相应章节保留概览与跳转链接。
|
|
88
88
|
|
|
89
|
-
- SpringBootAI 版本:`1.8.
|
|
89
|
+
- SpringBootAI 版本:`1.8.2`
|
|
90
90
|
- 内嵌 PyMyBatis 版本:`1.4.0`
|
|
91
91
|
- Python:3.10+
|
|
92
92
|
- 状态:Beta(企业试点)
|
|
@@ -107,6 +107,7 @@ SpringBootAI 是一个借鉴 Spring Boot 编程模型的 Python Web 框架,提
|
|
|
107
107
|
| Swagger / OpenAPI(对齐 SpringDoc) | [SWAGGER_MODULE.md](doc/SWAGGER_MODULE.md) | 随核心包 | `@Tag` / `@Operation` / `@ApiResponse` / `@Parameter` / `@Schema` / `@SecurityScheme` 注解驱动 API 文档 + Swagger2 别名 |
|
|
108
108
|
| P0/P1/P2 八大模块 | [EIGHT_MODULES.md](doc/EIGHT_MODULES.md) | 随核心包 | Spring Data Repository / Actuator / 多数据源读写分离 / 事务事件 / 配置松散绑定 / 测试切片 / i18n / WebSocket |
|
|
109
109
|
| 安全 | [SECURITY.md](doc/SECURITY.md) | 随核心包 | JWT 生成校验 / 密码加密(SHA256/MD5/BCrypt)/ SQL 注入防护 / 访问控制 |
|
|
110
|
+
| BeanUtils(属性复制工具) | [BEAN_UTILS.md](doc/BEAN_UTILS.md) | 随核心包 | `copy_properties` / `clone` / `get_property` / `set_property` / `populate` / `describe` 对齐 Spring + Apache Commons BeanUtils |
|
|
110
111
|
| 测试报告 | [TEST_REPORT.md](doc/TEST_REPORT.md) | — | 全量测试用例与覆盖范围 |
|
|
111
112
|
|
|
112
113
|
> 所有模块文档统一存放于 [`doc/`](doc/) 目录。本指南正文中出现的"已分离至独立文档"链接均指向 `doc/` 下的对应文件。
|
|
@@ -142,7 +143,7 @@ SpringBootAI 借鉴了 Spring Boot 的注解和分层习惯,但运行时是 Py
|
|
|
142
143
|
|
|
143
144
|
| 组件 | 当前版本 |
|
|
144
145
|
|------|----------|
|
|
145
|
-
| `spring` 框架 API | 1.8.
|
|
146
|
+
| `spring` 框架 API | 1.8.2 |
|
|
146
147
|
| `spring.orm.pymybatis` | 1.4.0 |
|
|
147
148
|
| Python | 3.10+ |
|
|
148
149
|
|
|
@@ -158,7 +159,7 @@ SpringBootAI 借鉴了 Spring Boot 的注解和分层习惯,但运行时是 Py
|
|
|
158
159
|
- 自动化 ORM 契约测试使用 SQLite;MySQL、PostgreSQL、Oracle 需单独验证。
|
|
159
160
|
- 本地 `@Transactional` 支持全部七种 Spring 传播模式;`REQUIRES_NEW` 和 `NOT_SUPPORTED` 需要连接池有额外可用连接。
|
|
160
161
|
- Profile 会筛选 `@Profile` Bean,但不会自动合并 `application-{profile}.yml`。
|
|
161
|
-
- Nacos、RabbitMQ、Prometheus 依赖外部服务;Sentinel
|
|
162
|
+
- Nacos、RabbitMQ、Prometheus 依赖外部服务;Sentinel 和 OpenTelemetry 追踪可内嵌运行。HTTP 事务模式是持久化补偿协调器,不提供 Seata AT 强一致性;生产强一致场景必须使用真实 Seata Server 或可靠消息方案。
|
|
162
163
|
- 限流、分布式锁、幂等和缓存语义依赖 Redis 等后端,降级路径需要故障注入。
|
|
163
164
|
|
|
164
165
|
### 1.4 注解使用总览
|
|
@@ -193,7 +194,7 @@ SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是
|
|
|
193
194
|
| Nacos 服务发现 | ✅ 可用 | 服务注册/发现/订阅,支持无认证开发模式 |
|
|
194
195
|
| Sentinel 限流熔断 | ✅ 可用 | 内嵌引擎,QPS 限流、异常比例/异常数/慢调用熔断、热点参数限流,无需 Dashboard |
|
|
195
196
|
| 分布式追踪 | ✅ 可用 | 原生 OpenTelemetry(W3C traceparent),自动 HTTP/Feign 注入,无需 OAP Server |
|
|
196
|
-
| Seata 分布式事务 |
|
|
197
|
+
| Seata 分布式事务 | ⚠️ 有边界 | `distributed` 对接真实 Seata SDK/Server;`http` 仅提供显式启用的持久化补偿,支持重启恢复和 XID 传播,不等同 AT |
|
|
197
198
|
| API Gateway | ✅ 可用 | 轻量 ASGI/WSGI 网关,路由转发、路径重写、过滤器链、负载均衡 |
|
|
198
199
|
| Prometheus 监控 | ✅ 可用 | Counter/Gauge/Histogram 指标暴露 |
|
|
199
200
|
| Feign 声明式 HTTP | ✅ 可用 | 声明式接口、Fallback 降级、自动传播 XID 和 trace 头 |
|
|
@@ -1491,7 +1492,7 @@ class ScheduledTasks:
|
|
|
1491
1492
|
| `@SentinelResource` | 限流、业务异常 fallback | 受管 Bean 方法会包装;已内嵌限流熔断引擎,无需 Sentinel Dashboard |
|
|
1492
1493
|
| `@EnableGateway` | 启用 API Gateway | 元数据标记;配合 `GatewayRouter` 使用,内嵌轻量 ASGI/WSGI 网关 |
|
|
1493
1494
|
| `@LoadBalanced` | 给 `@Bean` 工厂返回对象添加负载均衡标记 | `@Bean` 方法上会执行,但实际请求仍需配合框架负载均衡客户端 |
|
|
1494
|
-
| `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean
|
|
1495
|
+
| `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean 方法调用 Seata manager;HTTP 模式为持久化补偿且要求幂等回调,强一致生产场景使用 `distributed`;不支持嵌套 |
|
|
1495
1496
|
| `@Valid` | 非空/嵌套对象基础检查 | 受管 Bean 方法会执行简化校验,不等同 Jakarta Bean Validation |
|
|
1496
1497
|
| `@Validated` | 分组校验 | 会执行简化检查,但 `groups` 当前未驱动真正的分组规则 |
|
|
1497
1498
|
| `@RabbitListener` | 注册 RabbitMQ 消费者 | 可直接装饰受管 Bean 方法;容器声明队列/交换机、注册回调,并在刷新后启动后台消费;支持同步和异步回调 |
|
|
@@ -2018,7 +2019,7 @@ class CleanupJob:
|
|
|
2018
2019
|
|
|
2019
2020
|
`@SentinelResource`、`@GlobalTransactional` 和方法级 `@LoadBalanced` 有 AOP 消费路径;`@EnableDiscoveryClient`、`@FeignClient` 等当前主要是元数据,Nacos 实际初始化由 `discovery.enabled` 和 `ApplicationContext` 启动流程控制。Nacos 客户端需要 `nacos-sdk-python`、`NACOS_SERVER`、`NACOS_USERNAME`、`NACOS_PASSWORD`;Nacos 2.2+ Docker 服务端还需要 Base64 token 和 identity 环境变量。
|
|
2020
2021
|
|
|
2021
|
-
配置 `rabbitmq.enabled: true` 并提供 host、port、username、password、virtual_host。开发环境也应在真实 Nacos、RabbitMQ、Redis 等环境执行集成、断线和重复投递测试。
|
|
2022
|
+
配置 `rabbitmq.enabled: true` 并提供 host、port、username、password、virtual_host。开发环境也应在真实 Nacos、RabbitMQ、Redis 等环境执行集成、断线和重复投递测试。HTTP 事务模式会将协调元数据写入 `seata.store_path`,并在 worker 启动后周期恢复;它是补偿事务,不是 Seata AT,也不能单独满足支付、订单、库存的一致性要求。
|
|
2022
2023
|
|
|
2023
2024
|
---
|
|
2024
2025
|
|
|
@@ -2228,7 +2229,7 @@ class UserMapper:
|
|
|
2228
2229
|
| `@LoadBalanced` | 同名注解 | 使用 Python 负载均衡实现;不要复用 `RestTemplate` 用法 |
|
|
2229
2230
|
| Sentinel `@SentinelResource` | 同名注解 | 已内嵌限流熔断引擎,无需 Dashboard;如需更强大治理能力可对接外部 Sentinel Dashboard |
|
|
2230
2231
|
| Spring Cloud Gateway | `@EnableGateway` + `GatewayRouter` | 内嵌轻量 ASGI/WSGI 网关;复杂网关需求可使用 Kong/APISIX 等专业网关 |
|
|
2231
|
-
| Seata `@GlobalTransactional` | 同名注解 |
|
|
2232
|
+
| Seata `@GlobalTransactional` | 同名注解 | `http` 模式是持久化补偿协调器,依赖幂等分支回调;企业强一致场景必须配置真实 Seata Server 与兼容 SDK |
|
|
2232
2233
|
|
|
2233
2234
|
**Cloud 高级功能迁移对照**:
|
|
2234
2235
|
|
|
@@ -2236,7 +2237,7 @@ class UserMapper:
|
|
|
2236
2237
|
|---|---|---|
|
|
2237
2238
|
| Sentinel Dashboard + `@SentinelResource` | `@SentinelResource` | 内嵌引擎,无需 Dashboard;支持 QPS 限流、异常比例/异常数/慢调用熔断、热点参数限流 |
|
|
2238
2239
|
| SkyWalking Agent + OAP Server | `@Trace` + 内嵌 Tracer | 原生 OpenTelemetry(W3C traceparent),无需 OAP Server;自动注入 HTTP/Feign 追踪头 |
|
|
2239
|
-
| Seata Server + `@GlobalTransactional` | `@GlobalTransactional` |
|
|
2240
|
+
| Seata Server + `@GlobalTransactional` | `@GlobalTransactional` | `distributed` 模式对接 Seata Server;`http` 仅用于补偿流程和故障演练,不提供 AT 语义 |
|
|
2240
2241
|
| Spring Cloud Gateway | `GatewayRouter` | 轻量 ASGI/WSGI 网关,支持路由转发、路径重写、过滤器链、负载均衡 |
|
|
2241
2242
|
| JPA `hibernate.ddl-auto` | `@entity` + `ddl-auto` 配置 | 支持 create/update/validate/create-drop;自动扫描实体包,自动建表/添加列/创建索引 |
|
|
2242
2243
|
|
|
@@ -2322,7 +2323,7 @@ Java 的 profile 文件自动合并不是当前功能。部署流程应生成最
|
|
|
2322
2323
|
4. 用目标 MySQL/PostgreSQL/Oracle 版本执行相同测试,特别验证自增主键、事务隔离、超时和连接中断。
|
|
2323
2324
|
5. 启动 ASGI 应用,检查 `/docs`、`/actuator/health/liveness` 和 `/actuator/health/readiness`。
|
|
2324
2325
|
6. 接入 Nacos、RabbitMQ、Redis 等外部中间件,并演练断线、重复投递和回滚。
|
|
2325
|
-
7.
|
|
2326
|
+
7. 验证 Cloud 功能:`@SentinelResource` 限流熔断、`@Trace` 追踪、`@GlobalTransactional` 补偿流程、`GatewayRouter` 网关、`@entity` + `ddl-auto` 自动建表。HTTP 补偿模式必须验证重复回调、进程重启和部分失败;强一致事务需部署真实 Seata Server 并执行集成测试。
|
|
2326
2327
|
|
|
2327
2328
|
---
|
|
2328
2329
|
|
|
@@ -2446,9 +2447,13 @@ logging:
|
|
|
2446
2447
|
| `DISCOVERY_ENABLED` | 是否启用 Nacos 服务发现 | false |
|
|
2447
2448
|
| `NACOS_SERVER` | Nacos 地址 | localhost:8848 |
|
|
2448
2449
|
| `NACOS_USERNAME` / `NACOS_PASSWORD` | Nacos 客户端账号/密码 | 空 |
|
|
2450
|
+
| `SEATA_HTTP_STORE_PATH` | HTTP 补偿协调 SQLite 文件路径(同主机多 worker 共享) | `./data/seata-http.sqlite3` |
|
|
2451
|
+
| `SEATA_HTTP_RECOVER_ON_STARTUP` | 初始化时扫描并恢复未完成补偿 | true |
|
|
2452
|
+
| `SEATA_HTTP_RECOVERY_GRACE_MS` | 接管其他 worker 的 COMMITTING/ROLLING_BACK 前等待时间 | 30000 |
|
|
2453
|
+
| `SEATA_HTTP_RECOVERY_INTERVAL_S` | worker 内周期恢复间隔,0 表示关闭 | 30 |
|
|
2449
2454
|
| `SPRING_DISABLE_DOCKER_IP_DETECT` | 设为 1 禁用 Docker 容器 IP 自动检测 | 0 |
|
|
2450
2455
|
|
|
2451
|
-
>
|
|
2456
|
+
> HTTP 补偿模式使用 `SEATA_HTTP_*` 配置,仍不提供 Seata AT;生产强一致事务必须配置 `SEATA_MODE=distributed`、真实 Server 和兼容 SDK。
|
|
2452
2457
|
|
|
2453
2458
|
### 14.5 启动应用
|
|
2454
2459
|
|
|
@@ -2526,7 +2531,7 @@ curl http://localhost:8080/actuator/health
|
|
|
2526
2531
|
}
|
|
2527
2532
|
```
|
|
2528
2533
|
|
|
2529
|
-
> Sentinel、OpenTelemetry
|
|
2534
|
+
> Sentinel、OpenTelemetry 追踪和 API Gateway 可内嵌运行;HTTP 补偿协调器的未完成事务会纳入恢复日志。它不属于强一致外部事务协调器,不能替代 Seata Server。
|
|
2530
2535
|
|
|
2531
2536
|
### 14.7 部署故障排查
|
|
2532
2537
|
|
|
@@ -2751,7 +2756,7 @@ export LOG_LEVEL=INFO
|
|
|
2751
2756
|
export LOG_DIR=logs
|
|
2752
2757
|
```
|
|
2753
2758
|
|
|
2754
|
-
> Sentinel 限流熔断、OpenTelemetry
|
|
2759
|
+
> Sentinel 限流熔断、OpenTelemetry 分布式追踪和 API Gateway 可内嵌实现;HTTP 补偿事务通过 `SEATA_HTTP_*` 配置。AI 模块环境变量见第 12.2 节。
|
|
2755
2760
|
|
|
2756
2761
|
## 附录 B:Docker Compose 示例
|
|
2757
2762
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
SpringBootAI 是一个借鉴 Spring Boot 编程模型的 Python Web 框架,提供装饰器式组件扫描、依赖注入、FastAPI 路由、配置加载、安全能力、内嵌的 PyMyBatis ORM,以及企业级 AI 模块(对齐 Spring AI 2.0:ChatClient/Advisor/Tools/RAG/Function Calling)。本指南为框架核心综合使用文档;**AI / ORM / Cloud / Excel 等模块的完整注解与功能说明已分离为独立文档**(见下方“模块文档”),本指南相应章节保留概览与跳转链接。
|
|
4
4
|
|
|
5
|
-
- SpringBootAI 版本:`1.8.
|
|
5
|
+
- SpringBootAI 版本:`1.8.2`
|
|
6
6
|
- 内嵌 PyMyBatis 版本:`1.4.0`
|
|
7
7
|
- Python:3.10+
|
|
8
8
|
- 状态:Beta(企业试点)
|
|
@@ -23,6 +23,7 @@ SpringBootAI 是一个借鉴 Spring Boot 编程模型的 Python Web 框架,提
|
|
|
23
23
|
| Swagger / OpenAPI(对齐 SpringDoc) | [SWAGGER_MODULE.md](doc/SWAGGER_MODULE.md) | 随核心包 | `@Tag` / `@Operation` / `@ApiResponse` / `@Parameter` / `@Schema` / `@SecurityScheme` 注解驱动 API 文档 + Swagger2 别名 |
|
|
24
24
|
| P0/P1/P2 八大模块 | [EIGHT_MODULES.md](doc/EIGHT_MODULES.md) | 随核心包 | Spring Data Repository / Actuator / 多数据源读写分离 / 事务事件 / 配置松散绑定 / 测试切片 / i18n / WebSocket |
|
|
25
25
|
| 安全 | [SECURITY.md](doc/SECURITY.md) | 随核心包 | JWT 生成校验 / 密码加密(SHA256/MD5/BCrypt)/ SQL 注入防护 / 访问控制 |
|
|
26
|
+
| BeanUtils(属性复制工具) | [BEAN_UTILS.md](doc/BEAN_UTILS.md) | 随核心包 | `copy_properties` / `clone` / `get_property` / `set_property` / `populate` / `describe` 对齐 Spring + Apache Commons BeanUtils |
|
|
26
27
|
| 测试报告 | [TEST_REPORT.md](doc/TEST_REPORT.md) | — | 全量测试用例与覆盖范围 |
|
|
27
28
|
|
|
28
29
|
> 所有模块文档统一存放于 [`doc/`](doc/) 目录。本指南正文中出现的"已分离至独立文档"链接均指向 `doc/` 下的对应文件。
|
|
@@ -58,7 +59,7 @@ SpringBootAI 借鉴了 Spring Boot 的注解和分层习惯,但运行时是 Py
|
|
|
58
59
|
|
|
59
60
|
| 组件 | 当前版本 |
|
|
60
61
|
|------|----------|
|
|
61
|
-
| `spring` 框架 API | 1.8.
|
|
62
|
+
| `spring` 框架 API | 1.8.2 |
|
|
62
63
|
| `spring.orm.pymybatis` | 1.4.0 |
|
|
63
64
|
| Python | 3.10+ |
|
|
64
65
|
|
|
@@ -74,7 +75,7 @@ SpringBootAI 借鉴了 Spring Boot 的注解和分层习惯,但运行时是 Py
|
|
|
74
75
|
- 自动化 ORM 契约测试使用 SQLite;MySQL、PostgreSQL、Oracle 需单独验证。
|
|
75
76
|
- 本地 `@Transactional` 支持全部七种 Spring 传播模式;`REQUIRES_NEW` 和 `NOT_SUPPORTED` 需要连接池有额外可用连接。
|
|
76
77
|
- Profile 会筛选 `@Profile` Bean,但不会自动合并 `application-{profile}.yml`。
|
|
77
|
-
- Nacos、RabbitMQ、Prometheus 依赖外部服务;Sentinel
|
|
78
|
+
- Nacos、RabbitMQ、Prometheus 依赖外部服务;Sentinel 和 OpenTelemetry 追踪可内嵌运行。HTTP 事务模式是持久化补偿协调器,不提供 Seata AT 强一致性;生产强一致场景必须使用真实 Seata Server 或可靠消息方案。
|
|
78
79
|
- 限流、分布式锁、幂等和缓存语义依赖 Redis 等后端,降级路径需要故障注入。
|
|
79
80
|
|
|
80
81
|
### 1.4 注解使用总览
|
|
@@ -109,7 +110,7 @@ SpringBootAI 注解会先把元数据放到 `__spring_annotations__`。之后是
|
|
|
109
110
|
| Nacos 服务发现 | ✅ 可用 | 服务注册/发现/订阅,支持无认证开发模式 |
|
|
110
111
|
| Sentinel 限流熔断 | ✅ 可用 | 内嵌引擎,QPS 限流、异常比例/异常数/慢调用熔断、热点参数限流,无需 Dashboard |
|
|
111
112
|
| 分布式追踪 | ✅ 可用 | 原生 OpenTelemetry(W3C traceparent),自动 HTTP/Feign 注入,无需 OAP Server |
|
|
112
|
-
| Seata 分布式事务 |
|
|
113
|
+
| Seata 分布式事务 | ⚠️ 有边界 | `distributed` 对接真实 Seata SDK/Server;`http` 仅提供显式启用的持久化补偿,支持重启恢复和 XID 传播,不等同 AT |
|
|
113
114
|
| API Gateway | ✅ 可用 | 轻量 ASGI/WSGI 网关,路由转发、路径重写、过滤器链、负载均衡 |
|
|
114
115
|
| Prometheus 监控 | ✅ 可用 | Counter/Gauge/Histogram 指标暴露 |
|
|
115
116
|
| Feign 声明式 HTTP | ✅ 可用 | 声明式接口、Fallback 降级、自动传播 XID 和 trace 头 |
|
|
@@ -1407,7 +1408,7 @@ class ScheduledTasks:
|
|
|
1407
1408
|
| `@SentinelResource` | 限流、业务异常 fallback | 受管 Bean 方法会包装;已内嵌限流熔断引擎,无需 Sentinel Dashboard |
|
|
1408
1409
|
| `@EnableGateway` | 启用 API Gateway | 元数据标记;配合 `GatewayRouter` 使用,内嵌轻量 ASGI/WSGI 网关 |
|
|
1409
1410
|
| `@LoadBalanced` | 给 `@Bean` 工厂返回对象添加负载均衡标记 | `@Bean` 方法上会执行,但实际请求仍需配合框架负载均衡客户端 |
|
|
1410
|
-
| `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean
|
|
1411
|
+
| `@GlobalTransactional` | 通过 Seata 管理全局事务 | 受管 Bean 方法调用 Seata manager;HTTP 模式为持久化补偿且要求幂等回调,强一致生产场景使用 `distributed`;不支持嵌套 |
|
|
1411
1412
|
| `@Valid` | 非空/嵌套对象基础检查 | 受管 Bean 方法会执行简化校验,不等同 Jakarta Bean Validation |
|
|
1412
1413
|
| `@Validated` | 分组校验 | 会执行简化检查,但 `groups` 当前未驱动真正的分组规则 |
|
|
1413
1414
|
| `@RabbitListener` | 注册 RabbitMQ 消费者 | 可直接装饰受管 Bean 方法;容器声明队列/交换机、注册回调,并在刷新后启动后台消费;支持同步和异步回调 |
|
|
@@ -1934,7 +1935,7 @@ class CleanupJob:
|
|
|
1934
1935
|
|
|
1935
1936
|
`@SentinelResource`、`@GlobalTransactional` 和方法级 `@LoadBalanced` 有 AOP 消费路径;`@EnableDiscoveryClient`、`@FeignClient` 等当前主要是元数据,Nacos 实际初始化由 `discovery.enabled` 和 `ApplicationContext` 启动流程控制。Nacos 客户端需要 `nacos-sdk-python`、`NACOS_SERVER`、`NACOS_USERNAME`、`NACOS_PASSWORD`;Nacos 2.2+ Docker 服务端还需要 Base64 token 和 identity 环境变量。
|
|
1936
1937
|
|
|
1937
|
-
配置 `rabbitmq.enabled: true` 并提供 host、port、username、password、virtual_host。开发环境也应在真实 Nacos、RabbitMQ、Redis 等环境执行集成、断线和重复投递测试。
|
|
1938
|
+
配置 `rabbitmq.enabled: true` 并提供 host、port、username、password、virtual_host。开发环境也应在真实 Nacos、RabbitMQ、Redis 等环境执行集成、断线和重复投递测试。HTTP 事务模式会将协调元数据写入 `seata.store_path`,并在 worker 启动后周期恢复;它是补偿事务,不是 Seata AT,也不能单独满足支付、订单、库存的一致性要求。
|
|
1938
1939
|
|
|
1939
1940
|
---
|
|
1940
1941
|
|
|
@@ -2144,7 +2145,7 @@ class UserMapper:
|
|
|
2144
2145
|
| `@LoadBalanced` | 同名注解 | 使用 Python 负载均衡实现;不要复用 `RestTemplate` 用法 |
|
|
2145
2146
|
| Sentinel `@SentinelResource` | 同名注解 | 已内嵌限流熔断引擎,无需 Dashboard;如需更强大治理能力可对接外部 Sentinel Dashboard |
|
|
2146
2147
|
| Spring Cloud Gateway | `@EnableGateway` + `GatewayRouter` | 内嵌轻量 ASGI/WSGI 网关;复杂网关需求可使用 Kong/APISIX 等专业网关 |
|
|
2147
|
-
| Seata `@GlobalTransactional` | 同名注解 |
|
|
2148
|
+
| Seata `@GlobalTransactional` | 同名注解 | `http` 模式是持久化补偿协调器,依赖幂等分支回调;企业强一致场景必须配置真实 Seata Server 与兼容 SDK |
|
|
2148
2149
|
|
|
2149
2150
|
**Cloud 高级功能迁移对照**:
|
|
2150
2151
|
|
|
@@ -2152,7 +2153,7 @@ class UserMapper:
|
|
|
2152
2153
|
|---|---|---|
|
|
2153
2154
|
| Sentinel Dashboard + `@SentinelResource` | `@SentinelResource` | 内嵌引擎,无需 Dashboard;支持 QPS 限流、异常比例/异常数/慢调用熔断、热点参数限流 |
|
|
2154
2155
|
| SkyWalking Agent + OAP Server | `@Trace` + 内嵌 Tracer | 原生 OpenTelemetry(W3C traceparent),无需 OAP Server;自动注入 HTTP/Feign 追踪头 |
|
|
2155
|
-
| Seata Server + `@GlobalTransactional` | `@GlobalTransactional` |
|
|
2156
|
+
| Seata Server + `@GlobalTransactional` | `@GlobalTransactional` | `distributed` 模式对接 Seata Server;`http` 仅用于补偿流程和故障演练,不提供 AT 语义 |
|
|
2156
2157
|
| Spring Cloud Gateway | `GatewayRouter` | 轻量 ASGI/WSGI 网关,支持路由转发、路径重写、过滤器链、负载均衡 |
|
|
2157
2158
|
| JPA `hibernate.ddl-auto` | `@entity` + `ddl-auto` 配置 | 支持 create/update/validate/create-drop;自动扫描实体包,自动建表/添加列/创建索引 |
|
|
2158
2159
|
|
|
@@ -2238,7 +2239,7 @@ Java 的 profile 文件自动合并不是当前功能。部署流程应生成最
|
|
|
2238
2239
|
4. 用目标 MySQL/PostgreSQL/Oracle 版本执行相同测试,特别验证自增主键、事务隔离、超时和连接中断。
|
|
2239
2240
|
5. 启动 ASGI 应用,检查 `/docs`、`/actuator/health/liveness` 和 `/actuator/health/readiness`。
|
|
2240
2241
|
6. 接入 Nacos、RabbitMQ、Redis 等外部中间件,并演练断线、重复投递和回滚。
|
|
2241
|
-
7.
|
|
2242
|
+
7. 验证 Cloud 功能:`@SentinelResource` 限流熔断、`@Trace` 追踪、`@GlobalTransactional` 补偿流程、`GatewayRouter` 网关、`@entity` + `ddl-auto` 自动建表。HTTP 补偿模式必须验证重复回调、进程重启和部分失败;强一致事务需部署真实 Seata Server 并执行集成测试。
|
|
2242
2243
|
|
|
2243
2244
|
---
|
|
2244
2245
|
|
|
@@ -2362,9 +2363,13 @@ logging:
|
|
|
2362
2363
|
| `DISCOVERY_ENABLED` | 是否启用 Nacos 服务发现 | false |
|
|
2363
2364
|
| `NACOS_SERVER` | Nacos 地址 | localhost:8848 |
|
|
2364
2365
|
| `NACOS_USERNAME` / `NACOS_PASSWORD` | Nacos 客户端账号/密码 | 空 |
|
|
2366
|
+
| `SEATA_HTTP_STORE_PATH` | HTTP 补偿协调 SQLite 文件路径(同主机多 worker 共享) | `./data/seata-http.sqlite3` |
|
|
2367
|
+
| `SEATA_HTTP_RECOVER_ON_STARTUP` | 初始化时扫描并恢复未完成补偿 | true |
|
|
2368
|
+
| `SEATA_HTTP_RECOVERY_GRACE_MS` | 接管其他 worker 的 COMMITTING/ROLLING_BACK 前等待时间 | 30000 |
|
|
2369
|
+
| `SEATA_HTTP_RECOVERY_INTERVAL_S` | worker 内周期恢复间隔,0 表示关闭 | 30 |
|
|
2365
2370
|
| `SPRING_DISABLE_DOCKER_IP_DETECT` | 设为 1 禁用 Docker 容器 IP 自动检测 | 0 |
|
|
2366
2371
|
|
|
2367
|
-
>
|
|
2372
|
+
> HTTP 补偿模式使用 `SEATA_HTTP_*` 配置,仍不提供 Seata AT;生产强一致事务必须配置 `SEATA_MODE=distributed`、真实 Server 和兼容 SDK。
|
|
2368
2373
|
|
|
2369
2374
|
### 14.5 启动应用
|
|
2370
2375
|
|
|
@@ -2442,7 +2447,7 @@ curl http://localhost:8080/actuator/health
|
|
|
2442
2447
|
}
|
|
2443
2448
|
```
|
|
2444
2449
|
|
|
2445
|
-
> Sentinel、OpenTelemetry
|
|
2450
|
+
> Sentinel、OpenTelemetry 追踪和 API Gateway 可内嵌运行;HTTP 补偿协调器的未完成事务会纳入恢复日志。它不属于强一致外部事务协调器,不能替代 Seata Server。
|
|
2446
2451
|
|
|
2447
2452
|
### 14.7 部署故障排查
|
|
2448
2453
|
|
|
@@ -2667,7 +2672,7 @@ export LOG_LEVEL=INFO
|
|
|
2667
2672
|
export LOG_DIR=logs
|
|
2668
2673
|
```
|
|
2669
2674
|
|
|
2670
|
-
> Sentinel 限流熔断、OpenTelemetry
|
|
2675
|
+
> Sentinel 限流熔断、OpenTelemetry 分布式追踪和 API Gateway 可内嵌实现;HTTP 补偿事务通过 `SEATA_HTTP_*` 配置。AI 模块环境变量见第 12.2 节。
|
|
2671
2676
|
|
|
2672
2677
|
## 附录 B:Docker Compose 示例
|
|
2673
2678
|
|
|
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
|
|
|
12
12
|
[project]
|
|
13
13
|
# 发行名 springbootAI(PyPI 包名,pip install springbootAI);导入包名仍为 spring
|
|
14
14
|
name = "springbootAI"
|
|
15
|
-
version = "1.8.
|
|
15
|
+
version = "1.8.2"
|
|
16
16
|
description = "Python版Spring Boot框架,集成PyMyBatis ORM"
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
license = "MIT"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .logger import SpringLogger
|
|
2
2
|
from .banner import BannerPrinter
|
|
3
3
|
from .redis_client import RedisClient
|
|
4
|
+
from .bean_utils import BeanUtils
|
|
4
5
|
|
|
5
|
-
__all__ = ["SpringLogger", "BannerPrinter", "RedisClient"]
|
|
6
|
+
__all__ = ["SpringLogger", "BannerPrinter", "RedisClient", "BeanUtils"]
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BeanUtils - 属性复制工具(对齐 Spring org.springframework.beans.BeanUtils)
|
|
3
|
+
|
|
4
|
+
提供对象间属性复制、属性读写、属性描述符等功能,命名与方法语义对齐 Spring BeanUtils,
|
|
5
|
+
并补充 Apache Commons BeanUtils 常用方法(getProperty/setProperty/populate/describe)。
|
|
6
|
+
|
|
7
|
+
== 与 Java Spring BeanUtils 的差异 ==
|
|
8
|
+
1. Python 动态类型,属性复制不做类型转换(Java 通过 PropertyEditor/ConversionService 转换);
|
|
9
|
+
若同名属性类型不兼容,直接原样赋值,由目标对象自行决定是否接受。
|
|
10
|
+
2. 基于 ``__dict__`` / ``getattr`` / ``setattr`` 实现,支持普通类、dataclass、Pydantic v2 Model、ORM entity。
|
|
11
|
+
3. 默认浅拷贝(嵌套对象复制引用),需要深拷贝请用 ``copy_deep=True`` 或 ``BeanUtils.clone(source, deep=True)``。
|
|
12
|
+
4. 不支持 Java 的 PropertyChangeListener / VetoableChangeListener 等监听机制。
|
|
13
|
+
5. 双下划线开头(``__``)的特殊属性与方法(callable)自动排除;单下划线(``_``)约定的私有属性默认参与复制,
|
|
14
|
+
如需排除请通过 ``ignore`` 显式指定。
|
|
15
|
+
|
|
16
|
+
== 方法对齐速查 ==
|
|
17
|
+
- Spring BeanUtils: copy_properties / copy_property / get_property_descriptors / get_property_descriptor / clone
|
|
18
|
+
- Apache Commons BeanUtils: get_property / set_property / get_simple_property / populate / describe
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import copy as _copy_module
|
|
23
|
+
from typing import Any, Dict, Iterable, Mapping, Optional
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = ["BeanUtils"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _is_copyable_attr(name: str, value: Any) -> bool:
|
|
30
|
+
"""判断属性是否可参与复制:排除双下划线特殊属性与可调用对象(方法)。"""
|
|
31
|
+
if name.startswith("__"):
|
|
32
|
+
return False
|
|
33
|
+
if callable(value):
|
|
34
|
+
return False
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_readable_properties(source: Any) -> Dict[str, Any]:
|
|
39
|
+
"""
|
|
40
|
+
收集源对象所有可读属性(实例属性 + 类上定义的 property)。
|
|
41
|
+
|
|
42
|
+
优先取实例 ``__dict__``,再补充类层 property(dataclass/普通类均覆盖)。
|
|
43
|
+
property 的 getter 抛异常时跳过该属性,避免复制中断。
|
|
44
|
+
"""
|
|
45
|
+
props: Dict[str, Any] = {}
|
|
46
|
+
source_dict = getattr(source, "__dict__", None)
|
|
47
|
+
if isinstance(source_dict, dict):
|
|
48
|
+
for name, value in source_dict.items():
|
|
49
|
+
if _is_copyable_attr(name, value):
|
|
50
|
+
props[name] = value
|
|
51
|
+
|
|
52
|
+
# 补充类层 property(不含实例 __dict__ 已收集的)
|
|
53
|
+
for klass in type(source).__mro__:
|
|
54
|
+
try:
|
|
55
|
+
klass_vars = vars(klass)
|
|
56
|
+
except TypeError:
|
|
57
|
+
continue
|
|
58
|
+
for name, attr in klass_vars.items():
|
|
59
|
+
if name in props:
|
|
60
|
+
continue
|
|
61
|
+
if name.startswith("__"):
|
|
62
|
+
# 提前跳过 dunder,避免触发 Pydantic 等内部 property 的弃用 getter
|
|
63
|
+
continue
|
|
64
|
+
if isinstance(attr, property) and attr.fget is not None:
|
|
65
|
+
try:
|
|
66
|
+
value = attr.fget(source)
|
|
67
|
+
except Exception:
|
|
68
|
+
continue
|
|
69
|
+
if _is_copyable_attr(name, value):
|
|
70
|
+
props[name] = value
|
|
71
|
+
return props
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _is_writable(target: Any, name: str) -> bool:
|
|
75
|
+
"""判断目标对象是否能写入该属性:只读 property 返回 False。"""
|
|
76
|
+
for klass in type(target).__mro__:
|
|
77
|
+
try:
|
|
78
|
+
klass_vars = vars(klass)
|
|
79
|
+
except TypeError:
|
|
80
|
+
continue
|
|
81
|
+
if name in klass_vars:
|
|
82
|
+
attr = klass_vars[name]
|
|
83
|
+
if isinstance(attr, property):
|
|
84
|
+
return attr.fset is not None
|
|
85
|
+
# dataclass field / 普通类属性 -> 可写
|
|
86
|
+
return True
|
|
87
|
+
# 实例 __dict__ 可动态扩展的普通对象 -> 可写
|
|
88
|
+
return hasattr(target, "__dict__") or hasattr(target, "__slots__")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class BeanUtils:
|
|
92
|
+
"""属性复制工具类(对齐 Spring BeanUtils + Apache Commons BeanUtils)。"""
|
|
93
|
+
|
|
94
|
+
# ------------------------------------------------------------------
|
|
95
|
+
# Spring BeanUtils 对齐
|
|
96
|
+
# ------------------------------------------------------------------
|
|
97
|
+
@staticmethod
|
|
98
|
+
def copy_properties(
|
|
99
|
+
source: Any,
|
|
100
|
+
target: Any,
|
|
101
|
+
ignore: Optional[Iterable[str]] = None,
|
|
102
|
+
copy_deep: bool = False,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""
|
|
105
|
+
复制源对象同名属性到目标对象(对齐 Spring ``BeanUtils.copyProperties``)。
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
source: 源对象(``None`` 时直接返回)。
|
|
109
|
+
target: 目标对象(``None`` 时直接返回)。
|
|
110
|
+
ignore: 忽略的属性名集合(对齐 Java ``ignoreProperties`` 变长参数)。
|
|
111
|
+
copy_deep: 是否对每个属性值做深拷贝。默认 False(浅拷贝,与 Spring 一致)。
|
|
112
|
+
|
|
113
|
+
规则:
|
|
114
|
+
- 仅复制源对象存在且可读的属性;目标对象不可写(如只读 property)时跳过。
|
|
115
|
+
- 双下划线属性与方法自动排除;私有属性(单下划线)默认复制。
|
|
116
|
+
- 目标对象 setattr 失败(如 Pydantic frozen / slots 限制)时跳过,不抛异常。
|
|
117
|
+
"""
|
|
118
|
+
if source is None or target is None:
|
|
119
|
+
return
|
|
120
|
+
ignore_set: set = set(ignore) if ignore else set()
|
|
121
|
+
for name, value in _get_readable_properties(source).items():
|
|
122
|
+
if name in ignore_set:
|
|
123
|
+
continue
|
|
124
|
+
if not _is_writable(target, name):
|
|
125
|
+
continue
|
|
126
|
+
try:
|
|
127
|
+
setattr(target, name, _copy_module.deepcopy(value) if copy_deep else value)
|
|
128
|
+
except (AttributeError, TypeError, ValueError):
|
|
129
|
+
# 目标对象校验失败或不可写:跳过,保持 Spring 的"尽力复制"语义
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def copy_property(source: Any, target: Any, property_name: str) -> bool:
|
|
134
|
+
"""
|
|
135
|
+
复制单个属性(对齐 Spring ``BeanUtils.copyProperty``)。
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
是否复制成功(源无此属性或目标不可写返回 False)。
|
|
139
|
+
"""
|
|
140
|
+
if source is None or target is None:
|
|
141
|
+
return False
|
|
142
|
+
if not hasattr(source, property_name):
|
|
143
|
+
return False
|
|
144
|
+
try:
|
|
145
|
+
value = getattr(source, property_name)
|
|
146
|
+
except Exception:
|
|
147
|
+
return False
|
|
148
|
+
if not _is_copyable_attr(property_name, value):
|
|
149
|
+
return False
|
|
150
|
+
if not _is_writable(target, property_name):
|
|
151
|
+
return False
|
|
152
|
+
try:
|
|
153
|
+
setattr(target, property_name, value)
|
|
154
|
+
return True
|
|
155
|
+
except (AttributeError, TypeError, ValueError):
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def clone(source: Any, deep: bool = False) -> Any:
|
|
160
|
+
"""
|
|
161
|
+
克隆对象(对齐 Spring ``BeanUtils.cloneBean``,Apache Commons 风格)。
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
source: 源对象。
|
|
165
|
+
deep: True 做深拷贝;False 仅复制顶层(嵌套对象共享引用)。
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
与源同类型的新对象,属性已复制。
|
|
169
|
+
"""
|
|
170
|
+
if source is None:
|
|
171
|
+
return None
|
|
172
|
+
target = type(source).__new__(type(source))
|
|
173
|
+
BeanUtils.copy_properties(source, target, copy_deep=deep)
|
|
174
|
+
return target
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def get_property_descriptors(obj: Any) -> Dict[str, Optional[type]]:
|
|
178
|
+
"""
|
|
179
|
+
获取属性描述符(对齐 Spring ``BeanUtils.getPropertyDescriptors``)。
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
属性名 -> 属性值类型的映射(无法推断类型时为 None)。
|
|
183
|
+
"""
|
|
184
|
+
descriptors: Dict[str, Optional[type]] = {}
|
|
185
|
+
for name, value in _get_readable_properties(obj).items():
|
|
186
|
+
descriptors[name] = type(value) if value is not None else None
|
|
187
|
+
return descriptors
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def get_property_descriptor(obj: Any, name: str) -> Optional[type]:
|
|
191
|
+
"""获取单个属性的描述符(对齐 Spring ``BeanUtils.getPropertyDescriptor``)。"""
|
|
192
|
+
if obj is None or not hasattr(obj, name):
|
|
193
|
+
return None
|
|
194
|
+
try:
|
|
195
|
+
value = getattr(obj, name)
|
|
196
|
+
except Exception:
|
|
197
|
+
return None
|
|
198
|
+
return type(value) if value is not None else None
|
|
199
|
+
|
|
200
|
+
# ------------------------------------------------------------------
|
|
201
|
+
# Apache Commons BeanUtils 对齐
|
|
202
|
+
# ------------------------------------------------------------------
|
|
203
|
+
@staticmethod
|
|
204
|
+
def get_property(obj: Any, name: str, default: Any = None) -> Any:
|
|
205
|
+
"""
|
|
206
|
+
获取属性值,支持嵌套路径(对齐 Apache Commons ``BeanUtils.getProperty``)。
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
name: 属性名,支持点号嵌套,如 ``"user.address.city"``。
|
|
210
|
+
default: 路径中断或取值异常时返回的默认值。
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
属性值;路径中断或异常返回 ``default``。
|
|
214
|
+
"""
|
|
215
|
+
if obj is None or not name:
|
|
216
|
+
return default
|
|
217
|
+
current: Any = obj
|
|
218
|
+
for part in name.split("."):
|
|
219
|
+
if current is None:
|
|
220
|
+
return default
|
|
221
|
+
if isinstance(current, Mapping) and part in current:
|
|
222
|
+
current = current[part]
|
|
223
|
+
elif hasattr(current, part):
|
|
224
|
+
try:
|
|
225
|
+
current = getattr(current, part)
|
|
226
|
+
except Exception:
|
|
227
|
+
return default
|
|
228
|
+
else:
|
|
229
|
+
return default
|
|
230
|
+
return current if current is not None else default
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def set_property(obj: Any, name: str, value: Any) -> bool:
|
|
234
|
+
"""
|
|
235
|
+
设置属性值,支持嵌套路径(对齐 Apache Commons ``BeanUtils.setProperty``)。
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
name: 属性名,支持点号嵌套;嵌套中间节点为 None 时创建失败返回 False。
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
是否设置成功。
|
|
242
|
+
"""
|
|
243
|
+
if obj is None or not name:
|
|
244
|
+
return False
|
|
245
|
+
parts = name.split(".")
|
|
246
|
+
current: Any = obj
|
|
247
|
+
for part in parts[:-1]:
|
|
248
|
+
if current is None:
|
|
249
|
+
return False
|
|
250
|
+
if isinstance(current, Mapping):
|
|
251
|
+
if part not in current or current[part] is None:
|
|
252
|
+
return False
|
|
253
|
+
current = current[part]
|
|
254
|
+
elif hasattr(current, part):
|
|
255
|
+
try:
|
|
256
|
+
nxt = getattr(current, part)
|
|
257
|
+
except Exception:
|
|
258
|
+
return False
|
|
259
|
+
if nxt is None:
|
|
260
|
+
return False
|
|
261
|
+
current = nxt
|
|
262
|
+
else:
|
|
263
|
+
return False
|
|
264
|
+
leaf = parts[-1]
|
|
265
|
+
try:
|
|
266
|
+
if isinstance(current, Mapping):
|
|
267
|
+
current[leaf] = value
|
|
268
|
+
else:
|
|
269
|
+
setattr(current, leaf, value)
|
|
270
|
+
return True
|
|
271
|
+
except (AttributeError, TypeError, ValueError, KeyError):
|
|
272
|
+
return False
|
|
273
|
+
|
|
274
|
+
@staticmethod
|
|
275
|
+
def get_simple_property(obj: Any, name: str, default: Any = None) -> Any:
|
|
276
|
+
"""获取简单属性(不支持嵌套,对齐 Apache Commons ``getSimpleProperty``)。"""
|
|
277
|
+
if obj is None or not name or not hasattr(obj, name):
|
|
278
|
+
return default
|
|
279
|
+
try:
|
|
280
|
+
value = getattr(obj, name)
|
|
281
|
+
except Exception:
|
|
282
|
+
return default
|
|
283
|
+
return value if value is not None else default
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def populate(obj: Any, properties: Mapping[str, Any]) -> None:
|
|
287
|
+
"""
|
|
288
|
+
用字典批量设置属性(对齐 Apache Commons ``BeanUtils.populate``)。
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
obj: 目标对象。
|
|
292
|
+
properties: 属性名 -> 值的映射;不可写的属性自动跳过。
|
|
293
|
+
"""
|
|
294
|
+
if obj is None or not properties:
|
|
295
|
+
return
|
|
296
|
+
for name, value in properties.items():
|
|
297
|
+
if not _is_writable(obj, name):
|
|
298
|
+
continue
|
|
299
|
+
try:
|
|
300
|
+
setattr(obj, name, value)
|
|
301
|
+
except (AttributeError, TypeError, ValueError):
|
|
302
|
+
continue
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def describe(obj: Any) -> Dict[str, Any]:
|
|
306
|
+
"""
|
|
307
|
+
将对象可读属性导出为字典(对齐 Apache Commons ``BeanUtils.describe``)。
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
属性名 -> 值的映射(仅可读属性,排除方法与双下划线属性)。
|
|
311
|
+
"""
|
|
312
|
+
if obj is None:
|
|
313
|
+
return {}
|
|
314
|
+
return _get_readable_properties(obj)
|