上面只截取了一段代码,给出了Ribbon相关的 IClientConfig客户端配置 和 某一个核心接口IRule实现类 是如何加载配置并创建的
IClientConfig
IClientConfig就是Ribbon客户端配置的接口,可以看到先是创建了DefaultClientConfigImpl默认实现类,再config.loadProperties(this.name)加载当前Client相关的配置
//## com.netflix.client.config.DefaultClientConfigImpl#loadProperties() /** * Load properties for a given client. It first loads the default values for all properties, * and any properties already defined with Archaius ConfigurationManager. */ @Override public void loadProperties(String restClientName){ enableDynamicProperties = true; setClientName(restClientName); // 1、使用Netflix Archaius的ConfigurationManager从Spring env中加载“ribbon.配置项”这类默认配置 // 如没加载到有默认静态配置 loadDefaultValues(); // 2、使用Netflix Archaius的ConfigurationManager从Spring env中加载“client名.ribbon.配置项”这类针对某个Client的配置信息 Configuration props = ConfigurationManager.getConfigInstance().subset(restClientName); for (Iterator<String> keys = props.getKeys(); keys.hasNext(); ){ String key = keys.next(); String prop = key; try { if (prop.startsWith(getNameSpace())){ prop = prop.substring(getNameSpace().length() + 1); } setPropertyInternal(prop, getStringValue(props, key)); } catch (Exception ex) { throw new RuntimeException(String.format("Property %s is invalid", prop)); } } }根据如上注释,如果你没有在项目中指定ribbon相关配置,那么会使用DefaultClientConfigImpl中的默认静态配置,如果Spring enviroment中包含“ribbon.配置项”这类针对所有Client的配置会被加载进来,有“client名.ribbon.配置项”这类针对某个Client的配置信息也会被加载进来
静态配置如下:
RibbonClient核心接口实现类配置加载及创建
上面说完IClientCOnfig配置项是如何加载的,按道理说其中已经包含了当前RibbonClient使用哪个核心接口实现类的配置,但Spring Cloud在此处定义了自己的实现逻辑
@Autowired private PropertiesFactory propertiesFactory; @Bean @ConditionalOnMissingBean public IRule ribbonRule(IClientConfig config) { // 查看propertiesFactory是否有关于当前接口的配置,如有就使用,并创建实例返回 if (this.propertiesFactory.isSet(IRule.class, name)) { return this.propertiesFactory.get(IRule.class, config, name); } // spring cloud 默认配置 ZoneAvoidanceRule rule = new ZoneAvoidanceRule(); rule.initWithNiwsConfig(config); return rule; }下面看看PropertiesFactory的逻辑
public class PropertiesFactory { @Autowired private Environment environment; private Map<Class, String> classToProperty = new HashMap<>(); public PropertiesFactory() { classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName"); classToProperty.put(IPing.class, "NFLoadBalancerPingClassName"); classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName"); classToProperty.put(ServerList.class, "NIWSServerListClassName"); classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName"); } // 查看当前clazz是否在classToProperty管理的几个核心接口之一 // 如是,查看Spring environment中是否能找到 “clientName.ribbon.核心接口配置项”的配置信息 public boolean isSet(Class clazz, String name) { return StringUtils.hasText(getClassName(clazz, name)); } public String getClassName(Class clazz, String name) { if (this.classToProperty.containsKey(clazz)) { String classNameProperty = this.classToProperty.get(clazz); String className = environment.getProperty(name + "." + NAMESPACE + "." + classNameProperty); return className; } return null; } // 也是先调用getClassName()获取Spring enviroment中配置的核心接口实现类名 // 再使用IClientConfig配置信息创建其实例 @SuppressWarnings("unchecked") public <C> C get(Class<C> clazz, IClientConfig config, String name) { String className = getClassName(clazz, name); if (StringUtils.hasText(className)) { try { Class<?> toInstantiate = Class.forName(className); return (C) instantiateWithConfig(toInstantiate, config); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Unknown class to load "+className+" for class " + clazz + " named " + name); } } return null; } }故以上面创建IRule接口实现类的逻辑