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
|
@@ -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
|