springbootAI 2.3.4__tar.gz → 2.3.5__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-2.3.4 → springbootai-2.3.5}/CHANGELOG.md +8 -0
- {springbootai-2.3.4/springbootAI.egg-info → springbootai-2.3.5}/PKG-INFO +39 -7
- {springbootai-2.3.4 → springbootai-2.3.5}/README.md +38 -6
- {springbootai-2.3.4 → springbootai-2.3.5}/pyproject.toml +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5/springbootAI.egg-info}/PKG-INFO +39 -7
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootAI.egg-info/SOURCES.txt +3 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/__init__.py +10 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/conditional.py +112 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/core.py +24 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/aop/aspect.py +111 -2
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/context/bean_factory.py +185 -11
- springbootai-2.3.5/springbootai/context/lifecycle.py +241 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/data/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/datasource/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/logging/loguru_logger.py +7 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/version.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/secret_manager.py +2 -2
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/test/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/tx/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/utils/banner.py +2 -2
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/validation/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/__init__.py +1 -1
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_annotations_contract.py +1 -0
- springbootai-2.3.5/tests/test_aop_pointcut_enhancements.py +201 -0
- springbootai-2.3.5/tests/test_ioc_lifecycle_enhancements.py +328 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/LICENSE +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/MANIFEST.in +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/SECURITY.md +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/requirements-ai.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/requirements-langgraph.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/requirements-mcp.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/requirements.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/setup.cfg +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/setup.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootAI.egg-info/dependency_links.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootAI.egg-info/entry_points.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootAI.egg-info/requires.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootAI.egg-info/top_level.txt +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/advisors.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/annotation_runtime.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/autoconfig.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/core.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/etl.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/memory.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/observability.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/providers.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/resilience.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/tools.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/ai/vectorstore.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/batch.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/cache.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/cloud.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/data.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/enterprise.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/langchain.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/langgraph.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/lombok.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/messaging.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/annotations/security.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/aop/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/aop/cloud_aop.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/aop/comprehensive_aop.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/aop/method_interceptor.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/aop/proxy_factory.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/batch/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cli/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cli/main.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cli/scaffold.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/bus.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/config_center.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/discovery.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/feign.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/gateway.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/load_balancer.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/nacos_config.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/seata.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/seata_at_proxy.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/seata_bridge.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/sentinel.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/tracer.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/cloud/transaction_store.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/config/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/config/binding.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/config/config_loader.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/config/spring-configuration-metadata.json +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/context/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/context/application_context.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/context/bean_definition.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/context/registry.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/context/scanner.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/core/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/core/graceful_shutdown.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/core/typing_utils.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/converters.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/easy_csv.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/exceptions.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/reader.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/csv/writer.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/data/page.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/data/repository.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/data/rest.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/data/specification.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/datasource/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/datasource/context.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/datasource/dynamic.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/devtools.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/event/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/event/publisher.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/converters.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/easy_excel.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/exceptions.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/reader.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/style.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/excel/writer.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/accessor.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/auto_config.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/holder.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/locale.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/locale_resolver.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/message_source.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/middleware.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/properties.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/i18n/sources.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/adapters.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/agents/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/agents/services.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/autoconfig.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/callbacks/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/callbacks/handlers.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/chains/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/chains/services.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/core.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/indexes/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/indexes/index.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/loaders/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/loaders/loaders.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/memory/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/memory/memory.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/parsers/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/parsers/parsers.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/partners.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/prompts/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/prompts/templates.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/retrievers/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/retrievers/retrievers.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/tools/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/tools/tools.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/utilities/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/utilities/utils.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/vectorstores/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langchain/vectorstores/stores.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/annotation_runtime.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/autoconfig.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/checkpoint.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/config.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/langgraph/runtime.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/logging/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/main.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/annotation_runtime.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/autoconfig.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/client.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/config.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/mcp/server.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/messaging/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/messaging/kafka.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/messaging/rabbitmq.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/monitoring/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/monitoring/admin_client.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/monitoring/prometheus.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/database.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/ddl_auto.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/migration.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/mybatis_integration.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/annotations/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/annotations/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/cache/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/cache/cache.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/cache/redis_cache.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/circuit_breaker/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/circuit_breaker/circuit_breaker.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/configuration.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/core/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/core/sql_session.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/core/sql_session_factory.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/dialect/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/dialect/dialect.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/dynamic_sql/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/dynamic_sql/dynamic_sql.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/interceptor/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/interceptor/interceptor.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/mapper/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/mapper/mapper.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/metrics/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/metrics/metrics.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/pool/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/pool/connection_pool.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/security/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/security/access_control.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/security/password_encoder.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/security/sensitive_data_masker.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/security/sql_injection_detector.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/transaction/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/transaction/transaction.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/type_handler/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/type_handler/type_handler.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/xml_parser/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/orm/pymybatis/xml_parser/xml_parser.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/retry/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/retry/recovery.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/retry/retry_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/retry/retry_decorator.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/scheduling/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/scheduling/scheduler.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/jwt_utils.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/oauth2.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/replay_protection.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/security_aop.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/security/security_context.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/test/slicing.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/tracing/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/tracing/skywalking.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/tx/events.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/tx/synchronization.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/utils/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/utils/bean_utils.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/utils/logger.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/utils/redis_client.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/validation/aop.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/validation/constraints.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/validation/exceptions.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/validation/validator.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/__init__.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/actuator.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/csrf.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/exception_handler.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/hateoas.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/health.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/interceptor.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/request_metrics.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/result.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/swagger.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/web/web_context.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/broker.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/exceptions.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/handler.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/router.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/springbootai/websocket/session.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/sql/seata_undo_log.sql +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_actuator.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_actuator_prometheus_admin.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_ai_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_ai_comprehensive.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_ai_declarative_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_ai_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_annotation_combinations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_aop_annotations_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_audit_time.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_batch.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_bean_utils.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_bus.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_cache_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_cli.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_cloud_embedded_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_component_init_error.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_compositional_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_conditional_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_config_binding.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_config_center.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_config_fixes_185.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_connection_resilience.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_core.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_core_annotations_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_csrf.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_csv_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_data_repository.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_data_rest.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_datasource_routing.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_ddl_auto.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_declarative_aop_post_authorize_recover.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_devtools.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_di_config_event_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_excel_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_hateoas.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_i18n_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_jpa_version_transient.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_kafka.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_langchain_comprehensive.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_langchain_ext.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_langchain_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_langgraph_comprehensive.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_langgraph_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_logging_config.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_lombok_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_mcp_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_metadata.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_migration.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_nacos_config.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_new_annotations.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_new_features.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_oauth2.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_orm_pymybatis_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_production_readiness.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_pymybatis_configuration_hardening.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_pymybatis_contract.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_request_metrics.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_retry.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_runtime_configuration_fixes.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_scaffold_hardening.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_scheduling.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_seata_at_proxy.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_seata_bridge.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_seata_durable_store.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_security.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_security_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_security_hardening.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_select_page.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_starter.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_startup_lifecycle.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_swagger_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_test_slicing.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_tracing.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_transactional_events.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_validation_module.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_web_annotations_full.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_web_file_upload.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_web_root_mapping.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_web_security_responses.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_web_static_files.py +0 -0
- {springbootai-2.3.4 → springbootai-2.3.5}/tests/test_websocket_module.py +0 -0
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.3.5] - 2026-08-20
|
|
4
|
+
|
|
5
|
+
### 文档与发布
|
|
6
|
+
|
|
7
|
+
- 发布版本升级到 `2.3.5`,同步框架主包、扩展模块、PyMyBatis 内嵌版本、README 版本矩阵和打包元数据。
|
|
8
|
+
- README 首页新增 SpringBootAI 在线考试认证与证书说明,提供认证入口、适用场景、备考流程和重点知识范围。
|
|
9
|
+
- 认证平台入口:[http://www.yucg.cn:8230](http://www.yucg.cn:8230)。
|
|
10
|
+
|
|
3
11
|
## [2.3.4] - 2026-08-19
|
|
4
12
|
|
|
5
13
|
### 新增与完善
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: springbootAI
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.5
|
|
4
4
|
Summary: Spring-style Python framework with Web, ORM, Cloud and AI integrations
|
|
5
5
|
Author-email: YuConggen <1516933915@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -163,9 +163,41 @@ Dynamic: license-file
|
|
|
163
163
|
# SpringBootAI
|
|
164
164
|
|
|
165
165
|
<p align="center">
|
|
166
|
-
<img src="doc/images/hero-banner.png" alt="SpringBootAI 功能总览" width="100%" />
|
|
166
|
+
<img src="https://raw.githubusercontent.com/YUCONGGEN/springbootAI/master/doc/images/hero-banner.png" alt="SpringBootAI 功能总览" width="100%" />
|
|
167
167
|
</p>
|
|
168
168
|
|
|
169
|
+
## SpringBootAI 考试认证与证书
|
|
170
|
+
|
|
171
|
+
SpringBootAI 已提供配套的在线考试认证平台,学习者、团队成员和项目使用方可以通过认证考试检验自己对框架核心能力的掌握情况,并在通过考试后获得对应证书。
|
|
172
|
+
|
|
173
|
+
认证入口:[http://www.yucg.cn:8230](http://www.yucg.cn:8230)
|
|
174
|
+
|
|
175
|
+
认证适合以下场景:
|
|
176
|
+
|
|
177
|
+
- **个人学习证明**:用于记录自己已经系统学习 SpringBootAI 的 Web、IoC、配置、数据访问、AOP、AI 扩展和生产治理等核心内容。
|
|
178
|
+
- **团队能力评估**:团队引入 SpringBootAI 前,可以让成员通过统一考试确认基础概念、开发规范和常见问题处理能力。
|
|
179
|
+
- **项目交付背书**:在培训、内部推广、项目验收或技术选型说明中,证书可作为使用者熟悉框架体系和最佳实践的辅助证明。
|
|
180
|
+
- **持续学习路径**:认证内容覆盖从入门到进阶的关键知识点,适合结合 README、模块文档和示例工程逐步学习后再参加考试。
|
|
181
|
+
|
|
182
|
+
建议的认证准备流程:
|
|
183
|
+
|
|
184
|
+
1. 先按 README 中的“10 分钟跑通第一个接口”完成本地环境搭建,确认能够创建应用、启动服务并访问 Swagger。
|
|
185
|
+
2. 阅读 [新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md),理解 `@SpringBootApplication`、`@RestController`、`@Service`、`@Autowired`、配置文件和依赖注入的基本用法。
|
|
186
|
+
3. 根据实际使用方向阅读模块文档,例如 Web MVC、ORM、事务、缓存、安全、配置绑定、测试切片、AI、LangChain、LangGraph、MCP 和 Cloud 微服务等。
|
|
187
|
+
4. 运行示例或编写一个小型业务接口,至少覆盖 Controller、Service、配置读取、异常处理、Swagger 文档和数据访问中的若干能力。
|
|
188
|
+
5. 访问认证平台参加考试,通过后按平台页面提示查看、下载或领取证书。证书信息、考试规则、题量、通过标准和有效期以认证平台展示为准。
|
|
189
|
+
|
|
190
|
+
认证重点建议关注:
|
|
191
|
+
|
|
192
|
+
| 方向 | 建议掌握内容 |
|
|
193
|
+
|---|---|
|
|
194
|
+
| 快速入门 | 安装方式、项目结构、启动入口、扫描包、`application.yml`、Swagger 访问 |
|
|
195
|
+
| Web 与 IoC | 路由映射、参数绑定、Controller/Service 分层、Bean 注册、依赖注入、生命周期 |
|
|
196
|
+
| 数据与事务 | PyMyBatis Mapper、XML Mapper、分页、动态 SQL、事务边界、回滚行为 |
|
|
197
|
+
| 工程治理 | 配置合并、Profile、日志、健康检查、Prometheus、限流、熔断、测试切片 |
|
|
198
|
+
| 安全能力 | JWT、密码加密、SQL 注入防护、CSRF、OAuth2、访问控制与常见错误处理 |
|
|
199
|
+
| AI 生态 | ChatClient、Tools、RAG、Advisor、LangChain、LangGraph、MCP client/server |
|
|
200
|
+
|
|
169
201
|
[](https://pypi.org/project/springbootAI/)
|
|
170
202
|
[](https://pypi.org/project/springbootAI/)
|
|
171
203
|
[](https://github.com/YUCONGGEN/springbootAI/actions/workflows/ci.yml)
|
|
@@ -183,7 +215,7 @@ SpringBootAI 是一个采用 Spring 风格注解和分层结构的 Python 应用
|
|
|
183
215
|
| AI 与编排 | 模型调用、Tools、RAG、Chain、状态图、MCP client/server | LangChain / LangGraph / 官方 MCP SDK |
|
|
184
216
|
| 生产治理 | 健康检查、Prometheus、限流熔断、追踪、Swagger | prometheus-client / OpenTelemetry / OpenAPI |
|
|
185
217
|
|
|
186
|
-
当前版本是 `2.3.
|
|
218
|
+
当前版本是 `2.3.5`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 对接官方 TC + TCC 回调;`at` 模式通过 ORM 拦截器自动生成 undo_log 实现自动回滚。
|
|
187
219
|
|
|
188
220
|
[新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | [全部文档](https://github.com/YUCONGGEN/springbootAI/tree/master/doc) | [变更日志](https://github.com/YUCONGGEN/springbootAI/blob/master/CHANGELOG.md) | [安全报告](https://github.com/YUCONGGEN/springbootAI/blob/master/SECURITY.md) | [发布检查](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/RELEASE_CHECKLIST.md)
|
|
189
221
|
|
|
@@ -364,10 +396,10 @@ SpringBootAI 是一个 **Python Web 框架**。它把 Java Spring Boot 的"注
|
|
|
364
396
|
|
|
365
397
|
| 组件 | 当前版本 |
|
|
366
398
|
|------|----------|
|
|
367
|
-
| `springbootai` 框架 API | 2.3.
|
|
368
|
-
| `springbootai.orm.pymybatis` | 2.3.
|
|
369
|
-
| `springbootai.ai` AI 模块 | 2.3.
|
|
370
|
-
| `springbootai.langchain` LangChain 模块 | 2.3.
|
|
399
|
+
| `springbootai` 框架 API | 2.3.5 |
|
|
400
|
+
| `springbootai.orm.pymybatis` | 2.3.5 |
|
|
401
|
+
| `springbootai.ai` AI 模块 | 2.3.5 |
|
|
402
|
+
| `springbootai.langchain` LangChain 模块 | 2.3.5 |
|
|
371
403
|
| Python | 3.10+ |
|
|
372
404
|
|
|
373
405
|
### 1.4 适合什么场景
|
|
@@ -1,9 +1,41 @@
|
|
|
1
1
|
# SpringBootAI
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="doc/images/hero-banner.png" alt="SpringBootAI 功能总览" width="100%" />
|
|
4
|
+
<img src="https://raw.githubusercontent.com/YUCONGGEN/springbootAI/master/doc/images/hero-banner.png" alt="SpringBootAI 功能总览" width="100%" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
+
## SpringBootAI 考试认证与证书
|
|
8
|
+
|
|
9
|
+
SpringBootAI 已提供配套的在线考试认证平台,学习者、团队成员和项目使用方可以通过认证考试检验自己对框架核心能力的掌握情况,并在通过考试后获得对应证书。
|
|
10
|
+
|
|
11
|
+
认证入口:[http://www.yucg.cn:8230](http://www.yucg.cn:8230)
|
|
12
|
+
|
|
13
|
+
认证适合以下场景:
|
|
14
|
+
|
|
15
|
+
- **个人学习证明**:用于记录自己已经系统学习 SpringBootAI 的 Web、IoC、配置、数据访问、AOP、AI 扩展和生产治理等核心内容。
|
|
16
|
+
- **团队能力评估**:团队引入 SpringBootAI 前,可以让成员通过统一考试确认基础概念、开发规范和常见问题处理能力。
|
|
17
|
+
- **项目交付背书**:在培训、内部推广、项目验收或技术选型说明中,证书可作为使用者熟悉框架体系和最佳实践的辅助证明。
|
|
18
|
+
- **持续学习路径**:认证内容覆盖从入门到进阶的关键知识点,适合结合 README、模块文档和示例工程逐步学习后再参加考试。
|
|
19
|
+
|
|
20
|
+
建议的认证准备流程:
|
|
21
|
+
|
|
22
|
+
1. 先按 README 中的“10 分钟跑通第一个接口”完成本地环境搭建,确认能够创建应用、启动服务并访问 Swagger。
|
|
23
|
+
2. 阅读 [新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md),理解 `@SpringBootApplication`、`@RestController`、`@Service`、`@Autowired`、配置文件和依赖注入的基本用法。
|
|
24
|
+
3. 根据实际使用方向阅读模块文档,例如 Web MVC、ORM、事务、缓存、安全、配置绑定、测试切片、AI、LangChain、LangGraph、MCP 和 Cloud 微服务等。
|
|
25
|
+
4. 运行示例或编写一个小型业务接口,至少覆盖 Controller、Service、配置读取、异常处理、Swagger 文档和数据访问中的若干能力。
|
|
26
|
+
5. 访问认证平台参加考试,通过后按平台页面提示查看、下载或领取证书。证书信息、考试规则、题量、通过标准和有效期以认证平台展示为准。
|
|
27
|
+
|
|
28
|
+
认证重点建议关注:
|
|
29
|
+
|
|
30
|
+
| 方向 | 建议掌握内容 |
|
|
31
|
+
|---|---|
|
|
32
|
+
| 快速入门 | 安装方式、项目结构、启动入口、扫描包、`application.yml`、Swagger 访问 |
|
|
33
|
+
| Web 与 IoC | 路由映射、参数绑定、Controller/Service 分层、Bean 注册、依赖注入、生命周期 |
|
|
34
|
+
| 数据与事务 | PyMyBatis Mapper、XML Mapper、分页、动态 SQL、事务边界、回滚行为 |
|
|
35
|
+
| 工程治理 | 配置合并、Profile、日志、健康检查、Prometheus、限流、熔断、测试切片 |
|
|
36
|
+
| 安全能力 | JWT、密码加密、SQL 注入防护、CSRF、OAuth2、访问控制与常见错误处理 |
|
|
37
|
+
| AI 生态 | ChatClient、Tools、RAG、Advisor、LangChain、LangGraph、MCP client/server |
|
|
38
|
+
|
|
7
39
|
[](https://pypi.org/project/springbootAI/)
|
|
8
40
|
[](https://pypi.org/project/springbootAI/)
|
|
9
41
|
[](https://github.com/YUCONGGEN/springbootAI/actions/workflows/ci.yml)
|
|
@@ -21,7 +53,7 @@ SpringBootAI 是一个采用 Spring 风格注解和分层结构的 Python 应用
|
|
|
21
53
|
| AI 与编排 | 模型调用、Tools、RAG、Chain、状态图、MCP client/server | LangChain / LangGraph / 官方 MCP SDK |
|
|
22
54
|
| 生产治理 | 健康检查、Prometheus、限流熔断、追踪、Swagger | prometheus-client / OpenTelemetry / OpenAPI |
|
|
23
55
|
|
|
24
|
-
当前版本是 `2.3.
|
|
56
|
+
当前版本是 `2.3.5`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 对接官方 TC + TCC 回调;`at` 模式通过 ORM 拦截器自动生成 undo_log 实现自动回滚。
|
|
25
57
|
|
|
26
58
|
[新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | [全部文档](https://github.com/YUCONGGEN/springbootAI/tree/master/doc) | [变更日志](https://github.com/YUCONGGEN/springbootAI/blob/master/CHANGELOG.md) | [安全报告](https://github.com/YUCONGGEN/springbootAI/blob/master/SECURITY.md) | [发布检查](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/RELEASE_CHECKLIST.md)
|
|
27
59
|
|
|
@@ -202,10 +234,10 @@ SpringBootAI 是一个 **Python Web 框架**。它把 Java Spring Boot 的"注
|
|
|
202
234
|
|
|
203
235
|
| 组件 | 当前版本 |
|
|
204
236
|
|------|----------|
|
|
205
|
-
| `springbootai` 框架 API | 2.3.
|
|
206
|
-
| `springbootai.orm.pymybatis` | 2.3.
|
|
207
|
-
| `springbootai.ai` AI 模块 | 2.3.
|
|
208
|
-
| `springbootai.langchain` LangChain 模块 | 2.3.
|
|
237
|
+
| `springbootai` 框架 API | 2.3.5 |
|
|
238
|
+
| `springbootai.orm.pymybatis` | 2.3.5 |
|
|
239
|
+
| `springbootai.ai` AI 模块 | 2.3.5 |
|
|
240
|
+
| `springbootai.langchain` LangChain 模块 | 2.3.5 |
|
|
209
241
|
| Python | 3.10+ |
|
|
210
242
|
|
|
211
243
|
### 1.4 适合什么场景
|
|
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
|
|
|
12
12
|
[project]
|
|
13
13
|
# Distribution name: springbootAI (pip install springbootAI); import package: springbootai.
|
|
14
14
|
name = "springbootAI"
|
|
15
|
-
version = "2.3.
|
|
15
|
+
version = "2.3.5"
|
|
16
16
|
description = "Spring-style Python framework with Web, ORM, Cloud and AI integrations"
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
license = "MIT"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: springbootAI
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.5
|
|
4
4
|
Summary: Spring-style Python framework with Web, ORM, Cloud and AI integrations
|
|
5
5
|
Author-email: YuConggen <1516933915@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -163,9 +163,41 @@ Dynamic: license-file
|
|
|
163
163
|
# SpringBootAI
|
|
164
164
|
|
|
165
165
|
<p align="center">
|
|
166
|
-
<img src="doc/images/hero-banner.png" alt="SpringBootAI 功能总览" width="100%" />
|
|
166
|
+
<img src="https://raw.githubusercontent.com/YUCONGGEN/springbootAI/master/doc/images/hero-banner.png" alt="SpringBootAI 功能总览" width="100%" />
|
|
167
167
|
</p>
|
|
168
168
|
|
|
169
|
+
## SpringBootAI 考试认证与证书
|
|
170
|
+
|
|
171
|
+
SpringBootAI 已提供配套的在线考试认证平台,学习者、团队成员和项目使用方可以通过认证考试检验自己对框架核心能力的掌握情况,并在通过考试后获得对应证书。
|
|
172
|
+
|
|
173
|
+
认证入口:[http://www.yucg.cn:8230](http://www.yucg.cn:8230)
|
|
174
|
+
|
|
175
|
+
认证适合以下场景:
|
|
176
|
+
|
|
177
|
+
- **个人学习证明**:用于记录自己已经系统学习 SpringBootAI 的 Web、IoC、配置、数据访问、AOP、AI 扩展和生产治理等核心内容。
|
|
178
|
+
- **团队能力评估**:团队引入 SpringBootAI 前,可以让成员通过统一考试确认基础概念、开发规范和常见问题处理能力。
|
|
179
|
+
- **项目交付背书**:在培训、内部推广、项目验收或技术选型说明中,证书可作为使用者熟悉框架体系和最佳实践的辅助证明。
|
|
180
|
+
- **持续学习路径**:认证内容覆盖从入门到进阶的关键知识点,适合结合 README、模块文档和示例工程逐步学习后再参加考试。
|
|
181
|
+
|
|
182
|
+
建议的认证准备流程:
|
|
183
|
+
|
|
184
|
+
1. 先按 README 中的“10 分钟跑通第一个接口”完成本地环境搭建,确认能够创建应用、启动服务并访问 Swagger。
|
|
185
|
+
2. 阅读 [新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md),理解 `@SpringBootApplication`、`@RestController`、`@Service`、`@Autowired`、配置文件和依赖注入的基本用法。
|
|
186
|
+
3. 根据实际使用方向阅读模块文档,例如 Web MVC、ORM、事务、缓存、安全、配置绑定、测试切片、AI、LangChain、LangGraph、MCP 和 Cloud 微服务等。
|
|
187
|
+
4. 运行示例或编写一个小型业务接口,至少覆盖 Controller、Service、配置读取、异常处理、Swagger 文档和数据访问中的若干能力。
|
|
188
|
+
5. 访问认证平台参加考试,通过后按平台页面提示查看、下载或领取证书。证书信息、考试规则、题量、通过标准和有效期以认证平台展示为准。
|
|
189
|
+
|
|
190
|
+
认证重点建议关注:
|
|
191
|
+
|
|
192
|
+
| 方向 | 建议掌握内容 |
|
|
193
|
+
|---|---|
|
|
194
|
+
| 快速入门 | 安装方式、项目结构、启动入口、扫描包、`application.yml`、Swagger 访问 |
|
|
195
|
+
| Web 与 IoC | 路由映射、参数绑定、Controller/Service 分层、Bean 注册、依赖注入、生命周期 |
|
|
196
|
+
| 数据与事务 | PyMyBatis Mapper、XML Mapper、分页、动态 SQL、事务边界、回滚行为 |
|
|
197
|
+
| 工程治理 | 配置合并、Profile、日志、健康检查、Prometheus、限流、熔断、测试切片 |
|
|
198
|
+
| 安全能力 | JWT、密码加密、SQL 注入防护、CSRF、OAuth2、访问控制与常见错误处理 |
|
|
199
|
+
| AI 生态 | ChatClient、Tools、RAG、Advisor、LangChain、LangGraph、MCP client/server |
|
|
200
|
+
|
|
169
201
|
[](https://pypi.org/project/springbootAI/)
|
|
170
202
|
[](https://pypi.org/project/springbootAI/)
|
|
171
203
|
[](https://github.com/YUCONGGEN/springbootAI/actions/workflows/ci.yml)
|
|
@@ -183,7 +215,7 @@ SpringBootAI 是一个采用 Spring 风格注解和分层结构的 Python 应用
|
|
|
183
215
|
| AI 与编排 | 模型调用、Tools、RAG、Chain、状态图、MCP client/server | LangChain / LangGraph / 官方 MCP SDK |
|
|
184
216
|
| 生产治理 | 健康检查、Prometheus、限流熔断、追踪、Swagger | prometheus-client / OpenTelemetry / OpenAPI |
|
|
185
217
|
|
|
186
|
-
当前版本是 `2.3.
|
|
218
|
+
当前版本是 `2.3.5`;支持 Python 3.10、3.11 和 3.12,许可证为 MIT。项目仍标记为 Beta。用于公网高并发、合规敏感或支付/订单/库存等核心系统前,必须完成目标数据库、流量模型、故障恢复和安全基线验证。内嵌 Gateway 适合内部路由,不替代公网 Nginx/Kong/WAF;Seata `distributed` 对接官方 TC + TCC 回调;`at` 模式通过 ORM 拦截器自动生成 undo_log 实现自动回滚。
|
|
187
219
|
|
|
188
220
|
[新手指南](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/BEGINNER_GUIDE.md) | [全部文档](https://github.com/YUCONGGEN/springbootAI/tree/master/doc) | [变更日志](https://github.com/YUCONGGEN/springbootAI/blob/master/CHANGELOG.md) | [安全报告](https://github.com/YUCONGGEN/springbootAI/blob/master/SECURITY.md) | [发布检查](https://github.com/YUCONGGEN/springbootAI/blob/master/doc/RELEASE_CHECKLIST.md)
|
|
189
221
|
|
|
@@ -364,10 +396,10 @@ SpringBootAI 是一个 **Python Web 框架**。它把 Java Spring Boot 的"注
|
|
|
364
396
|
|
|
365
397
|
| 组件 | 当前版本 |
|
|
366
398
|
|------|----------|
|
|
367
|
-
| `springbootai` 框架 API | 2.3.
|
|
368
|
-
| `springbootai.orm.pymybatis` | 2.3.
|
|
369
|
-
| `springbootai.ai` AI 模块 | 2.3.
|
|
370
|
-
| `springbootai.langchain` LangChain 模块 | 2.3.
|
|
399
|
+
| `springbootai` 框架 API | 2.3.5 |
|
|
400
|
+
| `springbootai.orm.pymybatis` | 2.3.5 |
|
|
401
|
+
| `springbootai.ai` AI 模块 | 2.3.5 |
|
|
402
|
+
| `springbootai.langchain` LangChain 模块 | 2.3.5 |
|
|
371
403
|
| Python | 3.10+ |
|
|
372
404
|
|
|
373
405
|
### 1.4 适合什么场景
|
|
@@ -76,6 +76,7 @@ springbootai/context/__init__.py
|
|
|
76
76
|
springbootai/context/application_context.py
|
|
77
77
|
springbootai/context/bean_definition.py
|
|
78
78
|
springbootai/context/bean_factory.py
|
|
79
|
+
springbootai/context/lifecycle.py
|
|
79
80
|
springbootai/context/registry.py
|
|
80
81
|
springbootai/context/scanner.py
|
|
81
82
|
springbootai/core/__init__.py
|
|
@@ -268,6 +269,7 @@ tests/test_ai_module.py
|
|
|
268
269
|
tests/test_annotation_combinations.py
|
|
269
270
|
tests/test_annotations_contract.py
|
|
270
271
|
tests/test_aop_annotations_full.py
|
|
272
|
+
tests/test_aop_pointcut_enhancements.py
|
|
271
273
|
tests/test_audit_time.py
|
|
272
274
|
tests/test_batch.py
|
|
273
275
|
tests/test_bean_utils.py
|
|
@@ -296,6 +298,7 @@ tests/test_di_config_event_full.py
|
|
|
296
298
|
tests/test_excel_module.py
|
|
297
299
|
tests/test_hateoas.py
|
|
298
300
|
tests/test_i18n_module.py
|
|
301
|
+
tests/test_ioc_lifecycle_enhancements.py
|
|
299
302
|
tests/test_jpa_version_transient.py
|
|
300
303
|
tests/test_kafka.py
|
|
301
304
|
tests/test_langchain_comprehensive.py
|
|
@@ -54,7 +54,7 @@ from springbootai.ai.observability import AIMetrics, ai_metrics
|
|
|
54
54
|
from springbootai.ai.autoconfig import AIProperties, bind_ai_config, configure_ai
|
|
55
55
|
from springbootai.ai.annotation_runtime import ContentModerationError
|
|
56
56
|
|
|
57
|
-
__version__ = "2.3.
|
|
57
|
+
__version__ = "2.3.5"
|
|
58
58
|
|
|
59
59
|
__all__ = [
|
|
60
60
|
# core
|
|
@@ -15,6 +15,7 @@ from .core import (
|
|
|
15
15
|
Component,
|
|
16
16
|
Aspect,
|
|
17
17
|
Pointcut,
|
|
18
|
+
Order,
|
|
18
19
|
Before,
|
|
19
20
|
After,
|
|
20
21
|
Around,
|
|
@@ -102,6 +103,10 @@ from .conditional import (
|
|
|
102
103
|
ConditionalOnBean,
|
|
103
104
|
ConditionalOnMissingBean,
|
|
104
105
|
ConditionalOnClass,
|
|
106
|
+
ConditionalOnMissingClass,
|
|
107
|
+
ConditionalOnWebApplication,
|
|
108
|
+
ConditionalOnNotWebApplication,
|
|
109
|
+
ConditionalOnExpression,
|
|
105
110
|
)
|
|
106
111
|
|
|
107
112
|
# 可选导入:消息队列注解(RabbitMQ 需要 pika,Kafka 需要 kafka-python)
|
|
@@ -174,6 +179,7 @@ __all__ = [
|
|
|
174
179
|
"Component",
|
|
175
180
|
"Aspect",
|
|
176
181
|
"Pointcut",
|
|
182
|
+
"Order",
|
|
177
183
|
"Before",
|
|
178
184
|
"After",
|
|
179
185
|
"Around",
|
|
@@ -255,6 +261,10 @@ __all__ = [
|
|
|
255
261
|
"ConditionalOnBean",
|
|
256
262
|
"ConditionalOnMissingBean",
|
|
257
263
|
"ConditionalOnClass",
|
|
264
|
+
"ConditionalOnMissingClass",
|
|
265
|
+
"ConditionalOnWebApplication",
|
|
266
|
+
"ConditionalOnNotWebApplication",
|
|
267
|
+
"ConditionalOnExpression",
|
|
258
268
|
# 消息队列注解
|
|
259
269
|
"RabbitListener",
|
|
260
270
|
"RabbitTemplate",
|
|
@@ -238,10 +238,118 @@ class ConditionalOnClass(SpringAnnotation):
|
|
|
238
238
|
return False
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
class ConditionalOnMissingClass(SpringAnnotation):
|
|
242
|
+
"""``@ConditionalOnMissingClass``:类路径中不存在指定类时装配(对齐 Spring Boot)。"""
|
|
243
|
+
|
|
244
|
+
_annotation_type = _CONDITIONAL_TYPE
|
|
245
|
+
|
|
246
|
+
def __init__(self, name: str, value: Optional[Type] = None):
|
|
247
|
+
super().__init__(name=name, value=value)
|
|
248
|
+
|
|
249
|
+
def matches(self, ctx: Any) -> bool:
|
|
250
|
+
on_class = ConditionalOnClass(name=self.name, value=self.value)
|
|
251
|
+
return not on_class.matches(ctx)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class ConditionalOnWebApplication(SpringAnnotation):
|
|
255
|
+
"""``@ConditionalOnWebApplication``:Web应用环境下装配(对齐 Spring Boot)。"""
|
|
256
|
+
|
|
257
|
+
_annotation_type = _CONDITIONAL_TYPE
|
|
258
|
+
|
|
259
|
+
def __init__(self, type: str = "any"):
|
|
260
|
+
"""type: 'servlet' | 'reactive' | 'any'。Python框架统一为reactive(FastAPI)。"""
|
|
261
|
+
super().__init__(type=type)
|
|
262
|
+
|
|
263
|
+
def matches(self, ctx: Any) -> bool:
|
|
264
|
+
# 判断是否为Web环境:检查是否有WebApplicationContext
|
|
265
|
+
from springbootai.web.web_context import WebApplicationContext
|
|
266
|
+
return isinstance(ctx, WebApplicationContext) or hasattr(ctx, 'fastapi_app')
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class ConditionalOnNotWebApplication(SpringAnnotation):
|
|
270
|
+
"""``@ConditionalOnNotWebApplication``:非Web应用环境下装配。"""
|
|
271
|
+
|
|
272
|
+
_annotation_type = _CONDITIONAL_TYPE
|
|
273
|
+
|
|
274
|
+
def matches(self, ctx: Any) -> bool:
|
|
275
|
+
on_web = ConditionalOnWebApplication()
|
|
276
|
+
return not on_web.matches(ctx)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class ConditionalOnExpression(SpringAnnotation):
|
|
280
|
+
"""``@ConditionalOnExpression``:基于SpEL风格表达式的条件装配。
|
|
281
|
+
|
|
282
|
+
支持简单表达式:配置属性引用和基本布尔运算。
|
|
283
|
+
语法:
|
|
284
|
+
"${property:default}" - 配置属性值
|
|
285
|
+
"true" / "false" - 布尔字面量
|
|
286
|
+
"property == value" - 等值比较
|
|
287
|
+
"!property" - 逻辑非
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
_annotation_type = _CONDITIONAL_TYPE
|
|
291
|
+
|
|
292
|
+
def __init__(self, expression: str):
|
|
293
|
+
super().__init__(expression=expression)
|
|
294
|
+
|
|
295
|
+
def matches(self, ctx: Any) -> bool:
|
|
296
|
+
loader = getattr(ctx, "config_loader", None)
|
|
297
|
+
expr = self.expression.strip()
|
|
298
|
+
|
|
299
|
+
# 简单布尔字面量
|
|
300
|
+
if expr.lower() in ("true", "false"):
|
|
301
|
+
return expr.lower() == "true"
|
|
302
|
+
|
|
303
|
+
# 逻辑非
|
|
304
|
+
if expr.startswith("!"):
|
|
305
|
+
inner = ConditionalOnExpression(expr[1:])
|
|
306
|
+
return not inner.matches(ctx)
|
|
307
|
+
|
|
308
|
+
# 属性引用 ${...}
|
|
309
|
+
import re
|
|
310
|
+
prop_match = re.match(r'^\$\{([^:}]+)(?::(.+))?\}$', expr)
|
|
311
|
+
if prop_match:
|
|
312
|
+
key = prop_match.group(1)
|
|
313
|
+
default = prop_match.group(2)
|
|
314
|
+
try:
|
|
315
|
+
value = loader.get(key) if loader is not None else None
|
|
316
|
+
except Exception:
|
|
317
|
+
value = None
|
|
318
|
+
if value is None:
|
|
319
|
+
value = default
|
|
320
|
+
return value is not None and str(value).lower() not in ("false", "0", "no", "off", "")
|
|
321
|
+
|
|
322
|
+
# 等值比较 prop == value
|
|
323
|
+
eq_match = re.match(r'^(.+?)\s*==\s*(.+)$', expr)
|
|
324
|
+
if eq_match:
|
|
325
|
+
left = eq_match.group(1).strip()
|
|
326
|
+
right = eq_match.group(2).strip().strip("'\"")
|
|
327
|
+
if left.startswith("${") and left.endswith("}"):
|
|
328
|
+
key = left[2:-1].split(":")[0]
|
|
329
|
+
try:
|
|
330
|
+
actual = loader.get(key) if loader is not None else None
|
|
331
|
+
except Exception:
|
|
332
|
+
actual = None
|
|
333
|
+
return str(actual) == right
|
|
334
|
+
return left.strip("'\"") == right
|
|
335
|
+
|
|
336
|
+
# 简单属性名:存在且不为false即装配
|
|
337
|
+
if expr and " " not in expr:
|
|
338
|
+
try:
|
|
339
|
+
value = loader.get(expr) if loader is not None else None
|
|
340
|
+
except Exception:
|
|
341
|
+
value = None
|
|
342
|
+
return value is not None and str(value).lower() not in ("false", "0", "no", "off", "")
|
|
343
|
+
|
|
344
|
+
return False
|
|
345
|
+
|
|
346
|
+
|
|
241
347
|
# 供 _matches_conditions 识别的全部条件注解类型
|
|
242
348
|
CONDITION_ANNOTATIONS = (
|
|
243
349
|
Conditional, ConditionalOnProperty, ConditionalOnBean,
|
|
244
350
|
ConditionalOnMissingBean, ConditionalOnClass,
|
|
351
|
+
ConditionalOnMissingClass, ConditionalOnWebApplication,
|
|
352
|
+
ConditionalOnNotWebApplication, ConditionalOnExpression,
|
|
245
353
|
)
|
|
246
354
|
|
|
247
355
|
|
|
@@ -267,6 +375,10 @@ __all__ = [
|
|
|
267
375
|
"ConditionalOnBean",
|
|
268
376
|
"ConditionalOnMissingBean",
|
|
269
377
|
"ConditionalOnClass",
|
|
378
|
+
"ConditionalOnMissingClass",
|
|
379
|
+
"ConditionalOnWebApplication",
|
|
380
|
+
"ConditionalOnNotWebApplication",
|
|
381
|
+
"ConditionalOnExpression",
|
|
270
382
|
"CONDITION_ANNOTATIONS",
|
|
271
383
|
"all_conditions_match",
|
|
272
384
|
]
|
|
@@ -7,7 +7,7 @@ __all__ = [
|
|
|
7
7
|
"SpringAnnotation", "get_spring_annotations", "ApplicationEvent", "EventListener",
|
|
8
8
|
"SpringBootApplication", "ComponentScan", "RestController", "Controller",
|
|
9
9
|
"RequestMapping", "GetMapping", "PostMapping", "PutMapping", "PatchMapping",
|
|
10
|
-
"DeleteMapping", "Service", "Component", "Aspect", "Pointcut", "Before",
|
|
10
|
+
"DeleteMapping", "Service", "Component", "Aspect", "Pointcut", "Order", "Before",
|
|
11
11
|
"After", "Around", "AfterReturning", "AfterThrowing", "Repository", "Autowired",
|
|
12
12
|
"Qualifier", "Configuration", "Scope", "Bean", "Value", "ConfigurationProperties",
|
|
13
13
|
"RequestParam", "PathVariable", "RequestBody", "RequestPart", "FileUpload",
|
|
@@ -265,6 +265,29 @@ class Pointcut(SpringAnnotation):
|
|
|
265
265
|
super().__init__(value=value.strip())
|
|
266
266
|
|
|
267
267
|
|
|
268
|
+
class Order(SpringAnnotation):
|
|
269
|
+
"""声明组件/通知的执行优先级(对齐 Spring ``@Order``)。
|
|
270
|
+
|
|
271
|
+
数值越小优先级越高:
|
|
272
|
+
- 切面通知:优先级高的 @Before 先执行、@After 后执行(对齐 Spring 语义)
|
|
273
|
+
- BeanPostProcessor / 拦截器:数值小的先执行
|
|
274
|
+
|
|
275
|
+
使用示例:
|
|
276
|
+
@Aspect
|
|
277
|
+
@Order(1) # 比 @Order(2) 的通知先执行
|
|
278
|
+
class LoggingAspect:
|
|
279
|
+
...
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
_annotation_type = "order"
|
|
283
|
+
|
|
284
|
+
def __init__(self, value: int = 0):
|
|
285
|
+
super().__init__(value=int(value))
|
|
286
|
+
|
|
287
|
+
def get_order(self) -> int:
|
|
288
|
+
return int(self.value)
|
|
289
|
+
|
|
290
|
+
|
|
268
291
|
class _AdviceAnnotation:
|
|
269
292
|
def _init_advice(self, value: str = "", *, pointcut: Optional[str] = None):
|
|
270
293
|
expression = pointcut if pointcut is not None else value
|
|
@@ -73,6 +73,19 @@ class _AdviceDefinition:
|
|
|
73
73
|
annotation: Any
|
|
74
74
|
expression: str
|
|
75
75
|
pointcuts: Dict[str, str]
|
|
76
|
+
order: int = 0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _aspect_order(aspect_class: type) -> int:
|
|
80
|
+
"""解析切面类上的 ``@Order`` 优先级(默认 0,数值越小优先级越高)。"""
|
|
81
|
+
for annotation in get_spring_annotations(aspect_class):
|
|
82
|
+
if type(annotation).__name__ == "Order":
|
|
83
|
+
value = getattr(annotation, "value", 0)
|
|
84
|
+
try:
|
|
85
|
+
return int(value)
|
|
86
|
+
except (TypeError, ValueError):
|
|
87
|
+
return 0
|
|
88
|
+
return 0
|
|
76
89
|
|
|
77
90
|
|
|
78
91
|
def is_aspect_class(bean_class: type) -> bool:
|
|
@@ -118,6 +131,7 @@ def collect_advice_definitions(bean_factory: Any) -> List[_AdviceDefinition]:
|
|
|
118
131
|
annotation=annotation,
|
|
119
132
|
expression=annotation.value,
|
|
120
133
|
pointcuts=pointcuts,
|
|
134
|
+
order=_aspect_order(aspect_class),
|
|
121
135
|
)
|
|
122
136
|
)
|
|
123
137
|
return definitions
|
|
@@ -181,6 +195,48 @@ def _execution_pattern(expression: str) -> str:
|
|
|
181
195
|
return parts[-1]
|
|
182
196
|
|
|
183
197
|
|
|
198
|
+
def _annotation_name_matches(annotation: Any, expected: str) -> bool:
|
|
199
|
+
"""判断注解的类名(短名或全限定名)是否匹配期望名。"""
|
|
200
|
+
return (
|
|
201
|
+
type(annotation).__name__ == expected
|
|
202
|
+
or f"{type(annotation).__module__}.{type(annotation).__name__}" == expected
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _method_parameter_types(method: Callable) -> List[Any]:
|
|
207
|
+
"""提取方法的参数类型注解(无注解的参数视为 ``object``)。"""
|
|
208
|
+
try:
|
|
209
|
+
signature = inspect.signature(method)
|
|
210
|
+
except (TypeError, ValueError):
|
|
211
|
+
return []
|
|
212
|
+
types_list: List[Any] = []
|
|
213
|
+
for parameter in signature.parameters.values():
|
|
214
|
+
if parameter.kind in (
|
|
215
|
+
inspect.Parameter.VAR_POSITIONAL,
|
|
216
|
+
inspect.Parameter.VAR_KEYWORD,
|
|
217
|
+
):
|
|
218
|
+
continue
|
|
219
|
+
annotation = parameter.annotation
|
|
220
|
+
if annotation is inspect.Parameter.empty:
|
|
221
|
+
types_list.append(object)
|
|
222
|
+
else:
|
|
223
|
+
types_list.append(annotation)
|
|
224
|
+
return types_list
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _type_matches(actual: Any, expected_name: str) -> bool:
|
|
228
|
+
"""判断类型是否匹配期望类型名(短名或全限定名)。"""
|
|
229
|
+
if actual is None:
|
|
230
|
+
actual = object
|
|
231
|
+
try:
|
|
232
|
+
if actual.__name__ == expected_name:
|
|
233
|
+
return True
|
|
234
|
+
module = getattr(actual, "__module__", "")
|
|
235
|
+
return f"{module}.{actual.__name__}" == expected_name
|
|
236
|
+
except Exception:
|
|
237
|
+
return False
|
|
238
|
+
|
|
239
|
+
|
|
184
240
|
def _matches_atom(
|
|
185
241
|
expression: str,
|
|
186
242
|
*,
|
|
@@ -227,10 +283,55 @@ def _matches_atom(
|
|
|
227
283
|
if not expected:
|
|
228
284
|
raise ValueError("@annotation pointcut must include an annotation name")
|
|
229
285
|
return any(
|
|
230
|
-
|
|
231
|
-
or f"{type(annotation).__module__}.{type(annotation).__name__}" == expected
|
|
286
|
+
_annotation_name_matches(annotation, expected)
|
|
232
287
|
for annotation in get_spring_annotations(method)
|
|
233
288
|
)
|
|
289
|
+
if expression.startswith("@within(") and expression.endswith(")"):
|
|
290
|
+
expected = expression[len("@within("):-1].strip()
|
|
291
|
+
if not expected:
|
|
292
|
+
raise ValueError("@within pointcut must include an annotation name")
|
|
293
|
+
return any(
|
|
294
|
+
_annotation_name_matches(annotation, expected)
|
|
295
|
+
for annotation in get_spring_annotations(target_class)
|
|
296
|
+
)
|
|
297
|
+
if expression.startswith("@target(") and expression.endswith(")"):
|
|
298
|
+
expected = expression[len("@target("):-1].strip()
|
|
299
|
+
if not expected:
|
|
300
|
+
raise ValueError("@target pointcut must include an annotation name")
|
|
301
|
+
return any(
|
|
302
|
+
_annotation_name_matches(annotation, expected)
|
|
303
|
+
for annotation in get_spring_annotations(target_class)
|
|
304
|
+
)
|
|
305
|
+
if expression.startswith("args(") and expression.endswith(")"):
|
|
306
|
+
inner = expression[len("args("):-1].strip()
|
|
307
|
+
if not inner:
|
|
308
|
+
raise ValueError("args pointcut must include parameter types")
|
|
309
|
+
expected_types = [part.strip() for part in inner.split(",") if part.strip()]
|
|
310
|
+
actual_types = _method_parameter_types(method)
|
|
311
|
+
# 无类型约束时(空签名)不匹配任何 args 表达式
|
|
312
|
+
if not actual_types:
|
|
313
|
+
return False
|
|
314
|
+
# 参数数量必须一致(或用 .. 表示任意前缀,简化为前缀匹配)
|
|
315
|
+
if len(expected_types) != len(actual_types):
|
|
316
|
+
return False
|
|
317
|
+
return all(
|
|
318
|
+
_type_matches(actual, expected)
|
|
319
|
+
for actual, expected in zip(actual_types, expected_types)
|
|
320
|
+
)
|
|
321
|
+
if expression.startswith("@args(") and expression.endswith(")"):
|
|
322
|
+
expected = expression[len("@args("):-1].strip()
|
|
323
|
+
if not expected:
|
|
324
|
+
raise ValueError("@args pointcut must include an annotation name")
|
|
325
|
+
# 匹配方法参数上是否带有指定注解
|
|
326
|
+
try:
|
|
327
|
+
signature = inspect.signature(method)
|
|
328
|
+
except (TypeError, ValueError):
|
|
329
|
+
return False
|
|
330
|
+
for parameter in signature.parameters.values():
|
|
331
|
+
for annotation in get_spring_annotations(parameter.annotation) if isinstance(parameter.annotation, type) else []:
|
|
332
|
+
if _annotation_name_matches(annotation, expected):
|
|
333
|
+
return True
|
|
334
|
+
return False
|
|
234
335
|
raise ValueError(f"Unsupported pointcut expression: {expression}")
|
|
235
336
|
|
|
236
337
|
|
|
@@ -395,6 +496,14 @@ def apply_aspects(
|
|
|
395
496
|
item for item in advice if type(item.annotation) is AfterThrowing
|
|
396
497
|
]
|
|
397
498
|
|
|
499
|
+
# @Order 优先级:before/around 按 order 升序(值小先执行);
|
|
500
|
+
# after/after_returning/after_throwing 按 order 降序(值大先执行),形成"洋葱"结构。
|
|
501
|
+
before.sort(key=lambda item: item.order)
|
|
502
|
+
around.sort(key=lambda item: item.order)
|
|
503
|
+
after.sort(key=lambda item: item.order, reverse=True)
|
|
504
|
+
after_returning.sort(key=lambda item: item.order, reverse=True)
|
|
505
|
+
after_throwing.sort(key=lambda item: item.order, reverse=True)
|
|
506
|
+
|
|
398
507
|
def business_args(call_args):
|
|
399
508
|
if call_args and call_args[0] is target:
|
|
400
509
|
return call_args[1:]
|