Spring Cloud(十三):Spring 扩展
创始人
2024-02-10 10:26:06
  • Spring扩展点
    • Bean的生命周期中的BeanPostProcessor扩展点
    • Spring扩展点梳理
  • Spring扩展点应用场景
    • 整合Nacos 服务注册
      • ApplicationListener扩展场景——监听容器中发布的事件
      • Lifecycle Nacos 发布订阅 & Eureka 服务启动、同步、剔除
      • Lifecycle扩展场景——管理具有启动、停止生命周期需求的对象
    • 整合Nacos 发布订阅 NacosWatch
    • 整合Eureka 注册
      • 扩展: Eureka Server端上下文的初始化是在SmartLifecycle#start中实现的
    • 整合Ribbon
      • SmartInitializingSingleton扩展场景—— 对容器中的Bean对象进行定制处理
    • 整合Feign
      • FactoryBean的扩展场景——将接口生成的代理对象交给Spring管理
    • 整合sentinel
      • HandlerInterceptor扩展场景——对mvc请求增强
      • SmartInitializingSingleton&FactoryBean结合场景——根据类型动态装配对象
    • 整合seata
      • AbstractAutoProxyCreator&MethodInterceptor结合场景——实现方法增强

Spring扩展点

Bean的生命周期中的BeanPostProcessor扩展点

  1. bean create before
    resolveBeforeInstantiation(beanName, mbdToUse); => InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(beanClass, beanName);

  2. bean instance before
    determineConstructorsFromBeanPostProcessors(beanClass, beanName); => SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors(beanClass, beanName); @Autowired 构造器

  3. bean instance after

    • bean instance after merge bean query
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); => MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(mbd, beanType, beanName);

      1. AutowiredAnnotationBeanPostProcessor @Autowired @Value
      2. CommonAnnotationBeanPostProcessor @Resource @PostConstruct @PreDestroy
    • bean instance after earlybean
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); => SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference(exposedObject, beanName);

  4. bean field setting
    属性填充 populateBean(beanName, mbd, instanceWrapper);

    • 属性填充 before => InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)
    • 属性填充 after => field属性 和 setter方式
      InstantiationAwareBeanPostProcessor.postProcessProperties(pvs, bw.getWrappedInstance(), beanName)
      InstantiationAwareBeanPostProcessor.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  5. bean initialize

    • bean initialize before
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); =>

      1. InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(result, beanName); initmethod @PostConstruct
      2. ApplicationContextAwareProcessor.postProcessBeforeInitialization(result, beanName); Aware: ApplicationContextAware、ApplicationEventPublisherAware、EnvironmentAware
    • bean initialize before
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); =>

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

  6. bean create after
    destroySingleton(beanName) => DestructionAwareBeanPostProcessor#postProcessBeforeDestruction(this.bean, this.beanName);

