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.
Files changed (175) hide show
  1. spring/__init__.py +66 -0
  2. spring/ai/__init__.py +78 -0
  3. spring/ai/advisors.py +139 -0
  4. spring/ai/annotations.py +74 -0
  5. spring/ai/autoconfig.py +481 -0
  6. spring/ai/core.py +391 -0
  7. spring/ai/etl.py +188 -0
  8. spring/ai/memory.py +109 -0
  9. spring/ai/observability.py +129 -0
  10. spring/ai/providers.py +789 -0
  11. spring/ai/resilience.py +258 -0
  12. spring/ai/tools.py +106 -0
  13. spring/ai/vectorstore.py +303 -0
  14. spring/annotations/__init__.py +188 -0
  15. spring/annotations/cache.py +126 -0
  16. spring/annotations/cloud.py +207 -0
  17. spring/annotations/conditional.py +272 -0
  18. spring/annotations/core.py +864 -0
  19. spring/annotations/messaging.py +107 -0
  20. spring/aop/__init__.py +4 -0
  21. spring/aop/cloud_aop.py +404 -0
  22. spring/aop/comprehensive_aop.py +1015 -0
  23. spring/aop/method_interceptor.py +19 -0
  24. spring/aop/proxy_factory.py +55 -0
  25. spring/cloud/__init__.py +76 -0
  26. spring/cloud/discovery.py +364 -0
  27. spring/cloud/feign.py +469 -0
  28. spring/cloud/gateway.py +452 -0
  29. spring/cloud/load_balancer.py +149 -0
  30. spring/cloud/seata.py +557 -0
  31. spring/cloud/sentinel.py +525 -0
  32. spring/cloud/tracer.py +337 -0
  33. spring/config/__init__.py +21 -0
  34. spring/config/binding.py +206 -0
  35. spring/config/config_loader.py +405 -0
  36. spring/context/__init__.py +13 -0
  37. spring/context/application_context.py +589 -0
  38. spring/context/bean_definition.py +70 -0
  39. spring/context/bean_factory.py +1052 -0
  40. spring/context/registry.py +58 -0
  41. spring/context/scanner.py +106 -0
  42. spring/core/__init__.py +3 -0
  43. spring/core/graceful_shutdown.py +196 -0
  44. spring/core/typing_utils.py +50 -0
  45. spring/csv/__init__.py +52 -0
  46. spring/csv/annotations.py +402 -0
  47. spring/csv/converters.py +69 -0
  48. spring/csv/easy_csv.py +95 -0
  49. spring/csv/exceptions.py +27 -0
  50. spring/csv/reader.py +195 -0
  51. spring/csv/writer.py +155 -0
  52. spring/data/__init__.py +54 -0
  53. spring/data/page.py +181 -0
  54. spring/data/repository.py +274 -0
  55. spring/data/specification.py +228 -0
  56. spring/datasource/__init__.py +66 -0
  57. spring/datasource/annotations.py +133 -0
  58. spring/datasource/context.py +69 -0
  59. spring/datasource/dynamic.py +148 -0
  60. spring/event/__init__.py +7 -0
  61. spring/event/publisher.py +69 -0
  62. spring/excel/__init__.py +51 -0
  63. spring/excel/annotations.py +405 -0
  64. spring/excel/converters.py +231 -0
  65. spring/excel/easy_excel.py +94 -0
  66. spring/excel/exceptions.py +31 -0
  67. spring/excel/reader.py +254 -0
  68. spring/excel/style.py +95 -0
  69. spring/excel/writer.py +197 -0
  70. spring/i18n/__init__.py +97 -0
  71. spring/i18n/accessor.py +94 -0
  72. spring/i18n/auto_config.py +177 -0
  73. spring/i18n/holder.py +106 -0
  74. spring/i18n/locale.py +152 -0
  75. spring/i18n/locale_resolver.py +367 -0
  76. spring/i18n/message_source.py +250 -0
  77. spring/i18n/middleware.py +79 -0
  78. spring/i18n/properties.py +168 -0
  79. spring/i18n/sources.py +255 -0
  80. spring/logging/__init__.py +1 -0
  81. spring/logging/loguru_logger.py +228 -0
  82. spring/main.py +378 -0
  83. spring/messaging/__init__.py +1 -0
  84. spring/messaging/rabbitmq.py +302 -0
  85. spring/monitoring/__init__.py +1 -0
  86. spring/monitoring/prometheus.py +199 -0
  87. spring/orm/__init__.py +258 -0
  88. spring/orm/database.py +222 -0
  89. spring/orm/ddl_auto.py +1217 -0
  90. spring/orm/migration.py +419 -0
  91. spring/orm/mybatis_integration.py +400 -0
  92. spring/orm/pymybatis/__init__.py +86 -0
  93. spring/orm/pymybatis/annotations/__init__.py +30 -0
  94. spring/orm/pymybatis/annotations/annotations.py +332 -0
  95. spring/orm/pymybatis/cache/__init__.py +47 -0
  96. spring/orm/pymybatis/cache/cache.py +371 -0
  97. spring/orm/pymybatis/cache/redis_cache.py +434 -0
  98. spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
  99. spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
  100. spring/orm/pymybatis/configuration.py +525 -0
  101. spring/orm/pymybatis/core/__init__.py +10 -0
  102. spring/orm/pymybatis/core/sql_session.py +1382 -0
  103. spring/orm/pymybatis/core/sql_session_factory.py +76 -0
  104. spring/orm/pymybatis/dialect/__init__.py +9 -0
  105. spring/orm/pymybatis/dialect/dialect.py +445 -0
  106. spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
  107. spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
  108. spring/orm/pymybatis/interceptor/__init__.py +31 -0
  109. spring/orm/pymybatis/interceptor/interceptor.py +427 -0
  110. spring/orm/pymybatis/mapper/__init__.py +9 -0
  111. spring/orm/pymybatis/mapper/mapper.py +540 -0
  112. spring/orm/pymybatis/metrics/__init__.py +41 -0
  113. spring/orm/pymybatis/metrics/metrics.py +595 -0
  114. spring/orm/pymybatis/pool/__init__.py +9 -0
  115. spring/orm/pymybatis/pool/connection_pool.py +711 -0
  116. spring/orm/pymybatis/security/__init__.py +19 -0
  117. spring/orm/pymybatis/security/access_control.py +415 -0
  118. spring/orm/pymybatis/security/password_encoder.py +293 -0
  119. spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
  120. spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
  121. spring/orm/pymybatis/transaction/__init__.py +9 -0
  122. spring/orm/pymybatis/transaction/transaction.py +288 -0
  123. spring/orm/pymybatis/type_handler/__init__.py +37 -0
  124. spring/orm/pymybatis/type_handler/type_handler.py +473 -0
  125. spring/orm/pymybatis/version.py +9 -0
  126. spring/orm/pymybatis/xml_parser/__init__.py +9 -0
  127. spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
  128. spring/retry/__init__.py +12 -0
  129. spring/retry/retry_annotations.py +71 -0
  130. spring/retry/retry_decorator.py +155 -0
  131. spring/scheduling/__init__.py +3 -0
  132. spring/scheduling/scheduler.py +389 -0
  133. spring/security/__init__.py +39 -0
  134. spring/security/jwt_utils.py +281 -0
  135. spring/security/replay_protection.py +206 -0
  136. spring/security/secret_manager.py +226 -0
  137. spring/security/security_aop.py +248 -0
  138. spring/security/security_context.py +172 -0
  139. spring/test/__init__.py +45 -0
  140. spring/test/slicing.py +341 -0
  141. spring/tracing/__init__.py +11 -0
  142. spring/tracing/skywalking.py +229 -0
  143. spring/tx/__init__.py +52 -0
  144. spring/tx/events.py +172 -0
  145. spring/tx/synchronization.py +143 -0
  146. spring/utils/__init__.py +5 -0
  147. spring/utils/banner.py +32 -0
  148. spring/utils/logger.py +73 -0
  149. spring/utils/redis_client.py +526 -0
  150. spring/validation/__init__.py +55 -0
  151. spring/validation/aop.py +141 -0
  152. spring/validation/constraints.py +357 -0
  153. spring/validation/exceptions.py +55 -0
  154. spring/validation/validator.py +139 -0
  155. spring/web/__init__.py +12 -0
  156. spring/web/actuator.py +319 -0
  157. spring/web/exception_handler.py +61 -0
  158. spring/web/health.py +399 -0
  159. spring/web/interceptor.py +91 -0
  160. spring/web/result.py +44 -0
  161. spring/web/swagger.py +601 -0
  162. spring/web/web_context.py +755 -0
  163. spring/websocket/__init__.py +86 -0
  164. spring/websocket/annotations.py +169 -0
  165. spring/websocket/broker.py +238 -0
  166. spring/websocket/exceptions.py +26 -0
  167. spring/websocket/handler.py +243 -0
  168. spring/websocket/router.py +526 -0
  169. spring/websocket/session.py +216 -0
  170. springbootai-1.8.0.dist-info/METADATA +2796 -0
  171. springbootai-1.8.0.dist-info/RECORD +175 -0
  172. springbootai-1.8.0.dist-info/WHEEL +5 -0
  173. springbootai-1.8.0.dist-info/entry_points.txt +2 -0
  174. springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
  175. springbootai-1.8.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1052 @@
