SpringCloud NamedContextFactory 代码分析

400 阅读2分钟

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。