文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring Framework&Spring Boot集成apollo源码分析

2024-11-30 22:48

关注

由于Spring Framework和Spring Boot集成apollo的方式有些许不同,分别进行分析。

Spring Framework

示例

Spring XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.ctrip.com/schema/apollo
http://www.ctrip.com/schema/apollo.xsd"
>
<apollo:config namespaces="application,public_namespace"/>
<context:component-scan base-package="test.springframework"/>

<bean id="test" class="test.springframework.Test">
<property name="test1" value="${test1}"/>
bean>
beans>

Spring Bean

package test.springframework;
public class Test {
@ApolloConfig
private Config config;
@Value("${test2:}")
private String test2;
private String test1;
public String getTest1(){
return test1;
}
public void setTest1(String test1){
this.test1 = test1;
}
@ApolloConfigChangeListener
public void onChange(ConfigChangeEvent changeEvent){
System.out.println(changeEvent);
}
}

集成分析

refresh时序图

图中概括地描述了refresh过程,其中标识黄颜色的地方是这次分析的重点,下面分别进行描述。

解析apollo:config

当解析xml文件apollo:config标记的时候调用BeanDefinitionParserDelegate.parseCustomElement(…),主要流程如下:

个性化命名空间解析

图中流程主要完成了两件事:

调用BeanFactoryPostProcessor

apollo中ConfigPropertySourcesProcessor实现了Spring接口BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor,ConfigPropertySourcesProcessor实现如下:

public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor
implements BeanDefinitionRegistryPostProcessor {
//使用java SPI机制,ConfigPropertySourcesProcessorHelper对应的实现类是DefaultConfigPropertySourcesProcessorHelper,这段代码完成了apollo client初始化
private ConfigPropertySourcesProcessorHelper helper = ServiceBootstrap.loadPrimary(ConfigPropertySourcesProcessorHelper.class);

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
helper.postProcessBeanDefinitionRegistry(registry);
}
}

调用BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry

实际调用的是
DefaultConfigPropertySourcesProcessorHelper.postProcessBeanDefinitionRegistry方法,该方法主要完成以下几件事:

调用BeanFactoryPostProcessor.postProcessBeanFactory

PropertySourcesProcessor

CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
ImmutableSortedSet<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
Iterator<Integer> iterator = orders.iterator();

while (iterator.hasNext()) {
int order = iterator.next();
for (String namespace : NAMESPACE_NAMES.get(order)) {
Config config = ConfigService.getConfig(namespace);
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
}
NAMESPACE_NAMES.clear();
environment.getPropertySources().addFirst(composite);
AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(environment, beanFactory);

List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
for (ConfigPropertySource configPropertySource : configPropertySources) {
configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
}

PropertySourcesPlaceholderConfigurer

new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
new PropertySourcesPropertyResolver(this.propertySources)
StringValueResolver valueResolver = strVal -> {
String resolved = (ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(nullValue) ? null : resolved);
};
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
if (beanDefinition.hasPropertyValues()) {
visitPropertyValues(beanDefinition.getPropertyValues());
}
if (beanDefinition.hasConstructorArgumentValues()) {
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
}

@Nullable
protected String resolveStringValue(String strVal) {
if (this.valueResolver == null) {
throw new IllegalStateException("No StringValueResolver specified - xxx");
}
// 最终调用的是PropertySourcesPropertyResolver中的resolveXXX方法String resolvedValue = this.valueResolver.resolveStringValue(strVal);
return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
}

调用BeanPostProcessor

ApolloAnnotationProcessor

用于解析apollo注解ApolloConfig、
ApolloConfigChangeListener。

ApolloConfig

为注解为ApolloConfig的字段赋值对应的namespace Config对象。

protected void processField(Object bean, String beanName, Field field) {
ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
if (annotation == null) {
return;
}

Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()),
"Invalid type: %s for field: %s, should be Config", field.getType(), field);

String namespace = annotation.value();
Config config = ConfigService.getConfig(namespace);

ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, config);
}

ApolloConfigChangeListener

为注解为ApolloConfigChangeListener的方法添加namespace的监听。

ApolloConfigChangeListener annotation = AnnotationUtils
.findAnnotation(method, ApolloConfigChangeListener.class);
String[] namespaces = annotation.value();
ConfigChangeListener configChangeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
ReflectionUtils.invokeMethod(method, bean, changeEvent);
}
};
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace);
config.addChangeListener(configChangeListener);
}

SpringValueProcessor

protected void processField(Object bean, String beanName, Field field) {
Value value = field.getAnnotation(Value.class);
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
springValueRegistry.register(beanFactory, key, springValue);
}
}
protected void processMethod(Object bean, String beanName, Method method) {
Value value = method.getAnnotation(Value.class);
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
springValueRegistry.register(beanFactory, key, springValue);
}
}

ApolloJsonValueProcessor

protected void processField(Object bean, String beanName, Field field) {
ApolloJsonValue apolloJsonValue = AnnotationUtils.getAnnotation(field, ApolloJsonValue.class);
String placeholder = apolloJsonValue.value();
Object propertyValue = placeholderHelper
.resolvePropertyValue(beanFactory, beanName, placeholder);
boolean accessible = field.isAccessible();
field.setAccessible(true);
ReflectionUtils
.setField(field, bean, parseJsonValue((String)propertyValue, field.getGenericType()));
field.setAccessible(accessible);
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
for (String key : keys) {
SpringValue springValue = new SpringValue(key, placeholder, bean, beanName, field, true);
springValueRegistry.register(beanFactory, key, springValue);
}
}
}
protected void processMethod(Object bean, String beanName, Method method) {
ApolloJsonValue apolloJsonValue = AnnotationUtils.getAnnotation(method, ApolloJsonValue.class);
String placeHolder = apolloJsonValue.value();
Object propertyValue = placeholderHelper
.resolvePropertyValue(beanFactory, beanName, placeHolder);
boolean accessible = method.isAccessible();
method.setAccessible(true);
ReflectionUtils.invokeMethod(method, bean, parseJsonValue((String)propertyValue, types[0]));
method.setAccessible(accessible);

if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeHolder);
for (String key : keys) {
SpringValue springValue = new SpringValue(key, apolloJsonValue.value(), bean, beanName,method, true);
springValueRegistry.register(beanFactory, key, springValue);
}
}
}

Spring Boot

示例

// Spring Boot启动类
@SpringBootApplication
@EnableApolloConfig
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
//测试Bean见Spring Framework部分示例

上面代码中的@EnableApolloConfig是一个接入点,后面会分析到。

集成分析

Spring Boot启动流程

图中概括地描述了SpringApplication启动过程(其中会将启动类加入到spring容器中),其中标识黄颜色的地方是这次分析的重点,下面分别进行描述。

加载apollo ApplicationContextInitializer

setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 通过SpringFactoriesLoader加载、解析META-INF/spring.factories
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

apollo ApplicationContextInitializer initialize

protected void initialize(ConfigurableEnvironment environment) {
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
for (String namespace : namespaceList) {
Config config = ConfigService.getConfig(namespace);

composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}

environment.getPropertySources().addFirst(composite);
}

初始化BeanDefinitionLoader

我们关注BeanDefinitionLoader类中属性annotatedReader=new
AnnotatedBeanDefinitionReader(registry),AnnotatedBeanDefinitionReader构造方法如下:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 将Annotation相关的处理类注册到Spring容器中,如:// ConfigurationClassPostProcessor : 处理Configuration注解// AutowiredAnnotationBeanPostProcessor : 处理Value注解// RequiredAnnotationBeanPostProcessor : 处理Required注解// CommonAnnotationBeanPostProcessor : 处理PostConstruct/PreDestroy/Resource/Lazy注解// ... ...
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

与我们这次分析相关的类是:
ConfigurationClassPostProcessor,该类实现了BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口。

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry

ImportResource、Bean注解,没有则直接返回;

ImportResource、Bean注解进行解析。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 在上面parse阶段会将Import封装在ConfigurationClass中
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
int order() default Ordered.LOWEST_PRECEDENCE;
}
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata)
registrar.registerBeanDefinitions(metadata, this.registry));
}
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {

private ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
// 该方法中将apollo相关类注册到spring容器中,如:// PropertySourcesProcessor// ApolloAnnotationProcessor// SpringValueProcessor// SpringValueDefinitionProcessor// ApolloJsonValueProcessor
helper.registerBeanDefinitions(importingClassMetadata, registry);
}
}

总结

​文章主要介绍了apollo借助Spring扩展点完成了与Spring的集成:Spring Framework集成方式使用到了loadBeanDefinitions阶段中apollo:config NamespaceHandler,BeanDefinitionRegistryPostProcessor,BeanFactoryPostProcessor,BeanPostProcessor;Spring Boot集成方式使用到了ApplicationContextInitializer,BeanDefinitionRegistryPostProcessor,BeanFactoryPostProcessor,BeanPostProcessor;

apollo还提供了通过注解类ApolloAutoConfiguration与Spring Boot完成集成,原理是一致的。

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