spring 配置
BeanDefinition
spring 容器中的每一个 bean 都会抽象为一个 BeanDefinition 对象,spring 容器启动后会把所有的bean 配置信息转化为 beanDefinition 对象。
spring 容器中bean 的配置方式一般有三种:
- 基于XML
- 基于 component service 注解
- java config 形式
BeanDefinitionRegistry
�beanDefinition 中,所有bean的定义都抽象为 BeanDefinitionRegistry 对象
PropertySource
用于存放Spring配置资源信息,例如spring项目中properties或者yaml文件配置信息均会保存在PropertySource对象中。Spring支持使用@PropertySource注解,將配置信息加载到Environment对象中
�
ConfigurationProperties
�可以将配置文件中的配置值直接映射到 java bean
ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar是一个接口,该接口的实现类作用于在Spring解析Bean配置生成BeanDefinition对象阶段。在Spring解析Configuration注解时,向Spring容器中增加额外的BeanDefinition。
BeanFactoryPostProcessorBean
工厂后置处理器,用于在BeanDefinition对象注册完成后,修改Bean工厂信息,例如增加或者修改BeanDefinition对象。
BeanDefinitionRegistryPostProcessor
它是一个特殊的BeanFactoryPostProcessor,用于在BeanDefinition对象注册完成后,访问、新增或者修改BeanDefinition信息。
PropertySourcesPlaceholderConfigurer
它是一个特殊的BeanFactoryPostProcessor,用于解析Bean配置中的${…}参数占位符。
BeanPostProcessor
Bean后置处理器,bean初始化方法调用前后,执行拦截逻辑,可以对原有的Bean进行包装或者根据标记接口创建代理对象。
Apollo 配置管理
EnableApolloConfig
springboot 集成 apollo 只需要使用注解:EnableApolloConfig
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(ApolloConfigRegistrar.class)public @interface EnableApolloConfig {/*** Apollo namespaces to inject configuration into Spring Property Sources.*/String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};/*** The order of the apollo config, default is {@link Ordered#LOWEST_PRECEDENCE}, which is Integer.MAX_VALUE.* If there are properties with the same name in different apollo configs, the apollo config with smaller order wins.* @return*/int order() default Ordered.LOWEST_PRECEDENCE;}
ApolloConfigRegistrar
我们主要看 ApolloConfigRegistrar 类,ApolloConfigRegistrar 实现了接口 ImportBeanDefinitionRegistrar,在spring 容器初始化的过程中,会额外的注入很多配置类
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));String[] namespaces = attributes.getStringArray("value");int order = attributes.getNumber("order");PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();// to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurerpropertySourcesPlaceholderPropertyValues.put("order", 0);BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),PropertySourcesProcessor.class);BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),ApolloAnnotationProcessor.class);BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(), SpringValueDefinitionProcessor.class);BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),ApolloJsonValueProcessor.class);}}
bean 的注入
bean 的编程式注入spring 容器
public static boolean registerBeanDefinitionIfNotExists(BeanDefinitionRegistry registry,String beanName,Class<?> beanClass,Map<String, Object> extraPropertyValues) {if (registry.containsBeanDefinition(beanName)) {return false;}String[] candidates = registry.getBeanDefinitionNames();for (String candidate : candidates) {BeanDefinition beanDefinition = registry.getBeanDefinition(candidate);if (Objects.equals(beanDefinition.getBeanClassName(), beanClass.getName())) {return false;}}BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanClass).getBeanDefinition();if (extraPropertyValues != null) {for (Map.Entry<String, Object> entry : extraPropertyValues.entrySet()) {beanDefinition.getPropertyValues().add(entry.getKey(), entry.getValue());}}registry.registerBeanDefinition(beanName, beanDefinition);return true;}
SpringValueProcessor
这个类的注释如下,主要做value注解和xml中通配符的方法或者成员变量的管理
Spring value processor of field or method which has @Value and xml config placeholders.
这个类间接实现了接口 BeanFactoryPostProcessor,BeanFactoryAware,BeanPostProcessor 接口在bean 的初始化前可以拿到spring的上下文,在bean初始化之前会执行 postProcessBeforeInitialization 方法
这个方法里面会过滤含有 value 注解的方法或者注释,覆盖原来的值
protected void processMethod(Object bean, String beanName, Method method) {//register @Value on methodValue value = method.getAnnotation(Value.class);if (value == null) {return;}//skip Configuration bean methodsif (method.getAnnotation(Bean.class) != null) {return;}if (method.getParameterTypes().length != 1) {logger.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters",bean.getClass().getName(), method.getName(), method.getParameterTypes().length);return;}Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());if (keys.isEmpty()) {return;}for (String key : keys) {SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);springValueRegistry.register(beanFactory, key, springValue);logger.info("Monitoring {}", springValue);}}
容器中的bean 初始化前都会执行 postProcessBeforeInitialization 方法,这个方法里面拿到了每个bean 的成员变量和方法,然后判断方法上是否存在 value 注解,如果存在会将属性值维护到下面的map, 属性值更新时,也是从这个map中获取
Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap();
配置动态更新
如果应用在运行时,apollo 配置动态更新,会有listener 回调机制,最终会调用 AutoUpdateConfigChangeListener 的 updateSpringValue 方法,会定位到某个bean 的某个方法或者属性,
最终通过反射修改属性或者方法
public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {if (isField()) {injectField(newVal);} else {injectMethod(newVal);}}private void injectField(Object newVal) throws IllegalAccessException {Object bean = beanRef.get();if (bean == null) {return;}boolean accessible = field.isAccessible();field.setAccessible(true);field.set(bean, newVal);field.setAccessible(accessible);}private void injectMethod(Object newVal)throws InvocationTargetException, IllegalAccessException {Object bean = beanRef.get();if (bean == null) {return;}methodParameter.getMethod().invoke(bean, newVal);}
�
