SpringCloud中NamedContextFactory这个类可以实现子容器。Ribbon为每个ServiceName都拥有自己的Spring Context和Bean实例(不同服务之间的LoadBalancer和其依赖的Bean都是完全隔离的)。
SpringCloud的这个父子容器的实现原理与Spring的父子容器原理是一样的。先搞懂Spring的父子容器的原理与使用,springCloud这个理解起来要简单很多。
先看一个NamedContextFactory的例子
import lombok.ToString;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.context.named.NamedContextFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import java.util.Arrays;
@SpringBootTest
public class NamedContextFactoryTest {
private static final String PROPERTY_NAME = "custom.client.name";
@Test
public void context() {
// 创建 parent context
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
// parent context 的 Bean,可以被子容器继承
parent.register(ParentConfig.class);
// 初始化 parent
parent.refresh();
// 创建子容器
CustomSpecification aServiceSpec = new CustomSpecification("AService", new Class[]{AServiceConfig.class});
CustomSpecification bServiceSpec = new CustomSpecification("BService", new Class[]{BServiceConfig.class});
// 默认使用配置 commonConfig
CustomNamedContextFactory customNamedContextFactory = new CustomNamedContextFactory(CommonConfig.class);
// SpringBoot 中无需手动设置,会自动注入 parent
customNamedContextFactory.setApplicationContext(parent);
// 添加子容器到父容器中
customNamedContextFactory.setConfigurations(Arrays.asList(aServiceSpec, bServiceSpec));
// 准备工作完成,现在开始通过 NamedContextFactory get Bean
ParentBean aParentBean = customNamedContextFactory.getInstance("AService", ParentBean.class);
CommonBean aCommonBean = customNamedContextFactory.getInstance("AService", CommonBean.class);
AServiceBean aServiceBean = customNamedContextFactory.getInstance("AService", AServiceBean.class);
ParentBean bParentBean = customNamedContextFactory.getInstance("BService", ParentBean.class);
CommonBean bCommonBean = customNamedContextFactory.getInstance("BService", CommonBean.class);
BServiceBean bServiceBean = customNamedContextFactory.getInstance("BService", BServiceBean.class);
// 获取到相同的parent bean
Assertions.assertEquals(aParentBean, bParentBean);
// 获取到不同的common bean
Assertions.assertNotEquals(aCommonBean, bCommonBean);
// 获取到各自的service bean
Assertions.assertNotNull(aServiceBean);
Assertions.assertNotNull(bServiceBean);
System.out.println(aParentBean);
System.out.println(bParentBean);
System.out.println(aCommonBean);
System.out.println(bCommonBean);
System.out.println(aServiceBean);
System.out.println(bServiceBean);
}
static class CustomNamedContextFactory extends NamedContextFactory<CustomSpecification> {
public CustomNamedContextFactory(Class<?> defaultConfigType) {
super(defaultConfigType, "custom", PROPERTY_NAME);
}
}
static class CustomSpecification implements NamedContextFactory.Specification {
private String name;
private Class<?>[] configuration;
public CustomSpecification(String name, Class<?>[] configuration) {
this.name = name;
this.configuration = configuration;
}
@Override
public String getName() {
return name;
}
@Override
public Class<?>[] getConfiguration() {
return configuration;
}
}
@Configuration(proxyBeanMethods = false)
static class ParentConfig {
@Bean
public ParentBean parentBean() {
return new ParentBean();
}
}
static class ParentBean {
}
@Configuration(proxyBeanMethods = false)
static class CommonConfig {
@Bean
CommonBean commonBean(Environment environment, ParentBean parentBean) {
//在创建 NamedContextFactory 里面的子 ApplicationContext 的时候,会指定 name,这个 name 对应的属性 key 即 PROPERTY_NAME
return new CommonBean(environment.getProperty(PROPERTY_NAME), parentBean);
}
}
@ToString
static class CommonBean {
private final String name;
private final ParentBean parentBean;
public CommonBean(String name, ParentBean parentBean) {
this.name = name;
this.parentBean = parentBean;
}
}
@Configuration(proxyBeanMethods = false)
static class AServiceConfig {
@Bean
public AServiceBean aServiceBean(CommonBean commonBean) {
return new AServiceBean(commonBean);
}
}
@ToString
static class AServiceBean {
private final CommonBean commonBean;
public AServiceBean(CommonBean commonBean) {
this.commonBean = commonBean;
}
}
@Configuration(proxyBeanMethods = false)
static class BServiceConfig {
@Bean
public BServiceBean bServiceBean(CommonBean commonBean) {
return new BServiceBean(commonBean);
}
}
@ToString
static class BServiceBean {
private final CommonBean commonBean;
public BServiceBean(CommonBean commonBean) {
this.commonBean = commonBean;
}
}
}
设置父容器
customNamedContextFactory.setApplicationContext(parent);
创建子容器
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
type).length > 0) {
return context.getBean(type);
}
return null;
}
主要是getContext方法
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
看下contexts的定义
Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
该map中保存着各个子容器的定义。 每次获取子容器的时候,拿不到这个容器的时候,会创建一个子容器
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object>singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
// jdk11 issue
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
创建容器的时候会创建一个容器new AnnotationConfigApplicationContext();
CustomNamedContextFactory customNamedContextFactory = new CustomNamedContextFactory(CommonConfig.class);
static class CustomNamedContextFactory extends NamedContextFactory<CustomSpecification> {
public CustomNamedContextFactory(Class<?> defaultConfigType) {
super(defaultConfigType, "custom", PROPERTY_NAME);
}
}
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;
}
每个容器都会注册一个defaultConfigType 这个是初始化时候传入的
方法BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type) 支持父子容器,优先从当前容器中获取该type类的bean定义
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
}
HierarchicalBeanFactory支持层次结果的beanFactory。