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,589 @@
1
+ from typing import Optional, Type, Any, List, Dict, get_type_hints
2
+ import os
3
+ import sys
4
+ from spring.context.bean_factory import BeanFactory
5
+ from spring.context.bean_definition import BeanDefinition
6
+ from spring.context.scanner import ComponentScanner
7
+ from spring.annotations.core import (
8
+ SpringBootApplication,
9
+ ComponentScan,
10
+ Component,
11
+ Service,
12
+ Repository,
13
+ Controller,
14
+ RestController,
15
+ Configuration,
16
+ Bean,
17
+ Autowired,
18
+ Qualifier,
19
+ Value,
20
+ ConfigurationProperties,
21
+ Scope,
22
+ Profile,
23
+ Lazy,
24
+ EventListener,
25
+ )
26
+ from spring.annotations.cloud import EnableFeignClients, FeignClient
27
+ from spring.annotations.conditional import all_conditions_match as _all_conditions_match
28
+ from spring.config.config_loader import ConfigLoader, set_global_config_loader
29
+ from spring.event import ApplicationEventPublisher
30
+ from spring.core.typing_utils import unwrap_optional_type
31
+ import inspect
32
+
33
+
34
+ class ApplicationContext:
35
+ _current_context: Optional['ApplicationContext'] = None
36
+
37
+ def __init__(self, main_class: Type, config_loader: Optional[ConfigLoader] = None):
38
+ self.main_class = main_class
39
+ ApplicationContext._current_context = self
40
+
41
+ # 确保main_class是一个类
42
+ if not inspect.isclass(main_class):
43
+ # 如果不是类,尝试从__spring_annotations__中获取原始类
44
+ if hasattr(main_class, '__spring_annotations__'):
45
+ for annotation in main_class.__spring_annotations__:
46
+ if hasattr(annotation, '_original_class'):
47
+ main_class = annotation._original_class
48
+ break
49
+
50
+ main_class_file = inspect.getfile(main_class)
51
+ main_class_dir = os.path.dirname(os.path.abspath(main_class_file))
52
+ context_config_loader = config_loader or ConfigLoader(base_path=main_class_dir)
53
+ self.config_loader = set_global_config_loader(context_config_loader)
54
+ self.bean_factory = BeanFactory(self.config_loader)
55
+ self.event_publisher = ApplicationEventPublisher()
56
+ publisher_definition = BeanDefinition(
57
+ bean_class=ApplicationEventPublisher,
58
+ bean_name='application_event_publisher',
59
+ )
60
+ self.bean_factory.register_bean_definition(
61
+ 'application_event_publisher', publisher_definition
62
+ )
63
+ self.bean_factory.register_instance(
64
+ 'application_event_publisher', self.event_publisher
65
+ )
66
+ # 事务事件发布器(@TransactionalEventListener);spring.tx 缺失时为 None,降级为普通事件
67
+ self.tx_event_publisher = None
68
+ try:
69
+ from spring.tx import TransactionalEventPublisher
70
+ self.tx_event_publisher = TransactionalEventPublisher()
71
+ tx_publisher_definition = BeanDefinition(
72
+ bean_class=TransactionalEventPublisher,
73
+ bean_name='transactional_event_publisher',
74
+ )
75
+ self.bean_factory.register_bean_definition(
76
+ 'transactional_event_publisher', tx_publisher_definition
77
+ )
78
+ self.bean_factory.register_instance(
79
+ 'transactional_event_publisher', self.tx_event_publisher
80
+ )
81
+ except ImportError: # pragma: no cover
82
+ pass
83
+ self.scanner = ComponentScanner(self)
84
+ self._scheduler = None
85
+ self._started = False
86
+ from spring.utils.logger import SpringLogger
87
+ self.logger = SpringLogger()
88
+
89
+ @classmethod
90
+ def get_instance(cls) -> Optional['ApplicationContext']:
91
+ """Return the currently active application context, if any."""
92
+ return cls._current_context
93
+
94
+ def refresh(self) -> None:
95
+ if self._started:
96
+ return
97
+
98
+ try:
99
+ self._load_config()
100
+ self._scan_components()
101
+ self._register_feign_clients()
102
+ self._register_configuration_beans()
103
+ self._autowire_configuration_properties()
104
+ self._autowire_value_annotations()
105
+ self._register_event_listeners()
106
+ self._register_scheduled_tasks()
107
+ self._started = True
108
+ except Exception as e:
109
+ import traceback
110
+ self.logger.error(f"Failed to refresh application context: {str(e)}")
111
+ self.logger.error(traceback.format_exc())
112
+ raise
113
+
114
+ def _load_config(self) -> None:
115
+ self.config_loader.load_config()
116
+
117
+ def _scan_components(self) -> None:
118
+ base_packages = self._get_base_packages()
119
+ components = self.scanner.scan(base_packages)
120
+ for component in components:
121
+ self._register_component(component)
122
+
123
+ def _register_feign_clients(self) -> None:
124
+ """Scan and register typed ``@FeignClient`` proxies when enabled."""
125
+ annotations = getattr(self.main_class, '__spring_annotations__', [])
126
+ enable = next((item for item in annotations if isinstance(item, EnableFeignClients)), None)
127
+ if enable is None:
128
+ return
129
+ base_packages = enable.base_packages or self._get_base_packages()
130
+ from spring.cloud.feign import create_declared_feign_client
131
+ seen = set(self.bean_factory.get_bean_names())
132
+ for client_class in self.scanner.scan_classes(base_packages):
133
+ client_annotations = getattr(client_class, '__spring_annotations__', [])
134
+ client_annotation = next(
135
+ (item for item in client_annotations if isinstance(item, FeignClient)),
136
+ None,
137
+ )
138
+ if client_annotation is None:
139
+ continue
140
+ bean_name = client_annotation.value or self._generate_bean_name(client_class)
141
+ if bean_name in seen:
142
+ continue
143
+ definition = BeanDefinition(bean_class=client_class, bean_name=bean_name)
144
+ definition.add_annotation(client_annotation)
145
+ self.bean_factory.register_bean_definition(bean_name, definition)
146
+ self.bean_factory.register_instance(
147
+ bean_name,
148
+ create_declared_feign_client(client_class, client_annotation),
149
+ )
150
+ seen.add(bean_name)
151
+
152
+ def _get_base_packages(self) -> List[str]:
153
+ annotations = getattr(self.main_class, '__spring_annotations__', [])
154
+ for annotation in annotations:
155
+ if isinstance(annotation, SpringBootApplication):
156
+ if annotation.scan_base_packages:
157
+ return annotation.scan_base_packages
158
+ return [self._extract_package_name(self.main_class)]
159
+ elif isinstance(annotation, ComponentScan):
160
+ if annotation.base_packages:
161
+ return annotation.base_packages
162
+ return [self._extract_package_name(self.main_class)]
163
+
164
+ def _extract_package_name(self, main_class: Type) -> str:
165
+ module_name = main_class.__module__
166
+ # 处理__main__模块的情况(python -m xxx运行时)
167
+ if module_name == '__main__':
168
+ # 从__file__属性推断实际的模块名
169
+ if hasattr(main_class, '__module__'):
170
+ module_obj = sys.modules.get(main_class.__module__)
171
+ if module_obj and hasattr(module_obj, '__file__'):
172
+ import os
173
+ file_path = module_obj.__file__
174
+ # 去掉.py后缀和路径,获取模块名
175
+ module_name = os.path.basename(file_path)[:-3]
176
+ # 如果是Application.py,返回当前目录名作为包名
177
+ if module_name == 'Application':
178
+ return os.path.basename(os.path.dirname(file_path))
179
+ if '.' in module_name:
180
+ return module_name.rsplit('.', 1)[0]
181
+ return module_name
182
+
183
+ def _register_component(self, component_class: Type) -> None:
184
+ if not self._matches_active_profile(component_class):
185
+ return
186
+ if not self._matches_conditions(component_class):
187
+ return
188
+
189
+ annotations = getattr(component_class, '__spring_annotations__', [])
190
+ explicit_name = next(
191
+ (
192
+ getattr(annotation, 'value', '')
193
+ for annotation in annotations
194
+ if isinstance(annotation, (Component, Service, Repository, Controller, RestController))
195
+ and getattr(annotation, 'value', '')
196
+ ),
197
+ '',
198
+ )
199
+ bean_name = explicit_name or self._generate_bean_name(component_class)
200
+ scope = next(
201
+ (
202
+ annotation.value for annotation in annotations if isinstance(annotation, Scope)
203
+ ),
204
+ 'singleton',
205
+ )
206
+ definition = BeanDefinition(bean_class=component_class, bean_name=bean_name, scope=scope)
207
+
208
+ for annotation in annotations:
209
+ definition.add_annotation(annotation)
210
+
211
+ self._extract_dependencies(component_class, definition)
212
+ self.bean_factory.register_bean_definition(bean_name, definition)
213
+
214
+ def _matches_active_profile(self, component_class: Type) -> bool:
215
+ annotations = getattr(component_class, '__spring_annotations__', [])
216
+ for annotation in annotations:
217
+ if isinstance(annotation, Profile):
218
+ active_profile = self.config_loader.get_active_profile()
219
+ return active_profile in annotation.value
220
+ return True
221
+
222
+ def _matches_conditions(self, component_class: Type) -> bool:
223
+ """求类上条件装配注解(@Conditional / @ConditionalOnProperty / ...)的合取。
224
+
225
+ 与 ``_matches_active_profile`` 并列,在 ``_register_component`` 阶段执行:
226
+ 任一条件为假则跳过该 Bean 的注册。条件注解的 ``matches(ctx)`` 接收本上下文,
227
+ 可访问 ``self.config_loader`` 与 ``self.bean_factory``。
228
+ """
229
+ return _all_conditions_match(component_class, self)
230
+
231
+ def _generate_bean_name(self, cls: Type) -> str:
232
+ name = cls.__name__
233
+ if name.endswith('Controller'):
234
+ base_name = name[:-10]
235
+ elif name.endswith('Service'):
236
+ base_name = name[:-7]
237
+ elif name.endswith('Repository'):
238
+ base_name = name[:-10]
239
+ elif name.endswith('Config'):
240
+ base_name = name[:-6]
241
+ else:
242
+ base_name = name
243
+
244
+ # 将驼峰式转换为下划线式
245
+ result = []
246
+ for i, char in enumerate(base_name):
247
+ if i > 0 and char.isupper():
248
+ result.append('_')
249
+ result.append(char.lower())
250
+
251
+ suffix = ''
252
+ if name.endswith('Controller'):
253
+ suffix = '_controller'
254
+ elif name.endswith('Service'):
255
+ suffix = '_service'
256
+ elif name.endswith('Repository'):
257
+ suffix = '_repository'
258
+ elif name.endswith('Config'):
259
+ suffix = '_config'
260
+
261
+ return ''.join(result) + suffix
262
+
263
+ def _extract_dependencies(self, cls: Type, definition: BeanDefinition) -> None:
264
+ if hasattr(cls, '__init__'):
265
+ init_annotations = getattr(cls.__init__, '__spring_annotations__', [])
266
+ if any(isinstance(a, Autowired) for a in init_annotations):
267
+ sig = inspect.signature(cls.__init__)
268
+ try:
269
+ type_hints = get_type_hints(cls.__init__, include_extras=True)
270
+ except (NameError, TypeError):
271
+ type_hints = {}
272
+ autowired = next(a for a in init_annotations if isinstance(a, Autowired))
273
+ for param_name, param in sig.parameters.items():
274
+ if param_name == 'self':
275
+ continue
276
+ parameter_type = type_hints.get(param_name, param.annotation)
277
+ # 解包 Optional[X]:Python 3.10 的 get_type_hints 会把带 None 默认值的
278
+ # 构造参数注解自动包装为 Optional[X](3.11+ 不再包装)。此处统一解包为承载类型,
279
+ # 否则按类型匹配 Bean 时 Optional[SomeService] 无法命中已注册的 SomeService。
280
+ # 在 Annotated 解包之前先解 Optional,可正确处理 Optional[Annotated[X, Q]]。
281
+ parameter_type = unwrap_optional_type(parameter_type)
282
+ if parameter_type is not inspect.Parameter.empty:
283
+ qualifier = None
284
+ for ann in init_annotations:
285
+ if isinstance(ann, Qualifier):
286
+ qualifier = ann.value
287
+ break
288
+ inline_qualifier = None
289
+ try:
290
+ from typing import Annotated, get_args, get_origin
291
+ if get_origin(parameter_type) is Annotated:
292
+ base, *metadata = get_args(parameter_type)
293
+ parameter_type = base
294
+ inline_qualifier = next(
295
+ (item.value for item in metadata if isinstance(item, Qualifier)),
296
+ None,
297
+ )
298
+ except (TypeError, AttributeError):
299
+ pass
300
+ definition.add_dependency(
301
+ param_name,
302
+ parameter_type,
303
+ inline_qualifier or qualifier,
304
+ required=autowired.required,
305
+ )
306
+
307
+ def _register_configuration_beans(self) -> None:
308
+ for bean_name in self.bean_factory.get_bean_names():
309
+ definition = self.bean_factory.get_bean_definition(bean_name)
310
+ if definition and Configuration._annotation_type in definition.annotations:
311
+ config_instance = self.bean_factory.get_bean(bean_name)
312
+ self._register_beans_from_configuration(config_instance, definition)
313
+
314
+ def _register_beans_from_configuration(self, config_instance: Any, config_definition: BeanDefinition) -> None:
315
+ for name, method in inspect.getmembers(config_instance.__class__):
316
+ if not name.startswith('_') and inspect.isfunction(method):
317
+ annotations = getattr(method, '__spring_annotations__', [])
318
+ for annotation in annotations:
319
+ if isinstance(annotation, Bean):
320
+ bean_name = annotation.name or name
321
+ scope = annotation.scope
322
+ init_method = annotation.init_method
323
+ destroy_method = annotation.destroy_method
324
+
325
+ # 对工厂方法应用 Cloud AOP 注解(如 @LoadBalanced)
326
+ wrapped_method = method
327
+ try:
328
+ from spring.aop.cloud_aop import apply_cloud_annotations
329
+ wrapped_method = apply_cloud_annotations(config_instance, method)
330
+ except ImportError:
331
+ pass
332
+
333
+ # 对工厂方法应用 comprehensive AOP 注解
334
+ try:
335
+ from spring.aop.comprehensive_aop import apply_annotations
336
+ wrapped_method = apply_annotations(config_instance, wrapped_method)
337
+ except ImportError:
338
+ pass
339
+
340
+ bean_def = BeanDefinition(
341
+ bean_class=method,
342
+ bean_name=bean_name,
343
+ scope=scope,
344
+ init_method=init_method,
345
+ destroy_method=destroy_method,
346
+ factory_method=wrapped_method, # 使用包装后的方法
347
+ factory_class=config_instance.__class__,
348
+ )
349
+
350
+ for method_annotation in annotations:
351
+ bean_def.add_annotation(method_annotation)
352
+
353
+ return_type = inspect.signature(method).return_annotation
354
+ if return_type is not inspect.Signature.empty:
355
+ bean_def.bean_class = return_type
356
+
357
+ self.bean_factory.register_bean_definition(bean_name, bean_def)
358
+
359
+ def _autowire_configuration_properties(self) -> None:
360
+ for bean_name in self.bean_factory.get_bean_names():
361
+ definition = self.bean_factory.get_bean_definition(bean_name)
362
+ if definition and ConfigurationProperties._annotation_type in definition.annotations:
363
+ lazy_annotations = definition.annotations.get(Lazy._annotation_type, [])
364
+ if any(annotation.value for annotation in lazy_annotations):
365
+ continue
366
+ instance = self.bean_factory.get_bean(bean_name)
367
+ self._apply_configuration_properties(instance, definition)
368
+
369
+ def _apply_configuration_properties(self, instance: Any, definition: BeanDefinition) -> None:
370
+ properties_annotations = definition.annotations.get(ConfigurationProperties._annotation_type)
371
+ if not properties_annotations:
372
+ return
373
+
374
+ properties_annotation = properties_annotations[0]
375
+ prefix = properties_annotation.prefix
376
+ config = self.config_loader.get_prefix_config(prefix)
377
+
378
+ # 松散绑定(kebab/camel/snake 等价匹配)+ 嵌套绑定 + 类型强转
379
+ try:
380
+ from spring.config.binding import (
381
+ ConfigurationPropertiesBinder, validate_configuration_properties,
382
+ )
383
+ ConfigurationPropertiesBinder.bind(instance, config)
384
+ # @Validated 触发 Bean Validation;违反约束抛 ValidationError
385
+ validate_configuration_properties(instance)
386
+ except ImportError: # pragma: no cover - binding 为内置模块
387
+ # 回退到原扁平绑定(兼容 spring.config.binding 缺失场景)
388
+ for key, value in config.items():
389
+ attr_name = key.replace('-', '_')
390
+ if hasattr(instance, attr_name):
391
+ setattr(instance, attr_name, value)
392
+ elif hasattr(instance, key):
393
+ setattr(instance, key, value)
394
+
395
+ def _autowire_value_annotations(self) -> None:
396
+ for bean_name in self.bean_factory.get_bean_names():
397
+ try:
398
+ # 获取bean实例(如果尚未实例化,会触发创建)
399
+ definition = self.bean_factory.get_bean_definition(bean_name)
400
+ if definition:
401
+ lazy_annotations = definition.annotations.get(Lazy._annotation_type, [])
402
+ if any(annotation.value for annotation in lazy_annotations):
403
+ continue
404
+ instance = self.bean_factory.get_bean(bean_name)
405
+ bean_class = instance.__class__
406
+
407
+ # 处理构造函数参数中的@Value注解
408
+ if hasattr(bean_class, '__init__'):
409
+ sig = inspect.signature(bean_class.__init__)
410
+ for param_name, param in sig.parameters.items():
411
+ if param_name == 'self':
412
+ continue
413
+ if isinstance(param.default, Value):
414
+ value_annotation = param.default
415
+ config_value = self.config_loader.resolve_value_expression(
416
+ value_annotation.value,
417
+ getattr(value_annotation, 'default', None),
418
+ )
419
+ setattr(instance, param_name, config_value)
420
+
421
+ # 处理字段上的@Value注解
422
+ for name, field in inspect.getmembers(bean_class):
423
+ if not name.startswith('_'):
424
+ annotations = getattr(field, '__spring_annotations__', [])
425
+ for annotation in annotations:
426
+ if isinstance(annotation, Value):
427
+ setattr(
428
+ instance,
429
+ name,
430
+ self.config_loader.resolve_value_expression(
431
+ annotation.value,
432
+ getattr(annotation, 'default', None),
433
+ ),
434
+ )
435
+ else:
436
+ try:
437
+ from spring.annotations.cloud import NacosValue
438
+ except ImportError:
439
+ NacosValue = ()
440
+ if NacosValue and isinstance(annotation, NacosValue):
441
+ setattr(
442
+ instance,
443
+ name,
444
+ self.config_loader.resolve_value_expression(
445
+ annotation.value,
446
+ None,
447
+ ),
448
+ )
449
+ except Exception:
450
+ # 跳过无法实例化的bean(如配置类等)
451
+ continue
452
+
453
+ def _register_scheduled_tasks(self) -> None:
454
+ from spring.scheduling.scheduler import Scheduler
455
+ from spring.annotations.core import Scheduled
456
+
457
+ self._scheduler = Scheduler()
458
+
459
+ for bean_name in self.bean_factory.get_bean_names():
460
+ definition = self.bean_factory.get_bean_definition(bean_name)
461
+ if not definition:
462
+ continue
463
+
464
+ instance = self.bean_factory.get_bean(bean_name)
465
+ bean_class = instance.__class__
466
+
467
+ for name, method in inspect.getmembers(bean_class):
468
+ if not name.startswith('_') and inspect.isfunction(method):
469
+ annotations = getattr(method, '__spring_annotations__', [])
470
+ for annotation in annotations:
471
+ if isinstance(annotation, Scheduled):
472
+ task_id = f"{bean_name}.{name}"
473
+ self._scheduler.schedule(
474
+ task_id=task_id,
475
+ func=method.__get__(instance),
476
+ fixed_rate=annotation.fixed_rate,
477
+ fixed_delay=annotation.fixed_delay,
478
+ cron=annotation.cron,
479
+ initial_delay=annotation.initial_delay,
480
+ )
481
+
482
+ def _register_event_listeners(self) -> None:
483
+ self.event_publisher.clear()
484
+ if self.tx_event_publisher is not None:
485
+ self.tx_event_publisher.clear()
486
+ # 事务事件监听器注解类型(spring.tx 缺失时为 None)
487
+ try:
488
+ from spring.tx import TransactionalEventListener as _TxEventListener
489
+ except ImportError: # pragma: no cover
490
+ _TxEventListener = None
491
+
492
+ for bean_name in self.bean_factory.get_bean_names():
493
+ instance = self.bean_factory.get_bean(bean_name)
494
+ for name, method in inspect.getmembers(instance.__class__):
495
+ if name.startswith('_') or not inspect.isfunction(method):
496
+ continue
497
+ for annotation in getattr(method, '__spring_annotations__', []):
498
+ if isinstance(annotation, EventListener):
499
+ event_type = annotation.event_type
500
+ if event_type is None:
501
+ parameters = [
502
+ (parameter_name, parameter)
503
+ for parameter_name, parameter in inspect.signature(method).parameters.items()
504
+ if parameter_name != 'self'
505
+ ]
506
+ if parameters:
507
+ parameter_name, parameter = parameters[0]
508
+ try:
509
+ event_type = get_type_hints(method).get(parameter_name)
510
+ except (NameError, TypeError):
511
+ event_type = parameter.annotation
512
+ if not isinstance(event_type, type):
513
+ event_type = None
514
+ self.event_publisher.add_listener(
515
+ getattr(instance, name),
516
+ event_type=event_type,
517
+ order=annotation.order,
518
+ )
519
+ elif _TxEventListener is not None and isinstance(annotation, _TxEventListener):
520
+ # 事务事件监听器:注册到 TransactionalEventPublisher,按阶段触发
521
+ event_type = annotation.event_type
522
+ if event_type is None:
523
+ parameters = [
524
+ (parameter_name, parameter)
525
+ for parameter_name, parameter in inspect.signature(method).parameters.items()
526
+ if parameter_name != 'self'
527
+ ]
528
+ if parameters:
529
+ parameter_name, parameter = parameters[0]
530
+ try:
531
+ event_type = get_type_hints(method).get(parameter_name)
532
+ except (NameError, TypeError):
533
+ event_type = parameter.annotation
534
+ if not isinstance(event_type, type):
535
+ event_type = None
536
+ self.tx_event_publisher.add_listener(
537
+ getattr(instance, name),
538
+ event_type=event_type,
539
+ phase=annotation.phase,
540
+ fallback_execution=annotation.fallback_execution,
541
+ order=annotation.order,
542
+ )
543
+
544
+ def publish_event(self, event: Any):
545
+ # 普通监听器立即触发;事务监听器按事务阶段触发(无事务时按 fallback_execution 决定)
546
+ self.event_publisher.publish_event(event)
547
+ if self.tx_event_publisher is not None:
548
+ return self.tx_event_publisher.publish_event(event)
549
+ return event
550
+
551
+ def get_event_publisher(self) -> ApplicationEventPublisher:
552
+ return self.event_publisher
553
+
554
+ def get_bean(self, bean_name: str) -> Any:
555
+ return self.bean_factory.get_bean(bean_name)
556
+
557
+ def get_bean_by_type(self, bean_type: Type) -> Any:
558
+ return self.bean_factory.get_bean_by_type(bean_type)
559
+
560
+ def contains_bean(self, bean_name: str) -> bool:
561
+ return self.bean_factory.contains_bean(bean_name)
562
+
563
+ def get_bean_names(self) -> List[str]:
564
+ return self.bean_factory.get_bean_names()
565
+
566
+ def get_config(self) -> Dict[str, Any]:
567
+ return self.config_loader.get_config()
568
+
569
+ def get_value(self, key: str, default: Any = None) -> Any:
570
+ return self.config_loader.get_value(key, default)
571
+
572
+ def refresh_configuration(self) -> List[str]:
573
+ """Reload ``application.yml`` and rebind refreshable Beans."""
574
+ self.config_loader.reload()
575
+ refreshed = self.bean_factory.refresh_configuration()
576
+ try:
577
+ from spring.aop.cloud_aop import trigger_config_refresh
578
+ trigger_config_refresh()
579
+ except ImportError:
580
+ pass
581
+ return refreshed
582
+
583
+ def destroy(self) -> None:
584
+ self.bean_factory.destroy_all()
585
+ self.event_publisher.clear()
586
+ if self.tx_event_publisher is not None:
587
+ self.tx_event_publisher.clear()
588
+ if ApplicationContext._current_context is self:
589
+ ApplicationContext._current_context = None
@@ -0,0 +1,70 @@
1
+ from typing import Optional, Type, Callable, Dict, Any, List
2
+ from spring.annotations.core import SpringAnnotation
3
+
4
+
5
+ class BeanDefinition:
6
+ def __init__(
7
+ self,
8
+ bean_class: Type,
9
+ bean_name: str,
10
+ scope: str = "singleton",
11
+ init_method: Optional[str] = None,
12
+ destroy_method: Optional[str] = None,
13
+ factory_method: Optional[Callable] = None,
14
+ factory_class: Optional[Type] = None,
15
+ ):
16
+ self.bean_class = bean_class
17
+ self.bean_name = bean_name
18
+ self.scope = scope
19
+ self.init_method = init_method
20
+ self.destroy_method = destroy_method
21
+ self.factory_method = factory_method
22
+ self.factory_class = factory_class
23
+ self.annotations: Dict[str, List[SpringAnnotation]] = {}
24
+ self.dependencies: Dict[str, Type] = {}
25
+ self.qualifiers: Dict[str, str] = {}
26
+ self.dependency_required: Dict[str, bool] = {}
27
+ self._instance: Optional[Any] = None
28
+ self._initialized: bool = False
29
+ self._destroyed: bool = False
30
+
31
+ @property
32
+ def is_singleton(self) -> bool:
33
+ return self.scope == "singleton"
34
+
35
+ @property
36
+ def is_prototype(self) -> bool:
37
+ return self.scope == "prototype"
38
+
39
+ def get_instance(self) -> Optional[Any]:
40
+ if self.is_singleton:
41
+ return self._instance
42
+ return None
43
+
44
+ def set_instance(self, instance: Any) -> None:
45
+ if self.is_singleton:
46
+ self._instance = instance
47
+
48
+ def mark_initialized(self) -> None:
49
+ self._initialized = True
50
+
51
+ def mark_destroyed(self) -> None:
52
+ self._destroyed = True
53
+
54
+ def add_annotation(self, annotation: SpringAnnotation) -> None:
55
+ annotation_type = annotation._annotation_type
56
+ if annotation_type not in self.annotations:
57
+ self.annotations[annotation_type] = []
58
+ self.annotations[annotation_type].append(annotation)
59
+
60
+ def add_dependency(
61
+ self,
62
+ field_name: str,
63
+ field_type: Type,
64
+ qualifier: Optional[str] = None,
65
+ required: bool = True,
66
+ ) -> None:
67
+ self.dependencies[field_name] = field_type
68
+ self.dependency_required[field_name] = required
69
+ if qualifier:
70
+ self.qualifiers[field_name] = qualifier