Spring Cloud (13): Spring 拡張機能

  • スプリング拡張ポイント
    • Bean ライフサイクルにおける BeanPostProcessor 拡張ポイント
    • スプリング延長点コーミング
  • Spring 拡張ポイントのアプリケーション シナリオ
    • Nacos サービス登録を統合する
      • ApplicationListener 拡張シナリオ - コンテナーで公開されたイベントをリッスンする
      • ライフサイクル Nacos パブリッシュ サブスクライブ & Eureka サービスの起動、同期、拒否
      • ライフサイクル拡張シナリオ - ライフサイクルの開始と停止の要件を備えたオブジェクトの管理
    • Nacos の公開と購読を統合する NacosWatch
    • Eurekaレジストリの統合
      • 拡張機能: Eureka Server コンテキストの初期化は SmartLifecycle#start に実装されています
    • 統合リボン
      • SmartInitializingSingleton 拡張シナリオ - コンテナ内の Bean オブジェクトのカスタム処理
    • 統合フェイグ
      • FactoryBean 拡張シナリオ - インターフェイスによって生成されたプロキシ オブジェクトを Spring 管理に引き渡す
    • センチネルを統合する
      • HandlerInterceptor 拡張シナリオ - mvc リクエストの拡張
      • SmartInitializingSingleton&FactoryBean を組み合わせたシーン - タイプに従ってオブジェクトを動的に組み立てる
    • シータを統合する
      • AbstractAutoProxyCreator&MethodInterceptor の組み合わせシナリオ - 実装メソッドの強化

スプリング拡張ポイント

Bean ライフサイクルにおける BeanPostProcessor 拡張ポイント


  1. solveBeforeInstantiation(beanName, mbdToUse);の前に Bean を作成します。=> InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(beanClass, beanName);

  2. Bean インスタンスの前に、
    determineConstructorsFromBeanPostProcessors(beanClass, beanName); => SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors(beanClass, beanName); @Autowired 构造器

  3. 後の Bean インスタンス

    • マージ Bean クエリ後の Bean インスタンス
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); => MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(mbd, beanType, beanName);

      1. AutowiredAnnotationBeanPostProcessor @Autowired @Value
      2. CommonAnnotationBeanPostProcessor @Resource @PostConstruct @PreDestroy
    • EarlyBean の後の Bean インスタンス
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); => SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference(exusedObject, beanName);

  4. Bean フィールド設定
    属性 Populate PopulateBean(beanName, mbd, instanceWrapper);

    • 前に入力されたプロパティ => InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)
    • => フィールドのプロパティとセッター メソッドの後のプロパティの入力
      InstantiationAwareBeanPostProcessor.postProcessProperties(pvs, bw.getWrappedInstance(), beanName)
      InstantiationAwareBeanPostProcessor.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  5. Beanの初期化


    • WrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);の前に Bean を初期化します。=>

      1. InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(result, beanName); initメソッド @PostConstruct
      2. ApplicationContextAwareProcessor.postProcessBeforeInitialization(result, beanName); 認識: ApplicationContextAware、ApplicationEventPublisherAware、EnvironmentAware

    • WrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);の前に Bean を初期化します。=>

      1. AbstractAutoProxyCreator.postProcessAfterInitialization(result, beanName); AOP (wrapIfNecessary->createAopProxy().getProxy(classLoader); Cglib/Jdk)
    • bean 初期化 register distroy
      registerDisposableBeanIfNecessary(beanName, bean, mbd); => destroyAwareBeanPostProcessor

  6. destroySingleton(beanName)の後に Bean を作成
    => destroyAwareBeanPostProcessor#postProcessBeforeDestruction(this.bean, this.beanName);

スプリング延長点コーミング

  • BeanFactoryポストプロセッサ
    • BeanDefinitionRegistryPostProcessor
      • postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) # Bean定義の登録
  • Beanポストプロセッサ
    • InstantiationAwareBeanPostProcessor
      • postProcessBeforeInstantiation
      • postProcessAfterInstantiation
      • postProcessProperties
      • postProcessPropertyValues
    • AbstractAutoProxyCreator
      • postProcessAfterInitialization # インスタンス化後の AOP サポート
  • @輸入
    • ImportBeanDefinitionRegistrar
      • registerBean定義
    • インポートセレクター
      • String[] selectImports(AnnotationMetadata importingClassMetadata)
      • デフォルトの述語 getExclusionFilter() { return null;}
  • わかっている
    • アプリケーションコンテキスト認識
      • 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 はどのようにしてサービスの自動登録を実現しているのでしょうか?

  1. Tomcat が起動したら、ServletWebServerApplicationContext でイベント ServletWebServerInitializedEvent を発行します。

  2. 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();
	}
}
  1. 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);
            }
        }
    }
}

おすすめ

転載: blog.csdn.net/menxu_work/article/details/128000900