1
+ from typing import Type, Optional, Any, Dict, Callable, List, get_args, get_origin, Union
2
+ from spring.context.bean_definition import BeanDefinition
3
+ from spring.annotations.core import Autowired, Qualifier, Slf4j, PostConstruct, PreDestroy, Primary, Transactional, Cacheable, Retryable, Async, Value
4
+ from spring.annotations.cache import CachePut, CacheEvict, CacheConfig, Caching
5
+ from spring.aop.proxy_factory import ProxyFactory
6
+ import inspect
7
+ import time
8
+ import asyncio
9
+ import functools
10
+ import threading
11
+ import hashlib
12
+ import pickle
13
+ import concurrent.futures
14
+ import types
15
+
16
+
17
+ # 全局线程池,用于@Async注解的异步执行
18
+ _ASYNC_EXECUTOR = concurrent.futures.ThreadPoolExecutor(
19
+ max_workers=8,
20
+ thread_name_prefix="SpringAsync-"
21
+ )
22
+
23
+
24
+ def _get_tx_sync_manager():
25
+ """最佳努力获取事务同步管理器;``spring.tx`` 不可用时返回 ``None``。
26
+
27
+ ``@Transactional`` 切面在事务边界触发 ``@TransactionalEventListener`` 回调;
28
+ 若 ``spring.tx`` 未安装则回退到原始事务行为,保持向后兼容。
29
+ """
30
+ try:
31
+ from spring.tx.synchronization import TransactionSynchronizationManager
32
+ return TransactionSynchronizationManager
33
+ except ImportError: # pragma: no cover - spring.tx 为内置模块,仅在异常拆分时缺失
34
+ return None
35
+
36
+
37
+ class BeanFactory:
38
+ def __init__(self, config_loader=None):
39
+ self._bean_definitions: Dict[str, BeanDefinition] = {}
40
+ self._bean_instances: Dict[str, Any] = {}
41
+ self._type_to_name: Dict[Type, str] = {}
42
+ # 使用线程本地存储,确保每个线程有独立的状态
43
+ self._thread_local = threading.local()
44
+ self._proxy_factory = ProxyFactory()
45
+ # 缓存支持大小限制和TTL
46
+ self._cache: Dict[str, Any] = {}
47
+ self._cache_metadata: Dict[str, dict] = {} # 存储缓存项的元数据(创建时间、过期时间)
48
+ self._cache_max_size = 1000 # 默认最大缓存数
49
+ self._cache_default_ttl = 300 # 默认TTL(秒)
50
+ self._lock = threading.RLock()
51
+ self._config_loader = config_loader
52
+
53
+ def _get_initializing(self) -> set:
54
+ """获取当前线程的 initializing 集合"""
55
+ if not hasattr(self._thread_local, 'initializing'):
56
+ self._thread_local.initializing = set()
57
+ return self._thread_local.initializing
58
+
59
+ def _get_transaction_stack(self) -> List[Dict[str, Any]]:
60
+ """获取当前线程的 transaction_stack"""
61
+ if not hasattr(self._thread_local, 'transaction_stack'):
62
+ self._thread_local.transaction_stack = []
63
+ return self._thread_local.transaction_stack
64
+
65
+ def set_config_loader(self, config_loader):
66
+ self._config_loader = config_loader
67
+
68
+ def register_bean_definition(self, bean_name: str, definition: BeanDefinition) -> None:
69
+ self._bean_definitions[bean_name] = definition
70
+ if definition.bean_class not in self._type_to_name:
71
+ self._type_to_name[definition.bean_class] = bean_name
72
+
73
+ def register_instance(self, bean_name: str, instance: Any) -> None:
74
+ """
75
+ 直接注册一个已实例化的Bean
76
+
77
+ Args:
78
+ bean_name: Bean名称
79
+ instance: Bean实例
80
+ """
81
+ self._bean_instances[bean_name] = instance
82
+ bean_class = instance.__class__
83
+ if bean_class not in self._type_to_name:
84
+ self._type_to_name[bean_class] = bean_name
85
+
86
+ def get_bean_definition(self, bean_name: str) -> Optional[BeanDefinition]:
87
+ return self._bean_definitions.get(bean_name)
88
+
89
+ def get_bean(self, bean_name: str) -> Any:
90
+ definition = self.get_bean_definition(bean_name)
91
+ if not definition:
92
+ raise ValueError(f"No bean named '{bean_name}' found")
93
+
94
+ if definition.is_singleton:
95
+ if bean_name not in self._bean_instances:
96
+ with self._lock:
97
+ if bean_name not in self._bean_instances:
98
+ self._bean_instances[bean_name] = self._create_bean(definition)
99
+ return self._bean_instances[bean_name]
100
+ else:
101
+ return self._create_bean(definition)
102
+
103
+ def get_bean_by_type(self, bean_type: Type) -> Any:
104
+ if not isinstance(bean_type, type):
105
+ # ``Optional[T]`` and ``Annotated[T, ...]`` are typing objects,
106
+ # not valid arguments to issubclass().
107
+ bean_type, _, _ = self._unwrap_dependency_annotation(bean_type)
108
+ if not isinstance(bean_type, type):
109
+ raise ValueError(f"Bean 类型不可解析: {bean_type!r}")
110
+ if bean_type in self._type_to_name:
111
+ return self.get_bean(self._type_to_name[bean_type])
112
+
113
+ matching_definitions = []
114
+ for name, definition in self._bean_definitions.items():
115
+ if isinstance(definition.bean_class, type) and issubclass(definition.bean_class, bean_type):
116
+ matching_definitions.append((name, definition))
117
+
118
+ if not matching_definitions:
119
+ raise ValueError(f"No bean of type '{bean_type.__name__}' found")
120
+
121
+ if len(matching_definitions) == 1:
122
+ return self.get_bean(matching_definitions[0][0])
123
+
124
+ primary_definitions = [md for md in matching_definitions
125
+ if Primary._annotation_type in md[1].annotations]
126
+
127
+ if primary_definitions:
128
+ return self.get_bean(primary_definitions[0][0])
129
+
130
+ raise ValueError(f"Multiple beans of type '{bean_type.__name__}' found, use @Qualifier to specify")
131
+
132
+ def _create_bean(self, definition: BeanDefinition) -> Any:
133
+ initializing = self._get_initializing()
134
+ if definition.bean_name in initializing:
135
+ raise RuntimeError(f"Circular dependency detected for bean: {definition.bean_name}")
136
+
137
+ initializing.add(definition.bean_name)
138
+
139
+ try:
140
+ if definition.factory_method:
141
+ factory_instance = None
142
+ if definition.factory_class:
143
+ factory_instance = self.get_bean_by_type(definition.factory_class)
144
+ # 如果有factory_class,传递factory_instance;否则直接调用
145
+ if definition.factory_class:
146
+ instance = definition.factory_method(factory_instance)
147
+ else:
148
+ instance = definition.factory_method()
149
+ else:
150
+ instance = self._instantiate_bean(definition)
151
+
152
+ self._apply_aop_proxy(instance, definition)
153
+ self._populate_bean(definition, instance)
154
+ self._populate_config_values(definition, instance)
155
+ self._process_slf4j(instance, definition)
156
+ self._initialize_bean(definition, instance)
157
+
158
+ return instance
159
+ finally:
160
+ initializing.discard(definition.bean_name)
161
+
162
+ def _populate_config_values(self, definition: BeanDefinition, instance: Any) -> None:
163
+ if self._config_loader is None:
164
+ return
165
+
166
+ for name, value in vars(instance.__class__).items():
167
+ if isinstance(value, Value):
168
+ setattr(instance, name, self._resolve_value(value))
169
+
170
+ # Keep NacosValue optional: the framework can bind a local
171
+ # configuration snapshot even when the Nacos SDK is not installed.
172
+ try:
173
+ from spring.annotations.cloud import NacosValue
174
+ except ImportError:
175
+ NacosValue = ()
176
+ if NacosValue and isinstance(value, NacosValue):
177
+ setattr(instance, name, self._resolve_value(value))
178
+
179
+ property_annotations = definition.annotations.get('properties', [])
180
+ if property_annotations:
181
+ config = self._config_loader.get_prefix_config(
182
+ property_annotations[0].prefix
183
+ )
184
+ for key, value in config.items():
185
+ attribute = key.replace('-', '_')
186
+ if hasattr(instance, attribute):
187
+ setattr(instance, attribute, value)
188
+
189
+ def _resolve_value(self, annotation: Value) -> Any:
190
+ return self._config_loader.resolve_value_expression(
191
+ annotation.value,
192
+ getattr(annotation, 'default', None),
193
+ )
194
+
195
+ def _instantiate_bean(self, definition: BeanDefinition) -> Any:
196
+ constructor = self._find_constructor_with_autowire(definition.bean_class)
197
+ if constructor:
198
+ args = self._resolve_constructor_args(constructor, definition)
199
+ return definition.bean_class(*args)
200
+ return definition.bean_class()
201
+
202
+ def _find_constructor_with_autowire(self, bean_class: Type) -> Optional[Callable]:
203
+ for name, method in inspect.getmembers(bean_class):
204
+ if name == "__init__":
205
+ annotations = getattr(method, '__spring_annotations__', [])
206
+ for annotation in annotations:
207
+ if isinstance(annotation, Autowired):
208
+ return method
209
+ return None
210
+
211
+ def _resolve_constructor_args(self, constructor: Callable, definition: BeanDefinition) -> list:
212
+ sig = inspect.signature(constructor)
213
+ args = []
214
+ autowired = next(
215
+ (
216
+ annotation for annotation in getattr(constructor, '__spring_annotations__', [])
217
+ if isinstance(annotation, Autowired)
218
+ ),
219
+ Autowired(),
220
+ )
221
+ for param_name, param in sig.parameters.items():
222
+ if param_name == 'self':
223
+ continue
224
+
225
+ # 检查参数默认值是否是@Value注解
226
+ if isinstance(param.default, Value):
227
+ args.append(self._resolve_value(param.default))
228
+ continue
229
+
230
+ try:
231
+ from spring.annotations.cloud import NacosValue
232
+ except ImportError:
233
+ NacosValue = ()
234
+ if NacosValue and isinstance(param.default, NacosValue):
235
+ args.append(self._resolve_value(param.default))
236
+ continue
237
+
238
+ param_type, inline_qualifier, optional_type = self._unwrap_dependency_annotation(param.annotation)
239
+ if param_type is inspect.Parameter.empty:
240
+ if param.default is not inspect.Parameter.empty:
241
+ args.append(param.default)
242
+ continue
243
+ # 尝试通过参数名查找 Bean
244
+ try:
245
+ args.append(self.get_bean(param_name))
246
+ continue
247
+ except (KeyError, ValueError):
248
+ pass
249
+ if not autowired.required:
250
+ args.append(None)
251
+ continue
252
+ raise ValueError(f"Cannot resolve parameter '{param_name}' without type annotation")
253
+
254
+ qualifier = inline_qualifier or definition.qualifiers.get(param_name)
255
+ if qualifier:
256
+ try:
257
+ args.append(self.get_bean(qualifier))
258
+ except ValueError:
259
+ if not autowired.required or optional_type:
260
+ args.append(None)
261
+ else:
262
+ raise ValueError(f"Cannot resolve parameter '{param_name}'")
263
+ else:
264
+ try:
265
+ args.append(self.get_bean_by_type(param_type))
266
+ except ValueError:
267
+ # 如果通过类型找不到,尝试通过参数名查找
268
+ try:
269
+ args.append(self.get_bean(param_name))
270
+ except (KeyError, ValueError):
271
+ if not autowired.required or optional_type:
272
+ args.append(None)
273
+ continue
274
+ raise ValueError(f"Cannot resolve parameter '{param_name}'")
275
+ return args
276
+
277
+ @staticmethod
278
+ def _unwrap_dependency_annotation(annotation: Any):
279
+ """Extract ``Annotated[T, Qualifier(...)]`` and ``Optional[T]`` metadata."""
280
+ qualifier = None
281
+ optional_type = False
282
+ if get_origin(annotation) is not None and str(get_origin(annotation)).endswith('Annotated'):
283
+ args = get_args(annotation)
284
+ annotation = args[0]
285
+ for metadata in args[1:]:
286
+ if isinstance(metadata, Qualifier):
287
+ qualifier = metadata.value
288
+ origin = get_origin(annotation)
289
+ if origin is Union or str(origin) == 'types.UnionType':
290
+ candidates = [candidate for candidate in get_args(annotation) if candidate is not type(None)]
291
+ optional_type = len(candidates) != len(get_args(annotation))
292
+ if len(candidates) == 1:
293
+ annotation = candidates[0]
294
+ return annotation, qualifier, optional_type
295
+
296
+ def _populate_bean(self, definition: BeanDefinition, instance: Any) -> None:
297
+ for field_name, field_type in definition.dependencies.items():
298
+ qualifier = definition.qualifiers.get(field_name)
299
+ if qualifier:
300
+ dependency = self.get_bean(qualifier)
301
+ else:
302
+ try:
303
+ dependency = self.get_bean_by_type(field_type)
304
+ except ValueError:
305
+ # 如果通过类型找不到,尝试通过字段名查找
306
+ try:
307
+ dependency = self.get_bean(field_name)
308
+ except (KeyError, ValueError):
309
+ if not definition.dependency_required.get(field_name, True):
310
+ setattr(instance, field_name, None)
311
+ continue
312
+ raise ValueError(f"Cannot resolve field '{field_name}'")
313
+ setattr(instance, field_name, dependency)
314
+
315
+ for name, field in inspect.getmembers(instance.__class__):
316
+ if not name.startswith('_') and hasattr(field, '__spring_annotations__'):
317
+ annotations = field.__spring_annotations__
318
+ for annotation in annotations:
319
+ if isinstance(annotation, Autowired):
320
+ field_type = inspect.get_annotations(instance.__class__).get(name)
321
+ if field_type:
322
+ qualifier_annotation = None
323
+ for ann in annotations:
324
+ if isinstance(ann, Qualifier):
325
+ qualifier_annotation = ann
326
+ break
327
+ if qualifier_annotation:
328
+ dependency = self.get_bean(qualifier_annotation.value)
329
+ else:
330
+ dependency = self.get_bean_by_type(field_type)
331
+ setattr(instance, name, dependency)
332
+
333
+ def _process_slf4j(self, instance: Any, definition: BeanDefinition) -> None:
334
+ if Slf4j._annotation_type in definition.annotations:
335
+ from spring.utils.logger import get_logger
336
+ annotations = definition.annotations[Slf4j._annotation_type]
337
+ if annotations:
338
+ annotation = annotations[0]
339
+ logger_name = annotation.logger_name or instance.__class__.__name__
340
+ logger = get_logger(logger_name)
341
+ setattr(instance, 'logger', logger)
342
+
343
+ def _initialize_bean(self, definition: BeanDefinition, instance: Any) -> None:
344
+ if definition.init_method and hasattr(instance, definition.init_method):
345
+ init_method = getattr(instance, definition.init_method)
346
+ if callable(init_method):
347
+ init_method()
348
+
349
+ if hasattr(instance, 'init') and callable(instance.init):
350
+ instance.init()
351
+
352
+ self._register_rabbit_listeners(instance)
353
+ self._invoke_post_construct(instance)
354
+
355
+ definition.mark_initialized()
356
+
357
+ def _register_rabbit_listeners(self, instance: Any) -> None:
358
+ """Register annotated bound methods after all AOP wrappers are installed."""
359
+ if getattr(instance, '__rabbit_listeners_registered__', False):
360
+ return
361
+ if self._config_loader is not None:
362
+ if not self._config_loader.get_value('rabbitmq.enabled', False):
363
+ return
364
+ try:
365
+ from spring.messaging.rabbitmq import rabbitmq_client
366
+ except ImportError:
367
+ return
368
+ connection = rabbitmq_client._connection
369
+ if connection is None or getattr(connection, 'is_closed', False):
370
+ return
371
+ try:
372
+ from spring.annotations.messaging import RabbitListener, register_rabbit_listener
373
+ except ImportError:
374
+ return
375
+
376
+ registered = False
377
+ for name, method in inspect.getmembers(instance.__class__):
378
+ if name.startswith('_') or not inspect.isfunction(method):
379
+ continue
380
+ for annotation in getattr(method, '__spring_annotations__', []):
381
+ if isinstance(annotation, RabbitListener):
382
+ register_rabbit_listener(annotation, getattr(instance, name))
383
+ registered = True
384
+ if registered:
385
+ setattr(instance, '__rabbit_listeners_registered__', True)
386
+
387
+ def _invoke_post_construct(self, instance: Any) -> None:
388
+ for name, method in inspect.getmembers(instance.__class__):
389
+ if not name.startswith('_') and inspect.isfunction(method):
390
+ annotations = getattr(method, '__spring_annotations__', [])
391
+ for annotation in annotations:
392
+ if isinstance(annotation, PostConstruct):
393
+ method(instance)
394
+
395
+ def destroy_bean(self, bean_name: str) -> None:
396
+ definition = self.get_bean_definition(bean_name)
397
+ if not definition or definition._destroyed:
398
+ return
399
+
400
+ if bean_name in self._bean_instances:
401
+ instance = self._bean_instances[bean_name]
402
+
403
+ self._invoke_pre_destroy(instance)
404
+
405
+ if definition.destroy_method and hasattr(instance, definition.destroy_method):
406
+ destroy_method = getattr(instance, definition.destroy_method)
407
+ if callable(destroy_method):
408
+ destroy_method()
409
+
410
+ if hasattr(instance, 'destroy') and callable(instance.destroy):
411
+ instance.destroy()
412
+
413
+ definition.mark_destroyed()
414
+ del self._bean_instances[bean_name]
415
+
416
+ def _invoke_pre_destroy(self, instance: Any) -> None:
417
+ for name, method in inspect.getmembers(instance.__class__):
418
+ if not name.startswith('_') and inspect.isfunction(method):
419
+ annotations = getattr(method, '__spring_annotations__', [])
420
+ for annotation in annotations:
421
+ if isinstance(annotation, PreDestroy):
422
+ method(instance)
423
+
424
+ def destroy_all(self) -> None:
425
+ for bean_name in list(self._bean_instances.keys()):
426
+ self.destroy_bean(bean_name)
427
+
428
+ def contains_bean(self, bean_name: str) -> bool:
429
+ return bean_name in self._bean_definitions
430
+
431
+ def get_bean_names(self) -> list:
432
+ return list(self._bean_definitions.keys())
433
+
434
+ def get_bean_count(self) -> int:
435
+ return len(self._bean_definitions)
436
+
437
+ def _apply_aop_proxy(self, instance: Any, definition: BeanDefinition) -> None:
438
+ bean_class = instance.__class__
439
+ from spring.utils.logger import SpringLogger
440
+ logger = SpringLogger()
441
+
442
+ logger.info(f"Applying AOP proxy to bean: {definition.bean_name}, instance: {id(instance)}")
443
+
444
+ for name, method in inspect.getmembers(bean_class):
445
+ if not name.startswith('_') and inspect.isfunction(method):
446
+ annotations = getattr(method, '__spring_annotations__', [])
447
+
448
+ if annotations:
449
+ logger.info(f" Found method {name} with annotations: {[type(a).__name__ for a in annotations]}")
450
+
451
+ # 处理新注解(使用 comprehensive_aop)
452
+ try:
453
+ from spring.aop.comprehensive_aop import apply_annotations
454
+ wrapped_method = apply_annotations(instance, method)
455
+ if wrapped_method is not method:
456
+ logger.info(f" Method {name} wrapped successfully")
457
+ method = wrapped_method
458
+ except ImportError as e:
459
+ logger.error(f" Failed to import comprehensive_aop: {e}")
460
+
461
+ # 处理 Cloud 注解(使用 cloud_aop)
462
+ try:
463
+ from spring.aop.cloud_aop import apply_cloud_annotations
464
+ wrapped_method = apply_cloud_annotations(instance, method)
465
+ if wrapped_method is not method:
466
+ logger.info(f" Method {name} wrapped with Cloud AOP successfully")
467
+ method = wrapped_method
468
+ except ImportError as e:
469
+ logger.error(f" Failed to import cloud_aop: {e}")
470
+
471
+ # 固定包装顺序:事务在计算内部,缓存命中可跳过事务,异步最外层调度。
472
+ for annotation in annotations:
473
+ if isinstance(annotation, Transactional):
474
+ method = self._wrap_transactional(instance, method, annotation)
475
+ for annotation in annotations:
476
+ if isinstance(annotation, Cacheable):
477
+ method = self._wrap_cacheable(instance, method, annotation)
478
+ # 缓存增强:@CachePut / @CacheEvict / @Caching(复用 @Cacheable 同一存储)
479
+ for annotation in annotations:
480
+ if isinstance(annotation, CachePut):
481
+ method = self._wrap_cache_put(instance, method, annotation)
482
+ for annotation in annotations:
483
+ if isinstance(annotation, CacheEvict):
484
+ method = self._wrap_cache_evict(instance, method, annotation)
485
+ for annotation in annotations:
486
+ if isinstance(annotation, Caching):
487
+ method = self._wrap_caching(instance, method, annotation)
488
+ for annotation in annotations:
489
+ if isinstance(annotation, Async):
490
+ method = self._wrap_async(instance, method, annotation)
491
+
492
+ # Security stays outermost: it consumes the internal request
493
+ # argument and authenticates before transaction/business logic.
494
+ try:
495
+ from spring.security.security_aop import apply_security_annotations
496
+ method = apply_security_annotations(instance, method)
497
+ except ImportError as e:
498
+ logger.error(f" Failed to import security_aop: {e}")
499
+
500
+ # 创建绑定方法并设置到实例
501
+ bound_method = types.MethodType(method, instance)
502
+ setattr(instance, name, bound_method)
503
+ logger.info(f" Method {name} bound to instance: {id(instance)}")
504
+
505
+ def _wrap_transactional(self, instance: Any, method: Callable, annotation: Transactional) -> Callable:
506
+ def get_session_factory():
507
+ try:
508
+ return self.get_bean('sqlSessionFactory')
509
+ except Exception as exc:
510
+ raise RuntimeError(
511
+ "@Transactional需要已启用的MyBatis SqlSessionFactory"
512
+ ) from exc
513
+
514
+ def should_rollback(exc: Exception) -> bool:
515
+ if annotation.no_rollback_for and any(
516
+ isinstance(exc, exc_type) for exc_type in annotation.no_rollback_for
517
+ ):
518
+ return False
519
+ if annotation.rollback_for and not any(
520
+ isinstance(exc, exc_type) for exc_type in annotation.rollback_for
521
+ ):
522
+ return False
523
+ return True
524
+
525
+ if asyncio.iscoroutinefunction(method):
526
+ @functools.wraps(method)
527
+ async def async_wrapper(*args, **kwargs):
528
+ from spring.orm.mybatis_integration import mybatis_transaction
529
+ tx_sync = _get_tx_sync_manager()
530
+
531
+ owns_sync = (
532
+ tx_sync is not None
533
+ and not tx_sync.is_synchronization_active()
534
+ )
535
+ if owns_sync:
536
+ tx_sync.init_synchronization()
537
+
538
+ deferred_exception = None
539
+ deferred_traceback = None
540
+ committed = False
541
+ try:
542
+ with mybatis_transaction(
543
+ get_session_factory(), str(annotation.propagation).upper()
544
+ ):
545
+ try:
546
+ result = await method(*args, **kwargs)
547
+ except Exception as exc:
548
+ if should_rollback(exc):
549
+ raise
550
+ deferred_exception = exc
551
+ deferred_traceback = exc.__traceback__
552
+ result = None
553
+ if owns_sync:
554
+ tx_sync.trigger_before_commit()
555
+ committed = True
556
+ if owns_sync:
557
+ tx_sync.trigger_after_commit()
558
+ if deferred_exception is not None:
559
+ raise deferred_exception.with_traceback(deferred_traceback)
560
+ return result
561
+ except Exception:
562
+ if owns_sync and not committed:
563
+ tx_sync.trigger_after_rollback()
564
+ raise
565
+ finally:
566
+ if owns_sync:
567
+ tx_sync.trigger_after_completion(
568
+ 'commit' if committed else 'rollback'
569
+ )
570
+ tx_sync.clear_synchronization()
571
+
572
+ return async_wrapper
573
+
574
+ @functools.wraps(method)
575
+ def wrapper(*args, **kwargs):
576
+ from spring.orm.mybatis_integration import mybatis_transaction
577
+ tx_sync = _get_tx_sync_manager()
578
+
579
+ owns_sync = (
580
+ tx_sync is not None
581
+ and not tx_sync.is_synchronization_active()
582
+ )
583
+ if owns_sync:
584
+ tx_sync.init_synchronization()
585
+
586
+ deferred_exception = None
587
+ deferred_traceback = None
588
+ committed = False
589
+ try:
590
+ with mybatis_transaction(
591
+ get_session_factory(), str(annotation.propagation).upper()
592
+ ):
593
+ try:
594
+ result = method(*args, **kwargs)
595
+ except Exception as exc:
596
+ if should_rollback(exc):
597
+ raise
598
+ deferred_exception = exc
599
+ deferred_traceback = exc.__traceback__
600
+ result = None
601
+ # 成功路径(含 no-rollback 异常):提交前触发 BEFORE_COMMIT
602
+ if owns_sync:
603
+ tx_sync.trigger_before_commit()
604
+ committed = True
605
+ if owns_sync:
606
+ tx_sync.trigger_after_commit()
607
+ if deferred_exception is not None:
608
+ raise deferred_exception.with_traceback(deferred_traceback)
609
+ return result
610
+ except Exception:
611
+ if owns_sync and not committed:
612
+ tx_sync.trigger_after_rollback()
613
+ raise
614
+ finally:
615
+ if owns_sync:
616
+ tx_sync.trigger_after_completion(
617
+ 'commit' if committed else 'rollback'
618
+ )
619
+ tx_sync.clear_synchronization()
620
+
621
+ return wrapper
622
+
623
+ def _wrap_cacheable(self, instance: Any, method: Callable, annotation: Cacheable) -> Callable:
624
+ signature = inspect.signature(method)
625
+
626
+ def serialize_arg(arg: Any) -> str:
627
+ if isinstance(arg, (int, float, str, bool, type(None))):
628
+ return str(arg)
629
+ if isinstance(arg, (list, tuple)):
630
+ return '[' + ','.join(serialize_arg(item) for item in arg) + ']'
631
+ if isinstance(arg, dict):
632
+ items = sorted(
633
+ ((serialize_arg(key), serialize_arg(value)) for key, value in arg.items()),
634
+ key=lambda item: item[0],
635
+ )
636
+ return '{' + ','.join(f"{key}:{value}" for key, value in items) + '}'
637
+ return f"obj_{id(arg)}"
638
+
639
+ def resolve_call(args, kwargs):
640
+ bound = signature.bind_partial(*args, **kwargs)
641
+ bound.apply_defaults()
642
+ cache_arguments = dict(bound.arguments)
643
+ cache_arguments.pop('self', None)
644
+
645
+ condition = annotation.condition
646
+ if condition:
647
+ if callable(condition):
648
+ enabled = bool(condition(**cache_arguments))
649
+ else:
650
+ condition_name = str(condition).strip()
651
+ negate = condition_name.startswith('!')
652
+ if negate:
653
+ condition_name = condition_name[1:]
654
+ if condition_name not in cache_arguments:
655
+ raise ValueError(
656
+ f"@Cacheable condition只支持参数名,未找到: {condition_name}"
657
+ )
658
+ enabled = bool(cache_arguments[condition_name])
659
+ if negate:
660
+ enabled = not enabled
661
+ if not enabled:
662
+ return False, None
663
+
664
+ if annotation.key:
665
+ if '{' in annotation.key:
666
+ try:
667
+ resolved_key = annotation.key.format(**cache_arguments)
668
+ except KeyError as exc:
669
+ raise ValueError(
670
+ f"@Cacheable key引用了不存在的参数: {exc.args[0]}"
671
+ ) from exc
672
+ elif annotation.key in cache_arguments:
673
+ resolved_key = serialize_arg(cache_arguments[annotation.key])
674
+ else:
675
+ resolved_key = annotation.key
676
+ # key = cacheName + resolvedKey(对齐 Spring Cache:不含方法名,
677
+ # 使 @CachePut / @CacheEvict 可跨方法更新/失效 @Cacheable 条目)。
678
+ key_data = f"{annotation.value}:{resolved_key}"
679
+ else:
680
+ arguments = ','.join(
681
+ f"{name}:{serialize_arg(value)}"
682
+ for name, value in sorted(cache_arguments.items())
683
+ )
684
+ key_data = f"{annotation.value}:{arguments}"
685
+ return True, hashlib.sha256(key_data.encode('utf-8')).hexdigest()
686
+
687
+ def get_cached(cache_key):
688
+ with self._lock:
689
+ current_time = time.time()
690
+ metadata = self._cache_metadata.get(cache_key)
691
+ if metadata is None or cache_key not in self._cache:
692
+ return False, None
693
+ if current_time > metadata.get('expire_time', current_time):
694
+ self._cache.pop(cache_key, None)
695
+ self._cache_metadata.pop(cache_key, None)
696
+ return False, None
697
+ return True, self._cache[cache_key]
698
+
699
+ def store(cache_key, result):
700
+ current_time = time.time()
701
+ with self._lock:
702
+ if len(self._cache) >= self._cache_max_size:
703
+ oldest_key = min(
704
+ self._cache_metadata,
705
+ key=lambda key: self._cache_metadata[key].get('create_time', 0),
706
+ )
707
+ self._cache.pop(oldest_key, None)
708
+ self._cache_metadata.pop(oldest_key, None)
709
+ self._cache[cache_key] = result
710
+ self._cache_metadata[cache_key] = {
711
+ 'create_time': current_time,
712
+ 'expire_time': current_time + self._cache_default_ttl,
713
+ # 登记 namespace,供 @CacheEvict(all_entries=True) 按命名空间清空
714
+ 'namespace': annotation.value,
715
+ }
716
+
717
+ if asyncio.iscoroutinefunction(method):
718
+ @functools.wraps(method)
719
+ async def async_wrapper(*args, **kwargs):
720
+ enabled, cache_key = resolve_call(args, kwargs)
721
+ if not enabled:
722
+ return await method(*args, **kwargs)
723
+ with self._lock:
724
+ exists = cache_key in self._cache_metadata
725
+ if exists:
726
+ hit, cached = get_cached(cache_key)
727
+ if hit:
728
+ return cached
729
+ result = await method(*args, **kwargs)
730
+ store(cache_key, result)
731
+ return result
732
+
733
+ return async_wrapper
734
+
735
+ @functools.wraps(method)
736
+ def wrapper(*args, **kwargs):
737
+ enabled, cache_key = resolve_call(args, kwargs)
738
+ if not enabled:
739
+ return method(*args, **kwargs)
740
+ with self._lock:
741
+ exists = cache_key in self._cache_metadata
742
+ if exists:
743
+ hit, cached = get_cached(cache_key)
744
+ if hit:
745
+ return cached
746
+ result = method(*args, **kwargs)
747
+ store(cache_key, result)
748
+ return result
749
+
750
+ return wrapper
751
+
752
+ # ==================== 缓存增强:@CachePut / @CacheEvict / @Caching ====================
753
+ # 复用 @Cacheable 同一存储(self._cache / self._cache_metadata / self._lock / TTL)。
754
+ # key 解析逻辑与 _wrap_cacheable 一致(参数名/{param}模板/全参数聚合 + condition),
755
+ # 为保证既有 @Cacheable 行为零回归,本组方法独立实现解析,未改动 _wrap_cacheable。
756
+
757
+ def _resolve_cache_value(self, instance: Any, annotation_value: str) -> str:
758
+ """解析缓存命名空间:注解 value 为空时回退到类级 ``@CacheConfig`` 默认。"""
759
+ if annotation_value:
760
+ return annotation_value
761
+ # 读类级 @CacheConfig
762
+ from spring.annotations.core import get_spring_annotations
763
+ try:
764
+ for ann in get_spring_annotations(instance.__class__):
765
+ if isinstance(ann, CacheConfig) and ann.cache_names:
766
+ return ann.cache_names[0]
767
+ except Exception:
768
+ pass
769
+ return annotation_value
770
+
771
+ def _cache_serialize_arg(self, arg: Any) -> str:
772
+ """缓存参数序列化(与 _wrap_cacheable.serialize_arg 一致)。"""
773
+ if isinstance(arg, (int, float, str, bool, type(None))):
774
+ return str(arg)
775
+ if isinstance(arg, (list, tuple)):
776
+ return '[' + ','.join(self._cache_serialize_arg(item) for item in arg) + ']'
777
+ if isinstance(arg, dict):
778
+ items = sorted(
779
+ ((self._cache_serialize_arg(k), self._cache_serialize_arg(v)) for k, v in arg.items()),
780
+ key=lambda item: item[0],
781
+ )
782
+ return '{' + ','.join(f"{k}:{v}" for k, v in items) + '}'
783
+ return f"obj_{id(arg)}"
784
+
785
+ def _cache_resolve_call(self, method: Callable, annotation: Any, instance: Any, args, kwargs):
786
+ """解析一次缓存操作的 (enabled, cache_key, namespace)。返回 (False, None, None) 表示跳过。
787
+
788
+ 与 _wrap_cacheable.resolve_call 语义一致:condition 支持 参数名 / ``!参数名`` / callable。
789
+ 额外返回 namespace(解析后的 value),供 _cache_store 登记,便于 @CacheEvict(all_entries) 清空。
790
+ """
791
+ signature = inspect.signature(method)
792
+ bound = signature.bind_partial(*args, **kwargs)
793
+ bound.apply_defaults()
794
+ cache_arguments = dict(bound.arguments)
795
+ cache_arguments.pop('self', None)
796
+
797
+ condition = getattr(annotation, 'condition', None)
798
+ if condition:
799
+ if callable(condition):
800
+ enabled = bool(condition(**cache_arguments))
801
+ else:
802
+ condition_name = str(condition).strip()
803
+ negate = condition_name.startswith('!')
804
+ if negate:
805
+ condition_name = condition_name[1:]
806
+ if condition_name not in cache_arguments:
807
+ raise ValueError(
808
+ f"缓存 condition 只支持参数名,未找到: {condition_name}"
809
+ )
810
+ enabled = bool(cache_arguments[condition_name])
811
+ if negate:
812
+ enabled = not enabled
813
+ if not enabled:
814
+ return False, None, None
815
+
816
+ value = self._resolve_cache_value(instance, getattr(annotation, 'value', '') or '')
817
+ key_expr = getattr(annotation, 'key', None)
818
+ if key_expr:
819
+ if '{' in key_expr:
820
+ try:
821
+ resolved_key = key_expr.format(**cache_arguments)
822
+ except KeyError as exc:
823
+ raise ValueError(
824
+ f"缓存 key 引用了不存在的参数: {exc.args[0]}"
825
+ ) from exc
826
+ elif key_expr in cache_arguments:
827
+ resolved_key = self._cache_serialize_arg(cache_arguments[key_expr])
828
+ else:
829
+ resolved_key = key_expr
830
+ # key = namespace + resolvedKey(与 _wrap_cacheable 一致,不含方法名,
831
+ # 使 @CachePut / @CacheEvict 与 @Cacheable 跨方法共享同一缓存条目)。
832
+ key_data = f"{value}:{resolved_key}"
833
+ else:
834
+ arguments = ','.join(
835
+ f"{name}:{self._cache_serialize_arg(val)}"
836
+ for name, val in sorted(cache_arguments.items())
837
+ )
838
+ key_data = f"{value}:{arguments}"
839
+ return True, hashlib.sha256(key_data.encode('utf-8')).hexdigest(), value
840
+
841
+ def _cache_get(self, cache_key: str):
842
+ """读取缓存条目(过期则视为未命中并清理)。返回 (hit, value)。"""
843
+ with self._lock:
844
+ current_time = time.time()
845
+ metadata = self._cache_metadata.get(cache_key)
846
+ if metadata is None or cache_key not in self._cache:
847
+ return False, None
848
+ if current_time > metadata.get('expire_time', current_time):
849
+ self._cache.pop(cache_key, None)
850
+ self._cache_metadata.pop(cache_key, None)
851
+ return False, None
852
+ return True, self._cache[cache_key]
853
+
854
+ def _cache_store(self, cache_key: str, result: Any, namespace: str = "") -> None:
855
+ """写入缓存条目(复用 @Cacheable 的容量淘汰与 TTL)。
856
+
857
+ ``namespace`` 登记到 metadata,供 ``@CacheEvict(all_entries=True)`` 按命名空间清空。
858
+ """
859
+ current_time = time.time()
860
+ with self._lock:
861
+ if len(self._cache) >= self._cache_max_size:
862
+ oldest_key = min(
863
+ self._cache_metadata,
864
+ key=lambda k: self._cache_metadata[k].get('create_time', 0),
865
+ )
866
+ self._cache.pop(oldest_key, None)
867
+ self._cache_metadata.pop(oldest_key, None)
868
+ self._cache[cache_key] = result
869
+ self._cache_metadata[cache_key] = {
870
+ 'create_time': current_time,
871
+ 'expire_time': current_time + self._cache_default_ttl,
872
+ 'namespace': namespace,
873
+ }
874
+
875
+ def _cache_evict_key(self, cache_key: str) -> None:
876
+ with self._lock:
877
+ self._cache.pop(cache_key, None)
878
+ self._cache_metadata.pop(cache_key, None)
879
+
880
+ def _cache_evict_prefix(self, namespace: str) -> int:
881
+ """清空整个缓存命名空间。
882
+
883
+ ``cache_key`` 由 ``sha256(...)`` 生成,无法按前缀匹配;因此在 ``_cache_store`` 时把
884
+ ``namespace`` 登记到 metadata,这里扫描 metadata 按 namespace 清空(含 @Cacheable 条目)。
885
+ """
886
+ removed = 0
887
+ with self._lock:
888
+ victims = [k for k, meta in self._cache_metadata.items()
889
+ if meta.get('namespace') == namespace]
890
+ for k in victims:
891
+ self._cache.pop(k, None)
892
+ self._cache_metadata.pop(k, None)
893
+ removed += 1
894
+ return removed
895
+
896
+ def _wrap_cache_put(self, instance: Any, method: Callable, annotation: CachePut) -> Callable:
897
+ """``@CachePut``:方法总是执行,把返回值写入缓存。"""
898
+ if asyncio.iscoroutinefunction(method):
899
+ @functools.wraps(method)
900
+ async def async_wrapper(*args, **kwargs):
901
+ enabled, cache_key, ns = self._cache_resolve_call(method, annotation, instance, args, kwargs)
902
+ result = await method(*args, **kwargs)
903
+ if enabled:
904
+ self._cache_store(cache_key, result, ns)
905
+ return result
906
+ return async_wrapper
907
+
908
+ @functools.wraps(method)
909
+ def wrapper(*args, **kwargs):
910
+ enabled, cache_key, ns = self._cache_resolve_call(method, annotation, instance, args, kwargs)
911
+ result = method(*args, **kwargs)
912
+ if enabled:
913
+ self._cache_store(cache_key, result, ns)
914
+ return result
915
+ return wrapper
916
+
917
+ def _wrap_cache_evict(self, instance: Any, method: Callable, annotation: CacheEvict) -> Callable:
918
+ """``@CacheEvict``:失效缓存。
919
+
920
+ - ``before_invocation=True``:方法调用前失效(无论成功与否)。
921
+ - ``before_invocation=False``(默认):方法成功后失效(异常时不失效)。
922
+ - ``all_entries=True``:清空整个命名空间;否则按 key 失效。
923
+ """
924
+ def _do_evict(args, kwargs):
925
+ if annotation.all_entries:
926
+ self._cache_evict_prefix(
927
+ self._resolve_cache_value(instance, annotation.value or ''))
928
+ return
929
+ enabled, cache_key, _ns = self._cache_resolve_call(method, annotation, instance, args, kwargs)
930
+ if enabled:
931
+ self._cache_evict_key(cache_key)
932
+
933
+ if asyncio.iscoroutinefunction(method):
934
+ @functools.wraps(method)
935
+ async def async_wrapper(*args, **kwargs):
936
+ if annotation.before_invocation:
937
+ _do_evict(args, kwargs)
938
+ result = await method(*args, **kwargs)
939
+ if not annotation.before_invocation:
940
+ _do_evict(args, kwargs)
941
+ return result
942
+ return async_wrapper
943
+
944
+ @functools.wraps(method)
945
+ def wrapper(*args, **kwargs):
946
+ if annotation.before_invocation:
947
+ _do_evict(args, kwargs)
948
+ result = method(*args, **kwargs)
949
+ if not annotation.before_invocation:
950
+ _do_evict(args, kwargs)
951
+ return result
952
+ return wrapper
953
+
954
+ def _wrap_caching(self, instance: Any, method: Callable, annotation: Caching) -> Callable:
955
+ """``@Caching``:组合多个缓存操作,按 cacheable -> put -> evict 顺序叠加包装。
956
+
957
+ 每个子操作调用对应的 wrap,逐层包装(与 Spring ``@Caching`` 应用多操作语义一致)。
958
+ """
959
+ wrapped = method
960
+ for op in (annotation.cacheable or []):
961
+ wrapped = self._wrap_cacheable(instance, wrapped, op)
962
+ for op in (annotation.put or []):
963
+ wrapped = self._wrap_cache_put(instance, wrapped, op)
964
+ for op in (annotation.evict or []):
965
+ wrapped = self._wrap_cache_evict(instance, wrapped, op)
966
+ return wrapped
967
+
968
+ def _wrap_retryable(self, instance: Any, method: Callable, annotation: Retryable) -> Callable:
969
+ @functools.wraps(method)
970
+ def wrapper(*args, **kwargs):
971
+ last_exception = None
972
+
973
+ for attempt in range(annotation.max_attempts):
974
+ try:
975
+ return method(instance, *args, **kwargs)
976
+ except Exception as e:
977
+ last_exception = e
978
+
979
+ if annotation.value and not any(isinstance(e, exc_type) for exc_type in annotation.value):
980
+ raise
981
+
982
+ if attempt < annotation.max_attempts - 1:
983
+ time.sleep(annotation.backoff / 1000)
984
+
985
+ raise last_exception
986
+
987
+ return wrapper
988
+
989
+ def _wrap_async(self, instance: Any, method: Callable, annotation: Async) -> Callable:
990
+ @functools.wraps(method)
991
+ def wrapper(*args, **kwargs):
992
+ if asyncio.iscoroutinefunction(method):
993
+ try:
994
+ loop = asyncio.get_running_loop()
995
+ except RuntimeError:
996
+ return _ASYNC_EXECUTOR.submit(
997
+ asyncio.run, method(*args, **kwargs)
998
+ )
999
+ return loop.create_task(method(*args, **kwargs))
1000
+
1001
+ future = _ASYNC_EXECUTOR.submit(method, *args, **kwargs)
1002
+ try:
1003
+ asyncio.get_running_loop()
1004
+ except RuntimeError:
1005
+ return future
1006
+ return asyncio.wrap_future(future)
1007
+
1008
+ return wrapper
1009
+
1010
+ def get_transaction_stack(self) -> List[Dict[str, Any]]:
1011
+ return self._get_transaction_stack()
1012
+
1013
+ def get_cache(self) -> Dict[str, Any]:
1014
+ return self._cache
1015
+
1016
+ def clear_cache(self) -> None:
1017
+ self._cache.clear()
1018
+ self._cache_metadata.clear()
1019
+
1020
+ def refresh_configuration(self) -> List[str]:
1021
+ """Rebind refresh-scoped and auto-refreshed Nacos values in live Beans.
1022
+
1023
+ Python keeps object identity stable during refresh so collaborators that
1024
+ already hold a Bean reference see the new values immediately. This is
1025
+ intentionally different from Spring Cloud's target-swapping proxy and
1026
+ avoids exposing stale Python references.
1027
+ """
1028
+ refreshed = []
1029
+ try:
1030
+ from spring.annotations.cloud import NacosValue
1031
+ except ImportError:
1032
+ NacosValue = ()
1033
+
1034
+ for bean_name, instance in list(self._bean_instances.items()):
1035
+ definition = self._bean_definitions.get(bean_name)
1036
+ if definition is None:
1037
+ continue
1038
+ is_refresh_scope = bool(definition.annotations.get('refresh_scope'))
1039
+ dynamic_values = [
1040
+ value for value in vars(instance.__class__).values()
1041
+ if NacosValue and isinstance(value, NacosValue) and value.auto_refreshed
1042
+ ]
1043
+ if not is_refresh_scope and not dynamic_values:
1044
+ continue
1045
+ self._populate_config_values(definition, instance)
1046
+ refreshed.append(bean_name)
1047
+ return refreshed
1048
+
1049
+ def set_cache_config(self, max_size: int = 1000, default_ttl: int = 300) -> None:
1050
+ """设置缓存配置"""
1051
+ self._cache_max_size = max_size
1052
+ self._cache_default_ttl = default_ttl