本系列为之前系列的整顿重启版,随着我的项目的倒退以及我的项目中的应用,之前系列外面很多货色产生了变动,并且还有一些货色之前系列并没有提到,所以重启这个系列重新整理下,欢送各位留言交换,谢谢!~

spring-cloud-commons 中参考了 spring-cloud-netflix 的设计,引入了 NamedContextFactory 机制,个别用于对于不同微服务的客户端模块应用不同的 子 ApplicationContext 进行配置。

spring-cloud-commons 是 Spring Cloud 对于微服务根底组件的形象。在一个微服务中,调用微服务 A 与调用微服务 B 的配置可能不同。比较简单的例子就是,A 微服务是一个简略的用户订单查问服务,接口返回速度很快,B 是一个报表微服务,接口返回速度比较慢。这样的话咱们就不能对于调用微服务 A 和微服务 B 应用雷同的超时工夫配置。还有就是,咱们可能对于服务 A 通过注册核心进行发现,对于服务 B 则是通过 DNS 解析进行服务发现,所以对于不同的微服务咱们可能应用不同的组件,在 Spring 中就是应用不同类型的 Bean。

在这种需要下,不同微服务的客户端有不同的以及雷同的配置有不同的 Bean,也有雷同的 Bean。所以,咱们能够针对每一个微服务将他们的 Bean 所处于 ApplicationContext 独立开来,不同微服务客户端应用不同的 ApplicationContext。NamedContextFactory 就是用来实现这种机制的。

编写源码:

package com.github.hashjang.spring.cloud.iiford.service.common;import org.junit.Assert;import org.junit.Test;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.List;import java.util.Objects;public class CommonNameContextTest {    private static final String PROPERTY_NAME = "test.context.name";    @Test    public void test() {        //创立 parent context        AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();        //增加 BaseConfig 相干配置        parent.register(BaseConfig.class);        //初始化 parent        parent.refresh();        //创立 testClient1,默认配置应用 ClientCommonConfig        TestClient testClient1 = new TestClient(ClientCommonConfig.class);        //创立 service1 与 service2 以及指定对应额定的配置类        TestSpec testSpec1 = new TestSpec("service1", new Class[]{Service1Config1.class, Service1Config2.class});        TestSpec testSpec2 = new TestSpec("service2", new Class[]{Service2Config.class});        //设置 parent ApplicationContext 为 parent        testClient1.setApplicationContext(parent);        //将 service1 与 service2 的配置退出 testClient1        testClient1.setConfigurations(List.of(testSpec1, testSpec2));        BaseBean baseBean = testClient1.getInstance("service1", BaseBean.class);        System.out.println(baseBean);        //验证失常获取到了 baseBean        Assert.assertNotNull(baseBean);        ClientCommonBean commonBean = testClient1.getInstance("service1", ClientCommonBean.class);        System.out.println(commonBean);        //验证失常获取到了 commonBean        Assert.assertNotNull(commonBean);        Service1Bean1 service1Bean1 = testClient1.getInstance("service1", Service1Bean1.class);        System.out.println(service1Bean1);        //验证失常获取到了 service1Bean1        Assert.assertNotNull(service1Bean1);        Service1Bean2 service1Bean2 = testClient1.getInstance("service1", Service1Bean2.class);        System.out.println(service1Bean2);        //验证失常获取到了 service1Bean2        Assert.assertNotNull(service1Bean2);        BaseBean baseBean2 = testClient1.getInstance("service2", BaseBean.class);        System.out.println(baseBean2);        //验证失常获取到了 baseBean2 并且 baseBean2 就是 baseBean        Assert.assertEquals(baseBean, baseBean2);        ClientCommonBean commonBean2 = testClient1.getInstance("service2", ClientCommonBean.class);        System.out.println(commonBean2);        //验证失常获取到了 commonBean2 并且 commonBean 和 commonBean2 不是同一个        Assert.assertNotNull(commonBean2);        Assert.assertNotEquals(commonBean, commonBean2);        Service2Bean service2Bean = testClient1.getInstance("service2", Service2Bean.class);        System.out.println(service2Bean);        //验证失常获取到了 service2Bean        Assert.assertNotNull(service2Bean);    }    @Configuration(proxyBeanMethods = false)    static class BaseConfig {        @Bean        BaseBean baseBean() {            return new BaseBean();        }    }    static class BaseBean {}    @Configuration(proxyBeanMethods = false)    static class ClientCommonConfig {        @Bean        ClientCommonBean clientCommonBean(Environment environment, BaseBean baseBean) {            //在创立 NamedContextFactory 外面的子 ApplicationContext 的时候,会指定 name,这个 name 对应的属性 key 即 PROPERTY_NAME            return new ClientCommonBean(environment.getProperty(PROPERTY_NAME), baseBean);        }    }    static class ClientCommonBean {        private final String name;        private final BaseBean baseBean;        ClientCommonBean(String name, BaseBean baseBean) {            this.name = name;            this.baseBean = baseBean;        }        @Override        public String toString() {            return "ClientCommonBean{" +                    "name='" + name + '\'' +                    ", baseBean=" + baseBean +                    '}';        }    }    @Configuration(proxyBeanMethods = false)    static class Service1Config1 {        @Bean        Service1Bean1 service1Bean1(ClientCommonBean clientCommonBean) {            return new Service1Bean1(clientCommonBean);        }    }    static class Service1Bean1 {        private final ClientCommonBean clientCommonBean;        Service1Bean1(ClientCommonBean clientCommonBean) {            this.clientCommonBean = clientCommonBean;        }        @Override        public String toString() {            return "Service1Bean1{" +                    "clientCommonBean=" + clientCommonBean +                    '}';        }    }    @Configuration(proxyBeanMethods = false)    static class Service1Config2 {        @Bean        Service1Bean2 service1Bean2() {            return new Service1Bean2();        }    }    static class Service1Bean2 {    }    @Configuration(proxyBeanMethods = false)    static class Service2Config {        @Bean        Service2Bean service2Bean(ClientCommonBean clientCommonBean) {            return new Service2Bean(clientCommonBean);        }    }    static class Service2Bean {        private final ClientCommonBean clientCommonBean;        Service2Bean(ClientCommonBean clientCommonBean) {            this.clientCommonBean = clientCommonBean;        }        @Override        public String toString() {            return "Service2Bean{" +                    "clientCommonBean=" + clientCommonBean +                    '}';        }    }    static class TestSpec implements NamedContextFactory.Specification {        private final String name;        private final Class<?>[] configurations;        public TestSpec(String name, Class<?>[] configurations) {            this.name = name;            this.configurations = configurations;        }        @Override        public String getName() {            return name;        }        @Override        public Class<?>[] getConfiguration() {            return configurations;        }    }    static class TestClient extends NamedContextFactory<TestSpec> {        public TestClient(Class<?> defaultConfigType) {            super(defaultConfigType, "testClient", PROPERTY_NAME);        }    }}

后果输入为:

com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$BaseBean@3faf2e7dClientCommonBean{name='service1', baseBean=com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$BaseBean@3faf2e7d}Service1Bean1{clientCommonBean=ClientCommonBean{name='service1', baseBean=com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$BaseBean@3faf2e7d}}com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$Service1Bean2@4648ce9com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$BaseBean@3faf2e7dClientCommonBean{name='service2', baseBean=com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$BaseBean@3faf2e7d}Service2Bean{clientCommonBean=ClientCommonBean{name='service2', baseBean=com.github.hashjang.spring.cloud.iiford.service.common.CommonNameContextTest$BaseBean@3faf2e7d}}

代码中实现了这样一个 Context 构造:

图中的被蕴含的 ApplicationContext 能够看到外层 ApplicationContext 的 Bean,也就是通过对被蕴含的 ApplicationContext 调用 getBean(xxx) 能够获取到外层 ApplicationContext 的 Bean (其实外层就是 parent ApplicationContext),然而外层的看不到内层公有的 Bean。

在咱们的测试代码中,首先,创立了一个 AnnotationConfigApplicationContext。这个其实就是模仿了咱们平时应用 Spring 框架的时候的根外围 ApplicationContext,所以咱们将其命名为 parent。咱们向外面注册了 BaseConfigBaseConfig 外面的 BaseBean 会注册到 parent。之后咱们 建 testClient1,默认配置应用 ClientCommonConfig。如果咱们指定了 testClient1 的 parent ApplicationContext 为 parent,那么 parent 外面的 Bean 都能被 testClient1 外面的子 ApplicationContext 拜访到。而后,咱们创立 service1 与 service2 以及指定对应额定的配置类。service1 会创立 ClientCommonConfigService1Config1Service1Config2 外面配置的 Bean。service2 会创立 ClientCommonConfigService2Config 外面配置的 Bean。

NamedContextFactory 的外围办法是 public <T> T getInstance(String name, Class<T> type),通过这个办法获取 NamedContextFactory 外面的子 ApplicationContext 外面的 Bean。源码是:

NamedContextFactory.java

/** * 获取某个 name 的 ApplicationContext 外面的某个类型的 Bean * @param name 子 ApplicationContext 名称 * @param type 类型 * @param <T> Bean 类型 * @return Bean */public <T> T getInstance(String name, Class<T> type) {    //获取或者创立对应名称的 ApplicationContext    AnnotationConfigApplicationContext context = getContext(name);    try {        //从对应的 ApplicationContext 获取 Bean,如果不存在则会抛出 NoSuchBeanDefinitionException        return context.getBean(type);    }    catch (NoSuchBeanDefinitionException e) {        //疏忽 NoSuchBeanDefinitionException    }    //没找到就返回 null    return null;}protected AnnotationConfigApplicationContext getContext(String name) {    //如果 map 中不存在,则创立    if (!this.contexts.containsKey(name)) {        //避免并发创立多个        synchronized (this.contexts) {            //再次判断,避免有多个期待锁            if (!this.contexts.containsKey(name)) {                this.contexts.put(name, createContext(name));            }        }    }    return this.contexts.get(name);}//依据名称创立对应的 contextprotected AnnotationConfigApplicationContext createContext(String name) {    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();    //如果 configurations 中有对应名称的配置类,则注册之    if (this.configurations.containsKey(name)) {        for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {            context.register(configuration);        }    }    //如果 configurations 中有名称结尾为 default. 的配置类,则注册之    for (Map.Entry<String, C> entry : this.configurations.entrySet()) {        if (entry.getKey().startsWith("default.")) {            for (Class<?> configuration : entry.getValue().getConfiguration()) {                context.register(configuration);            }        }    }    //注册 PropertyPlaceholderAutoConfiguration,这样能够解析 spring boot 相干的 application 配置    //注册默认的配置类 defaultConfigType    context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);    //将以后 context 的名称,放入对应的属性中,在配置类中可能会用到    //咱们下面举得例子,就是通过 environment.getProperty() 获取了这个属性    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);        //spring boot 能够打包成一种 fatjar 的模式,将依赖的 jar 包都打入同一个 jar 包中        //fatjar 中的依赖,通过默认的类加载器是加载不正确的,须要通过定制的类加载器        //因为 JDK 11 LTS 绝对于 JDK 8 LTS 多了模块化,通过 ClassUtils.getDefaultClassLoader() 有所不同        //在 JDK 8 中获取的就是定制的类加载器,JDK 11 中获取的是默认的类加载器,这样会有问题        //所以,这里须要手动设置以后 context 的类加载器为父 context 的类加载器        context.setClassLoader(this.parent.getClassLoader());    }    //生成展现名称    context.setDisplayName(generateDisplayName(name));    context.refresh();    return context;}

咱们这一节详细分析了 Spring Cloud 的根底 NamedContextFactory,搞清楚了其中的原理,并且举了一个简略的例子。接下来咱们会详细分析下如何应用以及剖析革新一个 Spring Cloud 组件。

微信搜寻“我的编程喵”关注公众号,每日一刷,轻松晋升技术,斩获各种offer