Spring扩展点梳理

  • BeanFactoryPostProcessor
    • BeanDefinitionRegistryPostProcessor
      • postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) # 注册Bean 定义
  • BeanPostProcessor
    • InstantiationAwareBeanPostProcessor
      • postProcessBeforeInstantiation
      • postProcessAfterInstantiation
      • postProcessProperties
      • postProcessPropertyValues
    • AbstractAutoProxyCreator
      • postProcessAfterInitialization # 实例化之后AOP支持
  • @Import
    • ImportBeanDefinitionRegistrar
      • registerBeanDefinitions
    • ImportSelector
      • String[] selectImports(AnnotationMetadata importingClassMetadata)
      • default Predicate getExclusionFilter() { return null;}
  • Aware
    • ApplicationContextAware
      • setApplicationContext(ApplicationContext applicationContext)
    • BeanFactoryAware
      • setBeanFactory(BeanFactory beanFactory)
  • InitializingBean || @PostConstruct
  • FactoryBean (动态创建Bean)(&beanName factoryBean.getObject | beanName beanFactory.getBean)(Mybatis#mapper、Openfeign)
    • T getObject() throws Exception;
    • Class getObjectType();
    • default boolean isSingleton() { return true; }
  • SmartInitializingSingleton Bean
    • void afterSingletonsInstantiated(); # 初始化之后调用 过滤器…
  • ApplicationListener
    • void onApplicationEvent(E event);
  • Lifecycle
    • SmartLifecycle
      • default boolean isAutoStartup() { return true; }
      • default void stop(Runnable callback)
    • LifecycleProcessor
  • HandlerInterceptor
    • default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    • default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception
    • default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception
  • MethodInterceptor
    • Object invoke(MethodInvocation invocation) throws Throwable;

Spring扩展点应用场景

整合Nacos 服务注册

ApplicationListener扩展场景——监听容器中发布的事件

NamingService -> NacosNamingService => registerInstance

思考: 为什么整合Nacos注册中心后,服务启动就会自动注册,Nacos是如何实现自动服务注册的?

  1. Tomcat启动之后, 在ServletWebServerApplicationContext中,发布事件 ServletWebServerInitializedEvent

  2. spring-cloud-commons 包 AbstractAutoServiceRegistration 监听 WebServerInitializedEvent

public abstract class AbstractAutoServiceRegistrationimplements AutoServiceRegistration, ApplicationContextAware,ApplicationListener {public void onApplicationEvent(WebServerInitializedEvent event) {bind(event); // .... register();}
}
  1. Nacos 实现ServiceRegistry.register

Lifecycle Nacos 发布订阅 & Eureka 服务启动、同步、剔除

Lifecycle扩展场景——管理具有启动、停止生命周期需求的对象

start => AbstractApplicationContext.refresh -> finishRefresh() -> getLifecycleProcessor().onRefresh(); -> start()

整合Nacos 发布订阅 NacosWatch

public class NacosWatchimplements ApplicationEventPublisherAware, SmartLifecycle, DisposableBean {@Overridepublic 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中实现的

EurekaServerInitializerConfiguration

整合Ribbon

SmartInitializingSingleton扩展场景—— 对容器中的Bean对象进行定制处理

所有的单例bean实例化之后调用

思考:为什么@Bean修饰的RestTemplate加上@LoadBalanced就能实现负载均衡功能?

@Bean
@LoadBalanced
public RestTemplate restTemplate() {return new RestTemplate();
}

LoadBalancerAutoConfiguration
对SmartInitializingSingleton的扩展,为所有用@LoadBalanced修饰的restTemplate(利用了@Qualifier 限定符)绑定实现了负载均衡逻辑的拦截器LoadBalancerInterceptor

@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider> 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 {
}

LoadBalancerInterceptor

	@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {@Beanpublic LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}

整合Feign

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(); //
}

整合sentinel

HandlerInterceptor扩展场景——对mvc请求增强

AbstractSentinelInterceptor#preHandle

public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {...@Overridepublic 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 {...@Overridepublic NacosDataSource getObject() throws Exception {...return new NacosDataSource(properties, groupId, dataId, converter);}...
}

SentinelDataSourceHandler

public class SentinelDataSourceHandler implements SmartInitializingSingleton {...@Overridepublic void afterSingletonsInstantiated() {...//sentinel.nacos.config.serverAddr=${sentinel.nacos.config.serverAddr}//NacosDataSourceAbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties.getValidDataSourceProperties();abstractDataSourceProperties.setEnv(env);abstractDataSourceProperties.preCheck(dataSourceName);registerBean(abstractDataSourceProperties, dataSourceName+ "-sentinel-" + validFields.get(0) + "-datasource"); //NacosDataSource...}...
}

整合seata (Spring Aop)

AbstractAutoProxyCreator&MethodInterceptor结合场景——实现方法增强

GlobalTransactionScanner

在这里插入图片描述

GlobalTransactionalInterceptor

在这里插入图片描述

public class GlobalTransactionalInterceptor implements ConfigurationChangeListener, MethodInterceptor, SeataInterceptor {...@Overridepublic 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 transactionInfoTransactionInfo 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 transactionbreak;case SUPPORTS:// If transaction is not existing, execute without transaction.if (notExistingTransaction(tx)) {return business.execute();}// Continue and execute with new transactionbreak;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 holderGlobalLockConfig 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 Businessrs = 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. clearresumeGlobalLockConfig(previousConfig);triggerAfterCompletion();cleanUp();}} finally {// If the transaction is suspended, resume it.if (suspendedResourcesHolder != null) {tx.resume(suspendedResourcesHolder);}}}
}

相关内容

热门资讯

猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...