1 NamedContextFactory抽象命名容器工厂
Creates a set of child contexts that allows a set of Specifications to define the beans in each child context.Ported from spring-cloud-netflix FeignClientFactory and SpringClientFactory
这是命名工厂的抽象父类,由spring-cloud-context提供,根据文档意思是:通过这个工厂可以创建定制化spring子容器,服务于spring-cloud-netflix的Feign和Ribbon。因为这是Ribbon和Feign的基础,先来看一下这个类的源码。
1.1 成员变量
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
implements DisposableBean, ApplicationContextAware {
// PropertySource的name属性,由子类构造方法传入
private final String propertySourceName;
// 首个property的key,由子类构造方法传入
private final String propertyName;
// 自定义的name(对于Ribbon来说就是服务名称) 映射 Spring容器
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
// 自定义的name(对于Ribbon来说就是服务名称) 映射 Specification,目的是独立Spring容器启动时,注入一些个性化的Configuration,对于Ribbon来说就是可以针对单独服务,设置IRule、IPing等
private Map<String, C> configurations = new ConcurrentHashMap<>();
// 父容器,ApplicationContextAware被调用时设置,一般就是我们普通的Spring应用,名字是application-1
private ApplicationContext parent;
// 容器启动时,注入的一个配置类,由子类构造方法传入
private Class<?> defaultConfigType;
}
1.2 唯一构造方法,必须子类实现
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType; // 一个之后会注册到子容器中的配置类
this.propertySourceName = propertySourceName;// 一个子容器中的propertySource名字
this.propertyName = propertyName;// 对于Ribbon这个是读取@RibbonClientName获取ribbon.client.name的关键,对于子容器来说是一个普通的properties中的key,对应的value就是子容器创建时传入的name
}
1.3 关键方法createContext:根据自定义name,创建Spring容器
protected AnnotationConfigApplicationContext createContext(String name) {
// 1. 先new一个AnnotationConfigApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 2. 注册name对应的自定义Configuration类
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
// 3. 注册default.开头命名的name对应的配置
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
// 4. 注册PropertyPlaceholderAutoConfiguration和构造方法传入的默认配置类
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
// 5. PropertySource初始化
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
// 6. 设置父容器
if (this.parent != null) {
context.setParent(this.parent);
}
// 7. 刷新容器
context.refresh();
return context;
}
2 SpringClientFactory-Ribbon命名容器工厂实现
SpringClientFactory是作为ribbon的自动配置项引入的,主要目的有几个
- 根据service名称,创建不同的容器,使不同的服务可以使用不同的配置,比如不同的负载均衡策略。这是NamedContextFactory抽象父类主要提供的功能。
- 通过IClientConfig,反射实例化一些bean。这是Ribbon命名工厂的特殊的功能。
2.1 构造方法(重要)
public SpringClientFactory() {
super(RibbonClientConfiguration.class, "ribbon", "ribbon.client.name");
}
- RibbonClientConfiguration.class: 是子容器会自动注入的配置类,提供了默认的负载均衡相关的抽象概念的实现,包括IRule、ServerList等等。先大致浏览一下,它就是注册了默认bean。
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
@RibbonClientName
private String name = "client";
@Autowired
private PropertiesFactory propertiesFactory;
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 1000);
config.set(CommonClientConfigKey.GZipPayload, true);
return config;
}
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, this.name)) {
return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
} else {
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
}
...
- ribbon.client.name:这个是@RibbonClientName注解(一个@Value("${ribbon.client.name}")的注解)获取子容器name的关键
2.2 getInstance方法:根据serviceName和Bean类型获取Bean实例
public <C> C getInstance(String name, Class<C> type) {
// 1. 首先尝试从NamedContextFactory的getInstance获取实例
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
} else {
// 2. 通过反射实例化
IClientConfig config = (IClientConfig)this.getInstance(name, IClientConfig.class);
return instantiateWithConfig(this.getContext(name), type, config);
}
}
2.3 instantiateWithConfig方法:通过反射实例化对象,利用Spring容器注入依赖
static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context, Class<C> clazz, IClientConfig config) {
Object result = null;
// 1. 尝试用有IClientConfig的构造方法实例化对象,如果成功直接返回
try {
Constructor<C> constructor = clazz.getConstructor(IClientConfig.class);
result = constructor.newInstance(config);
} catch (Throwable var5) {
}
// 2. 无参构造实例化对象
if (result == null) {
result = BeanUtils.instantiateClass(clazz);
// 2-1. IClientConfigAware的initWithNiwsConfig方法执行
if (result instanceof IClientConfigAware) {
((IClientConfigAware)result).initWithNiwsConfig(config);
}
// 2-2. 利用Spring容器做依赖注入
if (context != null) {
context.getAutowireCapableBeanFactory().autowireBean(result);
}
}
return result;
}
2.4 工具方法
提供一些获取ILoadBalancer、IClientConfig、RibbonLoadBalancerContext、IClient的包装方法,主要还是调用getInstance,例如:
public ILoadBalancer getLoadBalancer(String name) {
return (ILoadBalancer)this.getInstance(name, ILoadBalancer.class);
}
3 实现一个自己的NamedContextFactory
目标:实现一个自己的NamedContextFactory,让不同的容器可以使用不同的配置
3.1 自定义NamedContextFactory
public class MyContextFactory extends NamedContextFactory<MyRibbonClientSpecification> {
public MyContextFactory() {
super(DefaultConfiguration.class, "myRibbon", "myRibbon.client.name");
}
}
public class MyRibbonClientSpecification implements NamedContextFactory.Specification {
private final String name;
private final Class<?>[] configuration;
public MyRibbonClientSpecification(String name, Class<?>[] configuration) {
this.name = name;
this.configuration = configuration;
}
@Override
public String getName() {
return this.name;
}
@Override
public Class<?>[] getConfiguration() {
return this.configuration;
}
}
3.2 定义一个子容器默认会注入的配置类
public class DefaultConfiguration {
@Value("${myRibbon.client.name}") // 服务名称
// @RibbonClientName
private String name = "client";
// 每个服务会有不同的配置
@Bean
public IClientConfig config() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl("myRibbon");
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 1000);
config.set(CommonClientConfigKey.GZipPayload, true);
return config;
}
// 使用ConfigurationBasedServerList,是通过读取配置文件的方式获取服务实例列表的
@Bean
@ConditionalOnMissingBean
public ServerList<Server> ribbonServerList(IClientConfig config) {
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
}
3.3 测试类
stock-service.myRibbon.listOfServers=127.0.0.1:111相当于在application.properties里加入了这行配置。stock-service是创建子容器时传入的name,代表服务名称。myRibbon是DefaultClientConfigImpl config = new DefaultClientConfigImpl("myRibbon")传入的名称空间。listOfServers是Ribbon自己的配置属性,ConfigurationBasedServerList会读取这个配置,来获取ServerList。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class},
value={"stock-service.myRibbon.listOfServers=127.0.0.1:111", "stock-service.ribbon.listOfServers=127.0.0.1:456"})
public class SpringClientFactoryTest {
@Autowired
private MyContextFactory myContextFactory;
@Autowired
private SpringClientFactory springClientFactory;
@Test
public void testMyContextFactory() {
// 1. 自定义ContextFactory
ServerList serverList1 = myContextFactory.getInstance("stock-service", ServerList.class);
System.out.println(serverList1.getInitialListOfServers()); // 127.0.0.1:111
// 2. springClientFactory
ServerList serverList2 = springClientFactory.getInstance("stock-service", ServerList.class);
System.out.println(serverList2.getInitialListOfServers()); // 127.0.0.1:456
}
}
附ConfigurationBasedServerList部分源码
public class ConfigurationBasedServerList extends AbstractServerList<Server> {
private IClientConfig clientConfig;
@Override
public List<Server> getInitialListOfServers() {
return getUpdatedListOfServers();
}
// 获取服务列表,通过配置文件里的<clientName>.<nameSpace>.listOfServers
@Override
public List<Server> getUpdatedListOfServers() {
String listOfServers = clientConfig.get(CommonClientConfigKey.ListOfServers);
return derive(listOfServers);
}
// 配置ConfigurationBasedServerList时会主动调用
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.clientConfig = clientConfig;
}