Spring扩展点在SpringCloud微服务组件中的应用(一)

目录

概述

(一)针对Bean自身扩展

Bean扩展

AbstractAutoProxyCreator


 

概述

总结了spring的常用扩展点的,大致可以分为两类:一是针对bean本身去扩展的;二是针对bean生命周期植入的扩展点扩展。

思维导图 

结合下面的spring主体流程梳理图解,可以更方便我们理解spring框架的在伸缩扩展设计的魅力。

 

 spring主体流程梳理图

根据上面的思维导图,结合我们常用的微服务组件源码,将在下面一一分析这些扩展点是如何运用的。以后,如果需要将自己的框架或者三方框架,跟sping进行整合,就可以根据实际使用场景,采用合适的扩展点来黏合到spring框架中去。

(一)针对Bean自身扩展

  • Bean扩展

ImportBeanDefinitionRegistrar

我们在使用的feign时候,直接声明feign client就可以很方便的在spring容器中使用。那么你一定会好奇,feign它是如何将接口注入spring bean容器中的呢?

在我们通过@EnableFeignClients注解开启feign client的时候,就开启了向bean 容器中注入feign client。

通过@Import(FeignClientsRegistrar.class)注解注入FeignClientsRegistrar类。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients

FeignClientsRegistrar的工作就是,扫描包获取有@FeignClient注解的类,根据feign定义的工厂bean FeignClientFactoryBean获取beanDefinition,然后获取@FeignClien注解信息封装到beanDefinition中,后续会生成代理对象然后获取http client api,根据@FeignClien注解信息完成rpc调用。

class FeignClientsRegistrar
		implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}


	private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		validate(attributes);
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		String contextId = getContextId(attributes);
		definition.addPropertyValue("contextId", contextId);
		definition.addPropertyValue("type", className);
		definition.addPropertyValue("decode404", attributes.get("decode404"));
		definition.addPropertyValue("fallback", attributes.get("fallback"));
		definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

		String alias = contextId + "FeignClient";
		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

		boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
																// null

		beanDefinition.setPrimary(primary);

		String qualifier = getQualifier(attributes);
		if (StringUtils.hasText(qualifier)) {
			alias = qualifier;
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] { alias });
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

}

ImportSelector

ImportSelector扩展点的使用,以kafak组件为例,讲解下是如何将拥有@KafkaListener注解方法注入到spring bean容器中的。

 如果引入了kafak对应的jar包,spring boot 会自动装配EnableKafka类。

 EnableKafka类引入KafkaListenerConfigurationSelector类


@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({KafkaListenerConfigurationSelector.class})
public @interface EnableKafka {
}

 KafkaListenerConfigurationSelector实现了ImportSelector接口,通过重写selectImports方法,引入kafka的启动配置类KafkaBootstrapConfiguration。

@Order
public class KafkaListenerConfigurationSelector implements DeferredImportSelector {
    public KafkaListenerConfigurationSelector() {
    }

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{KafkaBootstrapConfiguration.class.getName()};
    }
}

 

 spring规定通过selectImports引入的类是需要加@Configuration注解的,所以很多使用selectImports场景是作为配置启动类,加载我们需要注入到spring容器的入口类

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

 通过KafkaBootstrapConfiguration全局启动配置类,显示向spring容器注入了KafkaListenerAnnotationBeanPostProcessor和KafkaListenerEndpointRegistry这两个类。

@Configuration
public class KafkaBootstrapConfiguration {

	@SuppressWarnings("rawtypes")
	@Bean(name = KafkaListenerConfigUtils.KAFKA_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public KafkaListenerAnnotationBeanPostProcessor kafkaListenerAnnotationProcessor() {
		return new KafkaListenerAnnotationBeanPostProcessor();
	}

	@Bean(name = KafkaListenerConfigUtils.KAFKA_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
	public KafkaListenerEndpointRegistry defaultKafkaListenerEndpointRegistry() {
		return new KafkaListenerEndpointRegistry();
	}

}

KafkaListenerAnnotationBeanPostProcessor类:主要功能是扫描@KafkaListener注解类,然后将其注入到spring 容器中;

KafkaListenerEndpointRegistry类:是kafka cosumer的消费者注册监听和消息业务逻辑处理。有兴趣的同学可以仔细看看源码。

根据ImportSelector当前扩展点,我们可以合理的引入我们需要集成三方框架。

参考demo:公共组件分布式锁抽取并和spring框架集成_张家老院子的博客-CSDN博客

 

  • AbstractAutoProxyCreator

alibaba的分布式事务组件seata框架就实现了AbstractAutoProxyCreator接口

 AbstractAutoProxyCreator类重写了wrapIfNecessary方法,并为需要代理的类生成了interceptor拦截类,将需要分布式事务处理的本地事务拦截下来进行seata内部的业务处理。

   @Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (disableGlobalTransaction) {
            return bean;
        }
        try {
            synchronized (PROXYED_SET) {
                if (PROXYED_SET.contains(beanName)) {
                    return bean;
                }
                interceptor = null;
                //check TCC proxy
                if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                    //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                    interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                } else {
                    Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                    Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);

                    if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                        return bean;
                    }

                    if (interceptor == null) {
                        interceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                        ConfigurationFactory.getInstance().addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener) interceptor);
                    }
                }

                LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
                if (!AopUtils.isAopProxy(bean)) {
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    for (Advisor avr : advisor) {
                        advised.addAdvisor(0, avr);
                    }
                }
                PROXYED_SET.add(beanName);
                return bean;
            }
        } catch (Exception exx) {
            throw new RuntimeException(exx);
        }
    }

那么是怎么让spring调用wrapIfNecessary方法的呢?在spring context刷新过程中会调用到wrapIfNecessar()方法。

又重写getAdvicesAndAdvisorsForBean方法,将interceptor拦截器加入到通知队列中,这样就能拦截我们的目标方法了。

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource)
        throws BeansException {
        return new Object[]{interceptor};
    }

getAdvicesAndAdvisorsForBean方法是通过 bean的后置处理器来完成调用的。下面代码是为目标类创建创建代理对象,并且并注入获取拦截器interceptor。

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知始行末

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值