- スプリング拡張ポイント
- Bean ライフサイクルにおける BeanPostProcessor 拡張ポイント
- スプリング延長点コーミング
- Spring 拡張ポイントのアプリケーション シナリオ
- Nacos サービス登録を統合する
- ApplicationListener 拡張シナリオ - コンテナーで公開されたイベントをリッスンする
- ライフサイクル Nacos パブリッシュ サブスクライブ & Eureka サービスの起動、同期、拒否
- ライフサイクル拡張シナリオ - ライフサイクルの開始と停止の要件を備えたオブジェクトの管理
- Nacos の公開と購読を統合する NacosWatch
- Eurekaレジストリの統合
- 拡張機能: Eureka Server コンテキストの初期化は SmartLifecycle#start に実装されています
- 統合リボン
- SmartInitializingSingleton 拡張シナリオ - コンテナ内の Bean オブジェクトのカスタム処理
- 統合フェイグ
- FactoryBean 拡張シナリオ - インターフェイスによって生成されたプロキシ オブジェクトを Spring 管理に引き渡す
- センチネルを統合する
- HandlerInterceptor 拡張シナリオ - mvc リクエストの拡張
- SmartInitializingSingleton&FactoryBean を組み合わせたシーン - タイプに従ってオブジェクトを動的に組み立てる
- シータを統合する
- AbstractAutoProxyCreator&MethodInterceptor の組み合わせシナリオ - 実装メソッドの強化
- Nacos サービス登録を統合する
スプリング拡張ポイント
Bean ライフサイクルにおける BeanPostProcessor 拡張ポイント
-
solveBeforeInstantiation(beanName, mbdToUse);の前に Bean を作成します。=> InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(beanClass, beanName); -
Bean インスタンスの前に、
determineConstructorsFromBeanPostProcessors(beanClass, beanName); => SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors(beanClass, beanName); @Autowired 构造器 -
後の Bean インスタンス
-
マージ Bean クエリ後の Bean インスタンス
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); => MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(mbd, beanType, beanName);- AutowiredAnnotationBeanPostProcessor @Autowired @Value
- CommonAnnotationBeanPostProcessor @Resource @PostConstruct @PreDestroy
-
EarlyBean の後の Bean インスタンス
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); => SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference(exusedObject, beanName);
-
-
Bean フィールド設定
属性 Populate PopulateBean(beanName, mbd, instanceWrapper);- 前に入力されたプロパティ => InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)
- => フィールドのプロパティとセッター メソッドの後のプロパティの入力
InstantiationAwareBeanPostProcessor.postProcessProperties(pvs, bw.getWrappedInstance(), beanName)
InstantiationAwareBeanPostProcessor.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
-
Beanの初期化
-
WrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);の前に Bean を初期化します。=>- InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(result, beanName); initメソッド @PostConstruct
- ApplicationContextAwareProcessor.postProcessBeforeInitialization(result, beanName); 認識: ApplicationContextAware、ApplicationEventPublisherAware、EnvironmentAware
-
WrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);の前に Bean を初期化します。=>- AbstractAutoProxyCreator.postProcessAfterInitialization(result, beanName); AOP (wrapIfNecessary->createAopProxy().getProxy(classLoader); Cglib/Jdk)
-
bean 初期化 register distroy
registerDisposableBeanIfNecessary(beanName, bean, mbd); => destroyAwareBeanPostProcessor
-
-
destroySingleton(beanName)の後に Bean を作成
=> destroyAwareBeanPostProcessor#postProcessBeforeDestruction(this.bean, this.beanName);
スプリング延長点コーミング
- BeanFactoryポストプロセッサ
- BeanDefinitionRegistryPostProcessor
- postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) # Bean定義の登録
- BeanDefinitionRegistryPostProcessor
- Beanポストプロセッサ
- InstantiationAwareBeanPostProcessor
- postProcessBeforeInstantiation
- postProcessAfterInstantiation
- postProcessProperties
- postProcessPropertyValues
- AbstractAutoProxyCreator
- postProcessAfterInitialization # インスタンス化後の AOP サポート
- InstantiationAwareBeanPostProcessor
- @輸入
- ImportBeanDefinitionRegistrar
- registerBean定義
- インポートセレクター
- String[] selectImports(AnnotationMetadata importingClassMetadata)
- デフォルトの述語 getExclusionFilter() { return null;}
- ImportBeanDefinitionRegistrar
- わかっている
- アプリケーションコンテキスト認識
- setApplicationContext(ApplicationContext applicationContext)
- BeanFactoryAware
- setBeanFactory(BeanFactory BeanFactory)
- アプリケーションコンテキスト認識
- || Bean を初期化しています @PostConstruct
- FactoryBean (Bean を動的に作成) (& beanName FactoryBean.getObject | beanName beanFactory.getBean) (Mybatis#mapper、Openfeign)
- T getObject() は例外をスローします。
- クラス<?> getObjectType();
- デフォルトのブール値 isSingleton() { true を返します。}
- SmartInitializingSingleton Bean
- void afterSingletonsInstantiated(); # 初期化後にフィルターを呼び出します...
- アプリケーションリスナー
- void onApplicationEvent(E イベント);
- ライフサイクル
- スマートライフサイクル
- デフォルトのブール値 isAutoStartup() { true を返します。}
- デフォルトの void stop(実行可能なコールバック)
- ライフサイクルプロセッサ
- スマートライフサイクル
- ハンドラーインターセプター
- デフォルトのブール値 preHandle(HttpServletRequest 要求、HttpServletResponse 応答、オブジェクト ハンドラー) は例外をスローします
- デフォルトの void postHandle(HttpServletRequest 要求、HttpServletResponse 応答、オブジェクト ハンドラー、@Nullable ModelAndView modelAndView) は例外をスローします
- デフォルトの void afterCompletion(HttpServletRequest 要求、HttpServletResponse 応答、オブジェクト ハンドラー、@Nullable Exception ex) は例外をスローします
- メソッドインターセプター
- オブジェクト呼び出し (MethodInvocation 呼び出し) は Throwable をスローします。
Spring 拡張ポイントのアプリケーション シナリオ
Nacos サービス登録を統合する
ApplicationListener 拡張シナリオ - コンテナーで公開されたイベントをリッスンする
NamingService -> NacosNamingService => registerInstance
考察: Nacos 登録センターの統合後、なぜサービスが自動的に登録されるようになったのでしょうか? Nacos はどのようにしてサービスの自動登録を実現しているのでしょうか?
-
Tomcat が起動したら、ServletWebServerApplicationContext でイベント ServletWebServerInitializedEvent を発行します。
-
spring-cloud-commons 包 AbstractAutoServiceRegistration 监听 WebServerInitializedEvent
public abstract class AbstractAutoServiceRegistration<R extends Registration>
implements AutoServiceRegistration, ApplicationContextAware,
ApplicationListener<WebServerInitializedEvent> {
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event); // .... register();
}
}
- Nacos は ServiceRegistry.register を実装します
ライフサイクル Nacos パブリッシュ サブスクライブ & Eureka サービスの起動、同期、拒否
ライフサイクル拡張シナリオ - ライフサイクルの開始と停止の要件を備えたオブジェクトの管理
start => AbstractApplicationContext.refresh ->finishRefresh() -> getLifecycleProcessor().onRefresh(); -> 開始()
Nacos の公開と購読を統合する NacosWatch
public class NacosWatch
implements ApplicationEventPublisherAware, SmartLifecycle, DisposableBean {
@Override
public void start() {
...
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
try {
namingService.subscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService subscribe failed, properties:{}", properties, e);
}
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::nacosServicesWatch, this.properties.getWatchDelay());
}
}
}
Eurekaレジストリの統合
拡張機能: Eureka Server コンテキストの初期化は SmartLifecycle#start に実装されています
EurekaServerInitializer構成
統合リボン
SmartInitializingSingleton 拡張シナリオ - コンテナ内の Bean オブジェクトのカスタム処理
すべてのシングルトン Bean がインスタンス化された後に呼び出されます
考察: @Bean が変更した RestTemplate と @LoadBalanced でロード バランシングが実現できるのはなぜでしょうか?
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
LoadBalancerAutoConfiguration
の SmartInitializingSingleton への拡張は、@LoadBalanced で修飾されたすべてのrestTemplates のロード バランシング ロジック インターセプター LoadBalancerInterceptor をバインドします (@Qualifier 修飾子を使用)
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
/**
* Annotation to mark a RestTemplate or WebClient bean to be configured to use a
* LoadBalancerClient.
* @author Spencer Gibb
*/
@Target({
ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier //Qualifier
public @interface LoadBalanced {
}
ロードバランサーインターセプター
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor loadBalancerInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
統合フェイグ
FactoryBean 拡張シナリオ - インターフェイスによって生成されたプロキシ オブジェクトを Spring 管理に引き渡す
考察: なぜ Feign インターフェースは @Autowired を通じて直接注入できるのでしょうか? Feign インターフェースは Spring 管理者にどのように引き継がれるのでしょうか?
@FeignClient(value = "order-service", path = "/order")
public interface OrderFeignService {
@GetMapping(value = "/info/{productId}")
public String findOrderInfoByProductId(@PathVariable("productId") Integer productId);
}
@SpringBootApplication
@EnableFeignClients(basePackages = "com.mx.use.feign")
@EnableHystrix
@EnableHystrixDashboard //开启 Hystrix 监控功能
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
...
}
FactoryBean => FeignClientsRegistrar -> scan(FeignClient) -> registerFeignClient -> FeignClientFactoryBean
@Override
public Object getObject() {
return getTarget(); //
}
センチネルを統合する
HandlerInterceptor 拡張シナリオ - mvc リクエストの拡張
AbstractSentinelInterceptor#preHandle
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
...
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
String resourceName = getResourceName(request);
if (StringUtil.isEmpty(resourceName)) {
return true;
}
if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) {
return true;
}
// Parse the request origin using registered origin parser.
String origin = parseOrigin(request);
String contextName = getContextName(request);
ContextUtil.enter(contextName, origin);
//Sentinel入口
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);
return true;
} catch (BlockException e) {
try {
handleBlockException(request, response, e);
} finally {
ContextUtil.exit();
}
return false;
}
}
...
}
SmartInitializingSingleton&FactoryBean を組み合わせたシーン - タイプに従ってオブジェクトを動的に組み立てる
SentinelDataSourceHandler
#Sentinel持久化读数据源设计,利用了SmartInitializingSingleton扩展点
SentinelDataSourceHandler#afterSingletonsInstantiated
# 注册一个FactoryBean类型的数据源
》SentinelDataSourceHandler#registerBean
》》NacosDataSourceFactoryBean#getObject
# 利用FactoryBean获取到读数据源
》》new NacosDataSource(properties, groupId, dataId, converter)
NacosDataSourceFactoryBean
public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> {
...
@Override
public NacosDataSource getObject() throws Exception {
...
return new NacosDataSource(properties, groupId, dataId, converter);
}
...
}
SentinelDataSourceHandler
public class SentinelDataSourceHandler implements SmartInitializingSingleton {
...
@Override
public void afterSingletonsInstantiated() {
...
//sentinel.nacos.config.serverAddr=${sentinel.nacos.config.serverAddr}
//NacosDataSource
AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties.getValidDataSourceProperties();
abstractDataSourceProperties.setEnv(env);
abstractDataSourceProperties.preCheck(dataSourceName);
registerBean(abstractDataSourceProperties, dataSourceName+ "-sentinel-" + validFields.get(0) + "-datasource"); //NacosDataSource
...
}
...
}
シータの統合 (Spring Aop)
AbstractAutoProxyCreator&MethodInterceptor の組み合わせシナリオ - 実装メソッドの強化
グローバルトランザクションスキャナー
GlobalTransactionalInterceptor
public class GlobalTransactionalInterceptor implements ConfigurationChangeListener, MethodInterceptor, SeataInterceptor {
...
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
Class<?> targetClass =
methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
final Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
final GlobalTransactional globalTransactionalAnnotation =
getAnnotation(method, targetClass, GlobalTransactional.class);
final GlobalLock globalLockAnnotation = getAnnotation(method, targetClass, GlobalLock.class);
boolean localDisable = disable || (degradeCheck && degradeNum >= degradeCheckAllowTimes);
if (!localDisable) {
if (globalTransactionalAnnotation != null || this.aspectTransactional != null) {
AspectTransactional transactional;
if (globalTransactionalAnnotation != null) {
transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(),
globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(),
globalTransactionalAnnotation.noRollbackForClassName(),
globalTransactionalAnnotation.noRollbackFor(),
globalTransactionalAnnotation.noRollbackForClassName(),
globalTransactionalAnnotation.propagation(),
globalTransactionalAnnotation.lockRetryInterval(),
globalTransactionalAnnotation.lockRetryTimes());
} else {
transactional = this.aspectTransactional;
}
// 执行事务 transactionalTemplate.execute()
return handleGlobalTransaction(methodInvocation, transactional);
} else if (globalLockAnnotation != null) {
return handleGlobalLock(methodInvocation, globalLockAnnotation);
}
}
}
return methodInvocation.proceed();
}
...
}
public class TransactionalTemplate {
public Object execute(TransactionalExecutor business) throws Throwable {
// 1. Get transactionInfo
TransactionInfo txInfo = business.getTransactionInfo();
if (txInfo == null) {
throw new ShouldNeverHappenException("transactionInfo does not exist");
}
// 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
GlobalTransaction tx = GlobalTransactionContext.getCurrent();
// 1.2 Handle the transaction propagation.
Propagation propagation = txInfo.getPropagation();
SuspendedResourcesHolder suspendedResourcesHolder = null;
try {
switch (propagation) {
case NOT_SUPPORTED:
// If transaction is existing, suspend it.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
}
// Execute without transaction and return.
return business.execute();
case REQUIRES_NEW:
// If transaction is existing, suspend it, and then begin new transaction.
if (existingTransaction(tx)) {
suspendedResourcesHolder = tx.suspend();
tx = GlobalTransactionContext.createNew();
}
// Continue and execute with new transaction
break;
case SUPPORTS:
// If transaction is not existing, execute without transaction.
if (notExistingTransaction(tx)) {
return business.execute();
}
// Continue and execute with new transaction
break;
case REQUIRED:
// If current transaction is existing, execute with current transaction,
// else continue and execute with new transaction.
break;
case NEVER:
// If transaction is existing, throw exception.
if (existingTransaction(tx)) {
throw new TransactionException(
String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
, tx.getXid()));
} else {
// Execute without transaction and return.
return business.execute();
}
case MANDATORY:
// If transaction is not existing, throw exception.
if (notExistingTransaction(tx)) {
throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
}
// Continue and execute with current transaction.
break;
default:
throw new TransactionException("Not Supported Propagation:" + propagation);
}
// 1.3 If null, create new transaction with role 'GlobalTransactionRole.Launcher'.
if (tx == null) {
tx = GlobalTransactionContext.createNew();
}
// set current tx config to holder
GlobalLockConfig previousConfig = replaceGlobalLockConfig(txInfo);
try {
// 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
// else do nothing. Of course, the hooks will still be triggered.
beginTransaction(txInfo, tx);
Object rs;
try {
// Do Your Business
rs = business.execute();
} catch (Throwable ex) {
// 3. The needed business exception to rollback.
completeTransactionAfterThrowing(txInfo, tx, ex);
throw ex;
}
// 4. everything is fine, commit.
commitTransaction(tx);
return rs;
} finally {
//5. clear
resumeGlobalLockConfig(previousConfig);
triggerAfterCompletion();
cleanUp();
}
} finally {
// If the transaction is suspended, resume it.
if (suspendedResourcesHolder != null) {
tx.resume(suspendedResourcesHolder);
}
}
}
}