目录
概述
总结了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;
}