springbootAI 1.8.0__py3-none-any.whl
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.
- spring/__init__.py +66 -0
- spring/ai/__init__.py +78 -0
- spring/ai/advisors.py +139 -0
- spring/ai/annotations.py +74 -0
- spring/ai/autoconfig.py +481 -0
- spring/ai/core.py +391 -0
- spring/ai/etl.py +188 -0
- spring/ai/memory.py +109 -0
- spring/ai/observability.py +129 -0
- spring/ai/providers.py +789 -0
- spring/ai/resilience.py +258 -0
- spring/ai/tools.py +106 -0
- spring/ai/vectorstore.py +303 -0
- spring/annotations/__init__.py +188 -0
- spring/annotations/cache.py +126 -0
- spring/annotations/cloud.py +207 -0
- spring/annotations/conditional.py +272 -0
- spring/annotations/core.py +864 -0
- spring/annotations/messaging.py +107 -0
- spring/aop/__init__.py +4 -0
- spring/aop/cloud_aop.py +404 -0
- spring/aop/comprehensive_aop.py +1015 -0
- spring/aop/method_interceptor.py +19 -0
- spring/aop/proxy_factory.py +55 -0
- spring/cloud/__init__.py +76 -0
- spring/cloud/discovery.py +364 -0
- spring/cloud/feign.py +469 -0
- spring/cloud/gateway.py +452 -0
- spring/cloud/load_balancer.py +149 -0
- spring/cloud/seata.py +557 -0
- spring/cloud/sentinel.py +525 -0
- spring/cloud/tracer.py +337 -0
- spring/config/__init__.py +21 -0
- spring/config/binding.py +206 -0
- spring/config/config_loader.py +405 -0
- spring/context/__init__.py +13 -0
- spring/context/application_context.py +589 -0
- spring/context/bean_definition.py +70 -0
- spring/context/bean_factory.py +1052 -0
- spring/context/registry.py +58 -0
- spring/context/scanner.py +106 -0
- spring/core/__init__.py +3 -0
- spring/core/graceful_shutdown.py +196 -0
- spring/core/typing_utils.py +50 -0
- spring/csv/__init__.py +52 -0
- spring/csv/annotations.py +402 -0
- spring/csv/converters.py +69 -0
- spring/csv/easy_csv.py +95 -0
- spring/csv/exceptions.py +27 -0
- spring/csv/reader.py +195 -0
- spring/csv/writer.py +155 -0
- spring/data/__init__.py +54 -0
- spring/data/page.py +181 -0
- spring/data/repository.py +274 -0
- spring/data/specification.py +228 -0
- spring/datasource/__init__.py +66 -0
- spring/datasource/annotations.py +133 -0
- spring/datasource/context.py +69 -0
- spring/datasource/dynamic.py +148 -0
- spring/event/__init__.py +7 -0
- spring/event/publisher.py +69 -0
- spring/excel/__init__.py +51 -0
- spring/excel/annotations.py +405 -0
- spring/excel/converters.py +231 -0
- spring/excel/easy_excel.py +94 -0
- spring/excel/exceptions.py +31 -0
- spring/excel/reader.py +254 -0
- spring/excel/style.py +95 -0
- spring/excel/writer.py +197 -0
- spring/i18n/__init__.py +97 -0
- spring/i18n/accessor.py +94 -0
- spring/i18n/auto_config.py +177 -0
- spring/i18n/holder.py +106 -0
- spring/i18n/locale.py +152 -0
- spring/i18n/locale_resolver.py +367 -0
- spring/i18n/message_source.py +250 -0
- spring/i18n/middleware.py +79 -0
- spring/i18n/properties.py +168 -0
- spring/i18n/sources.py +255 -0
- spring/logging/__init__.py +1 -0
- spring/logging/loguru_logger.py +228 -0
- spring/main.py +378 -0
- spring/messaging/__init__.py +1 -0
- spring/messaging/rabbitmq.py +302 -0
- spring/monitoring/__init__.py +1 -0
- spring/monitoring/prometheus.py +199 -0
- spring/orm/__init__.py +258 -0
- spring/orm/database.py +222 -0
- spring/orm/ddl_auto.py +1217 -0
- spring/orm/migration.py +419 -0
- spring/orm/mybatis_integration.py +400 -0
- spring/orm/pymybatis/__init__.py +86 -0
- spring/orm/pymybatis/annotations/__init__.py +30 -0
- spring/orm/pymybatis/annotations/annotations.py +332 -0
- spring/orm/pymybatis/cache/__init__.py +47 -0
- spring/orm/pymybatis/cache/cache.py +371 -0
- spring/orm/pymybatis/cache/redis_cache.py +434 -0
- spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
- spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
- spring/orm/pymybatis/configuration.py +525 -0
- spring/orm/pymybatis/core/__init__.py +10 -0
- spring/orm/pymybatis/core/sql_session.py +1382 -0
- spring/orm/pymybatis/core/sql_session_factory.py +76 -0
- spring/orm/pymybatis/dialect/__init__.py +9 -0
- spring/orm/pymybatis/dialect/dialect.py +445 -0
- spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
- spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
- spring/orm/pymybatis/interceptor/__init__.py +31 -0
- spring/orm/pymybatis/interceptor/interceptor.py +427 -0
- spring/orm/pymybatis/mapper/__init__.py +9 -0
- spring/orm/pymybatis/mapper/mapper.py +540 -0
- spring/orm/pymybatis/metrics/__init__.py +41 -0
- spring/orm/pymybatis/metrics/metrics.py +595 -0
- spring/orm/pymybatis/pool/__init__.py +9 -0
- spring/orm/pymybatis/pool/connection_pool.py +711 -0
- spring/orm/pymybatis/security/__init__.py +19 -0
- spring/orm/pymybatis/security/access_control.py +415 -0
- spring/orm/pymybatis/security/password_encoder.py +293 -0
- spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
- spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
- spring/orm/pymybatis/transaction/__init__.py +9 -0
- spring/orm/pymybatis/transaction/transaction.py +288 -0
- spring/orm/pymybatis/type_handler/__init__.py +37 -0
- spring/orm/pymybatis/type_handler/type_handler.py +473 -0
- spring/orm/pymybatis/version.py +9 -0
- spring/orm/pymybatis/xml_parser/__init__.py +9 -0
- spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
- spring/retry/__init__.py +12 -0
- spring/retry/retry_annotations.py +71 -0
- spring/retry/retry_decorator.py +155 -0
- spring/scheduling/__init__.py +3 -0
- spring/scheduling/scheduler.py +389 -0
- spring/security/__init__.py +39 -0
- spring/security/jwt_utils.py +281 -0
- spring/security/replay_protection.py +206 -0
- spring/security/secret_manager.py +226 -0
- spring/security/security_aop.py +248 -0
- spring/security/security_context.py +172 -0
- spring/test/__init__.py +45 -0
- spring/test/slicing.py +341 -0
- spring/tracing/__init__.py +11 -0
- spring/tracing/skywalking.py +229 -0
- spring/tx/__init__.py +52 -0
- spring/tx/events.py +172 -0
- spring/tx/synchronization.py +143 -0
- spring/utils/__init__.py +5 -0
- spring/utils/banner.py +32 -0
- spring/utils/logger.py +73 -0
- spring/utils/redis_client.py +526 -0
- spring/validation/__init__.py +55 -0
- spring/validation/aop.py +141 -0
- spring/validation/constraints.py +357 -0
- spring/validation/exceptions.py +55 -0
- spring/validation/validator.py +139 -0
- spring/web/__init__.py +12 -0
- spring/web/actuator.py +319 -0
- spring/web/exception_handler.py +61 -0
- spring/web/health.py +399 -0
- spring/web/interceptor.py +91 -0
- spring/web/result.py +44 -0
- spring/web/swagger.py +601 -0
- spring/web/web_context.py +755 -0
- spring/websocket/__init__.py +86 -0
- spring/websocket/annotations.py +169 -0
- spring/websocket/broker.py +238 -0
- spring/websocket/exceptions.py +26 -0
- spring/websocket/handler.py +243 -0
- spring/websocket/router.py +526 -0
- spring/websocket/session.py +216 -0
- springbootai-1.8.0.dist-info/METADATA +2796 -0
- springbootai-1.8.0.dist-info/RECORD +175 -0
- springbootai-1.8.0.dist-info/WHEEL +5 -0
- springbootai-1.8.0.dist-info/entry_points.txt +2 -0
- springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
- springbootai-1.8.0.dist-info/top_level.txt +1 -0
spring/tx/events.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""事务事件监听(对齐 Spring ``@TransactionalEventListener`` + ``TransactionalEventPublisher``)。
|
|
2
|
+
|
|
3
|
+
``@TransactionalEventListener(phase=...)`` 标记的监听器在事务达到指定阶段时才触发:
|
|
4
|
+
- ``BEFORE_COMMIT``:事务提交前
|
|
5
|
+
- ``AFTER_COMMIT``:事务提交后(默认)
|
|
6
|
+
- ``AFTER_ROLLBACK``:事务回滚后
|
|
7
|
+
- ``AFTER_COMPLETION``:事务完成后(提交或回滚)
|
|
8
|
+
|
|
9
|
+
设计:
|
|
10
|
+
- **事件延迟**:``TransactionalEventPublisher.publish_event`` 发布事件时,若当前存在活动事务,
|
|
11
|
+
把事务监听器包装为 ``TransactionSynchronization`` 注册到 ``TransactionSynchronizationManager``,
|
|
12
|
+
等待 ``@Transactional`` 切面在对应阶段触发;无活动事务时按 ``fallback_execution`` 决定是否立即执行。
|
|
13
|
+
- **复用既有事件基础设施**:普通 ``@EventListener`` 由 ``ApplicationEventPublisher`` 立即触发,
|
|
14
|
+
本模块仅处理事务监听器;可委托 ``ApplicationEventPublisher`` 处理普通监听器以共存的。
|
|
15
|
+
- **注解基类**:``TransactionalEventListener`` 继承 ``SpringAnnotation``,元数据挂到
|
|
16
|
+
``__spring_annotations__``,由 ``ApplicationContext._register_event_listeners`` 扫描注册。
|
|
17
|
+
|
|
18
|
+
与 Java 的差异:
|
|
19
|
+
- Spring ``@TransactionalEventListener`` 默认 ``AFTER_COMMIT``,无事务时不执行;本实现一致。
|
|
20
|
+
- 监听器顺序由注册顺序决定(Spring 支持 ``@Order``,本实现保留 ``order`` 字段供扩展)。
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import asyncio
|
|
25
|
+
import inspect
|
|
26
|
+
import logging
|
|
27
|
+
from typing import Any, Callable, List, Optional, Tuple, Type
|
|
28
|
+
|
|
29
|
+
from spring.annotations.core import ApplicationEvent, SpringAnnotation
|
|
30
|
+
from .synchronization import (
|
|
31
|
+
TransactionPhase,
|
|
32
|
+
TransactionSynchronization,
|
|
33
|
+
TransactionSynchronizationManager,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger("Spring.Tx.Events")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TransactionalEventListener(SpringAnnotation):
|
|
40
|
+
"""``@TransactionalEventListener`` 标记方法为事务阶段事件监听器。
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
phase: 触发阶段,默认 ``AFTER_COMMIT``。
|
|
44
|
+
fallback_execution: 无活动事务时是否立即执行(默认 ``False``,对齐 Spring)。
|
|
45
|
+
event_type: 监听的事件类型;未指定时从方法首参类型推断。
|
|
46
|
+
order: 监听器顺序(保留字段,当前按注册顺序触发)。
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
_annotation_type = "tx_event_listener"
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
phase: TransactionPhase = TransactionPhase.AFTER_COMMIT,
|
|
54
|
+
fallback_execution: bool = False,
|
|
55
|
+
event_type: Optional[Type[ApplicationEvent]] = None,
|
|
56
|
+
order: int = 0,
|
|
57
|
+
):
|
|
58
|
+
# 支持装饰器简写:@TransactionalEventListener(SomeEvent)
|
|
59
|
+
super().__init__(
|
|
60
|
+
phase=phase,
|
|
61
|
+
fallback_execution=fallback_execution,
|
|
62
|
+
event_type=event_type,
|
|
63
|
+
order=order,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# 事务监听器条目:(event_type, callback, phase, fallback_execution, order)
|
|
68
|
+
_TxListenerEntry = Tuple[Optional[Type[ApplicationEvent]], Callable, TransactionPhase, bool, int]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _ListenerSynchronization(TransactionSynchronization):
|
|
72
|
+
"""把一个事务监听器适配为事务同步回调,在指定阶段触发。"""
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
callback: Callable,
|
|
77
|
+
event: Any,
|
|
78
|
+
phase: TransactionPhase,
|
|
79
|
+
order: int,
|
|
80
|
+
):
|
|
81
|
+
self._callback = callback
|
|
82
|
+
self._event = event
|
|
83
|
+
self._phase = phase
|
|
84
|
+
self._order = order
|
|
85
|
+
# AFTER_COMPLETION 阶段在 after_completion 中触发;其它阶段在对应方法触发。
|
|
86
|
+
self._fired = False
|
|
87
|
+
|
|
88
|
+
def _invoke(self) -> None:
|
|
89
|
+
if self._fired:
|
|
90
|
+
return
|
|
91
|
+
self._fired = True
|
|
92
|
+
result = self._callback(self._event)
|
|
93
|
+
if inspect.isawaitable(result):
|
|
94
|
+
self._finish_awaitable(result)
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _finish_awaitable(awaitable) -> None:
|
|
98
|
+
try:
|
|
99
|
+
loop = asyncio.get_running_loop()
|
|
100
|
+
except RuntimeError:
|
|
101
|
+
asyncio.run(awaitable)
|
|
102
|
+
else:
|
|
103
|
+
loop.create_task(awaitable)
|
|
104
|
+
|
|
105
|
+
def before_commit(self) -> None:
|
|
106
|
+
if self._phase == TransactionPhase.BEFORE_COMMIT:
|
|
107
|
+
self._invoke()
|
|
108
|
+
|
|
109
|
+
def after_commit(self) -> None:
|
|
110
|
+
if self._phase == TransactionPhase.AFTER_COMMIT:
|
|
111
|
+
self._invoke()
|
|
112
|
+
|
|
113
|
+
def after_rollback(self) -> None:
|
|
114
|
+
if self._phase == TransactionPhase.AFTER_ROLLBACK:
|
|
115
|
+
self._invoke()
|
|
116
|
+
|
|
117
|
+
def after_completion(self, status: str) -> None:
|
|
118
|
+
if self._phase == TransactionPhase.AFTER_COMPLETION:
|
|
119
|
+
self._invoke()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class TransactionalEventPublisher:
|
|
123
|
+
"""事务事件发布器:管理事务监听器并在事务阶段触发。
|
|
124
|
+
|
|
125
|
+
与 ``ApplicationEventPublisher`` 平行存在;可单独使用,也可由 ``ApplicationContext``
|
|
126
|
+
注册为 Bean 供 ``publish_event`` 统一委托。
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self):
|
|
130
|
+
self._listeners: List[_TxListenerEntry] = []
|
|
131
|
+
|
|
132
|
+
def add_listener(
|
|
133
|
+
self,
|
|
134
|
+
callback: Callable,
|
|
135
|
+
event_type: Optional[Type[ApplicationEvent]] = None,
|
|
136
|
+
phase: TransactionPhase = TransactionPhase.AFTER_COMMIT,
|
|
137
|
+
fallback_execution: bool = False,
|
|
138
|
+
order: int = 0,
|
|
139
|
+
) -> None:
|
|
140
|
+
self._listeners.append((event_type, callback, phase, fallback_execution, order))
|
|
141
|
+
|
|
142
|
+
def clear(self) -> None:
|
|
143
|
+
self._listeners.clear()
|
|
144
|
+
|
|
145
|
+
def listener_count(self) -> int:
|
|
146
|
+
return len(self._listeners)
|
|
147
|
+
|
|
148
|
+
def publish_event(self, event: Any) -> Any:
|
|
149
|
+
"""发布事件:匹配的事务监听器按事务阶段触发或回退立即执行。"""
|
|
150
|
+
if not isinstance(event, ApplicationEvent):
|
|
151
|
+
event = ApplicationEvent(source=event)
|
|
152
|
+
|
|
153
|
+
tx_active = TransactionSynchronizationManager.is_synchronization_active()
|
|
154
|
+
for event_type, callback, phase, fallback, _order in list(self._listeners):
|
|
155
|
+
if event_type is not None and not isinstance(event, event_type):
|
|
156
|
+
continue
|
|
157
|
+
if tx_active:
|
|
158
|
+
sync = _ListenerSynchronization(callback, event, phase, _order)
|
|
159
|
+
TransactionSynchronizationManager.register_synchronization(sync)
|
|
160
|
+
elif fallback:
|
|
161
|
+
# 无活动事务且允许回退执行:立即触发
|
|
162
|
+
result = callback(event)
|
|
163
|
+
if inspect.isawaitable(result):
|
|
164
|
+
_ListenerSynchronization._finish_awaitable(result)
|
|
165
|
+
# 否则丢弃(对齐 Spring 默认:无事务不执行)
|
|
166
|
+
return event
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
__all__ = [
|
|
170
|
+
"TransactionalEventListener",
|
|
171
|
+
"TransactionalEventPublisher",
|
|
172
|
+
]
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""事务同步管理器(对齐 Spring ``TransactionSynchronizationManager``)。
|
|
2
|
+
|
|
3
|
+
跟踪当前事务的同步回调(``TransactionSynchronization``),在事务生命周期各阶段触发:
|
|
4
|
+
``BEFORE_COMMIT`` / ``AFTER_COMMIT`` / ``AFTER_ROLLBACK`` / ``AFTER_COMPLETION``。
|
|
5
|
+
|
|
6
|
+
设计:
|
|
7
|
+
- **ContextVar 持有**:用 ``ContextVar`` 保存当前事务的同步列表与活跃标志,兼容 ``asyncio`` 协程,
|
|
8
|
+
对齐 Spring 的 ``ThreadLocal<List<TransactionSynchronization>>``。
|
|
9
|
+
- **最佳努力触发**:同步回调抛错时记录日志但不中断事务流程(与 Spring ``after_completion``
|
|
10
|
+
语义一致;``beforeCommit`` 抛错在 Spring 中会触发回滚,此处为安全起见统一记录,避免影响既有事务)。
|
|
11
|
+
- **集成点**:``@Transactional`` 切面(``bean_factory._wrap_transactional``)在事务边界调用
|
|
12
|
+
``init/clear`` 与各 ``trigger_*``;非受管场景可用 ``transaction_sync_scope`` 上下文管理器。
|
|
13
|
+
|
|
14
|
+
与 Java 的差异:
|
|
15
|
+
- Spring 用 ``ThreadLocal``;Python 用 ``ContextVar`` 兼容协程。
|
|
16
|
+
- 同步回调抛错统一记录不中断事务(Spring ``beforeCommit`` 抛错会回滚),已在模块文档标注。
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
from contextlib import contextmanager
|
|
22
|
+
from contextvars import ContextVar
|
|
23
|
+
from enum import Enum
|
|
24
|
+
from typing import Callable, List, Optional
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger("Spring.Tx.Synchronization")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TransactionPhase(Enum):
|
|
30
|
+
"""事务事件触发阶段(对齐 Spring ``TransactionPhase``)。"""
|
|
31
|
+
BEFORE_COMMIT = "BEFORE_COMMIT"
|
|
32
|
+
AFTER_COMMIT = "AFTER_COMMIT"
|
|
33
|
+
AFTER_ROLLBACK = "AFTER_ROLLBACK"
|
|
34
|
+
AFTER_COMPLETION = "AFTER_COMPLETION"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TransactionSynchronization:
|
|
38
|
+
"""事务同步回调接口(对齐 Spring ``TransactionSynchronization``)。
|
|
39
|
+
|
|
40
|
+
子类按需重写各阶段回调;默认无操作。``@TransactionalEventListener`` 通过本接口的
|
|
41
|
+
适配实现把监听器挂到指定阶段。
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def before_commit(self) -> None:
|
|
45
|
+
"""事务提交前调用(同一事务可多次刷新时,仅最终提交前调用一次)。"""
|
|
46
|
+
|
|
47
|
+
def after_commit(self) -> None:
|
|
48
|
+
"""事务成功提交后调用。"""
|
|
49
|
+
|
|
50
|
+
def after_rollback(self) -> None:
|
|
51
|
+
"""事务回滚后调用。"""
|
|
52
|
+
|
|
53
|
+
def after_completion(self, status: str) -> None:
|
|
54
|
+
"""事务完成后调用(``status`` 为 ``'commit'`` 或 ``'rollback'``)。"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# 当前事务的同步回调列表(None 表示当前无活动事务)
|
|
58
|
+
_synchronizations: ContextVar[Optional[List[TransactionSynchronization]]] = ContextVar(
|
|
59
|
+
"spring_tx_synchronizations", default=None
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TransactionSynchronizationManager:
|
|
64
|
+
"""事务同步管理器(静态方法风格,对齐 Spring 同名类)。"""
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def is_synchronization_active() -> bool:
|
|
68
|
+
return _synchronizations.get() is not None
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def init_synchronization() -> None:
|
|
72
|
+
"""开启一个新的事务同步上下文(``@Transactional`` 入口调用)。"""
|
|
73
|
+
_synchronizations.set([])
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def clear_synchronization() -> None:
|
|
77
|
+
"""清空当前事务同步上下文(``@Transactional`` 出口调用)。"""
|
|
78
|
+
_synchronizations.set(None)
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def get_synchronizations() -> List[TransactionSynchronization]:
|
|
82
|
+
"""返回当前事务已注册的同步回调列表(无活动事务返回空列表)。"""
|
|
83
|
+
syncs = _synchronizations.get()
|
|
84
|
+
return list(syncs) if syncs is not None else []
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def register_synchronization(sync: TransactionSynchronization) -> None:
|
|
88
|
+
"""注册一个同步回调到当前事务;无活动事务时抛错(对齐 Spring)。"""
|
|
89
|
+
syncs = _synchronizations.get()
|
|
90
|
+
if syncs is None:
|
|
91
|
+
raise RuntimeError(
|
|
92
|
+
"注册事务同步回调要求当前存在活动事务;"
|
|
93
|
+
"请在 @Transactional 方法内或 transaction_sync_scope 内调用"
|
|
94
|
+
)
|
|
95
|
+
syncs.append(sync)
|
|
96
|
+
|
|
97
|
+
# ==================== 阶段触发 ====================
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def trigger_before_commit() -> None:
|
|
101
|
+
TransactionSynchronizationManager._trigger(
|
|
102
|
+
"before_commit", lambda s: s.before_commit()
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def trigger_after_commit() -> None:
|
|
107
|
+
TransactionSynchronizationManager._trigger(
|
|
108
|
+
"after_commit", lambda s: s.after_commit()
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def trigger_after_rollback() -> None:
|
|
113
|
+
TransactionSynchronizationManager._trigger(
|
|
114
|
+
"after_rollback", lambda s: s.after_rollback()
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def trigger_after_completion(status: str) -> None:
|
|
119
|
+
TransactionSynchronizationManager._trigger(
|
|
120
|
+
"after_completion", lambda s: s.after_completion(status)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _trigger(phase: str, invoker: Callable[[TransactionSynchronization], None]) -> None:
|
|
125
|
+
"""最佳努力触发:逐个调用同步回调,单个抛错记录日志但不中断后续。"""
|
|
126
|
+
for sync in TransactionSynchronizationManager.get_synchronizations():
|
|
127
|
+
try:
|
|
128
|
+
invoker(sync)
|
|
129
|
+
except Exception: # pragma: no cover - 防御性,同步回调实现多样
|
|
130
|
+
logger.exception("事务同步回调 %s 执行失败", phase)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@contextmanager
|
|
134
|
+
def transaction_sync_scope():
|
|
135
|
+
"""非受管场景的事务同步上下文:进入 init,退出 clear(不触发任何阶段)。
|
|
136
|
+
|
|
137
|
+
供独立测试或手动管理事务事件边界使用。``@Transactional`` 切面内部会自行管理。
|
|
138
|
+
"""
|
|
139
|
+
TransactionSynchronizationManager.init_synchronization()
|
|
140
|
+
try:
|
|
141
|
+
yield TransactionSynchronizationManager
|
|
142
|
+
finally:
|
|
143
|
+
TransactionSynchronizationManager.clear_synchronization()
|
spring/utils/__init__.py
ADDED
spring/utils/banner.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from spring.utils.logger import SpringLogger
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BannerPrinter:
|
|
5
|
+
SPRING_BANNER = """
|
|
6
|
+
____ _ _ ____ _ _ ____ _ ____
|
|
7
|
+
/ ___| ___ ___| |_ ___| |__ / ___|| | | | / ___| / \\ | _ \\
|
|
8
|
+
\\___ \\ / _ \\ / __| __/ __| '_ \\ \\___ \\| |_| | \\___ \\ / _ \\ | |_) |
|
|
9
|
+
___) | (_) | (__| || (__| | | | ___) | _ | ___) / ___ \\| __/
|
|
10
|
+
|____/ \\___/ \\___|\\__\\___|_| |_| |____/|_| |_| |____/_/ \\_\\_|
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, version: str = "0.1.0"):
|
|
15
|
+
self.version = version
|
|
16
|
+
self.logger = SpringLogger()
|
|
17
|
+
|
|
18
|
+
def print_banner(self) -> None:
|
|
19
|
+
print(self.SPRING_BANNER)
|
|
20
|
+
print(f" Spring Framework {self.version} ".center(60, "="))
|
|
21
|
+
print()
|
|
22
|
+
|
|
23
|
+
def print_startup_info(self, port: int, context_path: str = "") -> None:
|
|
24
|
+
self.logger.info("Starting Spring application...")
|
|
25
|
+
self.logger.info(f"Server port: {port}")
|
|
26
|
+
self.logger.info(f"Context path: {context_path or '/'}")
|
|
27
|
+
self.logger.info("Application started successfully!")
|
|
28
|
+
print()
|
|
29
|
+
|
|
30
|
+
def print_shutdown_info(self) -> None:
|
|
31
|
+
self.logger.info("Shutting down Spring application...")
|
|
32
|
+
self.logger.info("Application stopped successfully!")
|
spring/utils/logger.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SpringLogger:
|
|
7
|
+
_instance: Optional['SpringLogger'] = None
|
|
8
|
+
|
|
9
|
+
def __new__(cls, *args, **kwargs):
|
|
10
|
+
if cls._instance is None:
|
|
11
|
+
cls._instance = super().__new__(cls)
|
|
12
|
+
cls._instance._initialized = False
|
|
13
|
+
return cls._instance
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
if getattr(self, '_initialized', False):
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
self._logger = logging.getLogger("Spring")
|
|
20
|
+
self._logger.setLevel(logging.INFO)
|
|
21
|
+
|
|
22
|
+
formatter = logging.Formatter(
|
|
23
|
+
"%(asctime)s [%(levelname)s] %(name)s - %(message)s",
|
|
24
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
28
|
+
console_handler.setFormatter(formatter)
|
|
29
|
+
console_handler.setLevel(logging.INFO)
|
|
30
|
+
|
|
31
|
+
self._logger.addHandler(console_handler)
|
|
32
|
+
self._initialized = True
|
|
33
|
+
|
|
34
|
+
def get_logger(self) -> logging.Logger:
|
|
35
|
+
return self._logger
|
|
36
|
+
|
|
37
|
+
def info(self, message: str) -> None:
|
|
38
|
+
self._logger.info(message)
|
|
39
|
+
|
|
40
|
+
def warn(self, message: str) -> None:
|
|
41
|
+
self._logger.warning(message)
|
|
42
|
+
|
|
43
|
+
def warning(self, message: str) -> None:
|
|
44
|
+
"""Expose the standard-library logging spelling used by framework code."""
|
|
45
|
+
self._logger.warning(message)
|
|
46
|
+
|
|
47
|
+
def error(self, message: str) -> None:
|
|
48
|
+
self._logger.error(message)
|
|
49
|
+
|
|
50
|
+
def debug(self, message: str) -> None:
|
|
51
|
+
self._logger.debug(message)
|
|
52
|
+
|
|
53
|
+
def trace(self, message: str) -> None:
|
|
54
|
+
self._logger.debug(message)
|
|
55
|
+
|
|
56
|
+
def set_level(self, level: int) -> None:
|
|
57
|
+
self._logger.setLevel(level)
|
|
58
|
+
for handler in self._logger.handlers:
|
|
59
|
+
handler.setLevel(level)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_logger(name: str = "Spring") -> logging.Logger:
|
|
63
|
+
logger = logging.getLogger(name)
|
|
64
|
+
if not logger.handlers:
|
|
65
|
+
logger.setLevel(logging.INFO)
|
|
66
|
+
formatter = logging.Formatter(
|
|
67
|
+
"%(asctime)s [%(levelname)s] %(name)s - %(message)s",
|
|
68
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
69
|
+
)
|
|
70
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
71
|
+
console_handler.setFormatter(formatter)
|
|
72
|
+
logger.addHandler(console_handler)
|
|
73
|
+
return logger
|