java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > spring NamedContextFactory Fegin配置

spring NamedContextFactory在Fegin配置及使用详解

作者:头饰 tiger1000

在我们日常项目中,使用FeignClient实现各系统接口调用变得更加简单, 在各个系统集成过程中,难免会遇到某些系统的Client需要特殊的配置、返回读取等需求。Feign使用NamedContextFactory来为每个Client模块构造单独的上下文(ApplicationContext)

引言

文中代码来源于spring-cloud的2.2.9.release版本。

1、NamedContextFactory

看一下NamedContextFactory的主要代码。NamedContextFactory中有个contexts属性,contexts是Map<String, AnnotationConfigApplicationContext>,这个map以name为key 以子上线文为value。

getContext方法会从contexts查找name为key的子上下文,如果没有的话会调用createContext创建一个子上下文。
createContext方法是NamedContextFactory的重要方法之一。

createContext首选创建一个AnnotationConfigApplicationContext作为子上下文;然后查询configurations中有无以此name为key的Specification(这个类稍后介绍),如果有相关的Specification的话就会注册这个
Specification存储的class到子上下文;

然后注册configurations中包含的以"defalut."开头的key的Specification中存储的class到子上下文;紧接着在子上下文注册了PropertyPlaceholderAutoConfiguration 和 defaultConfigType(构造方法中初始的变量);

紧接着注册了一个名字为propertySourceName的MapPropertySource,MapPropertySource的内容包括一个key为propertyName值为name的变量;后边进行设置parent然后refresh子上下文。

可以看到NamedContextFactory可以通过getContext(name)并为每个模块构造不同的上下文,并且加载相关的配置加载到子上下文。

使用时可以通过Specification就是存储了模块名称和每个模块的配置类,进行配置,也可以通过NamedContextFactory指定一个配置类。

后边的getInstance等方法,都是通过查询子上下文中的来加载bean。

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
        implements DisposableBean, ApplicationContextAware {
   private final String propertySourceName;
   private final String propertyName;
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
private Map<String, C> configurations = new ConcurrentHashMap<>();
private Class<?> defaultConfigType;
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
            String propertyName) {
        this.defaultConfigType = defaultConfigType;
        this.propertySourceName = propertySourceName;
        this.propertyName = propertyName;
    }
  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);
    }
    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;
    }
public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        try {
            return context.getBean(type);
        }
        catch (NoSuchBeanDefinitionException e) {
            // ignore
        }
        return null;
    }
public void setConfigurations(List<C> configurations) {
        for (C client : configurations) {
            this.configurations.put(client.getName(), client);
        }
    }
}
public interface Specification {
        String getName();
        Class<?>[] getConfiguration();
    }

2、NamedContextFactory在feign中使用

feign中提供了Specification的实现

class FeignClientSpecification implements NamedContextFactory.Specification {
    private String name;
    private Class<?>[] configuration;
}

EnableFeignClients中import了FeignClientsRegistrar,下面代码不是全部的EnableFeignClients代码,做了简化处理。

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients中import了 {
Class&lt;?&gt;[] defaultConfiguration() default {};
}

具体client的配置注册是在FeignClientsRegistrar 实现的。registerBeanDefinitions方法实现了具体注册。

registerDefaultConfiguration 将EnableFeignClients中的defaultConfiguration的配置,以"defalut."开头的名称注入到了容器。

registerFeignClients扫描所有的@FeignClient类,得到name(具体逻辑可参考源码),得到FeignClient的configration属性,再调用registerClientConfiguration 就行配置(注册了name + "." + FeignClientSpecification.class.getSimpleName()的bean)。

class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
 
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        registerDefaultConfiguration(metadata, registry);
        registerFeignClients(metadata, registry);
    }

    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }
}

在FeignAutoConfiguration中引入了所有的 FeignClientSpecification,并且初始化了FeiginContext,调用了setConfigurations(参考NamedContextFactory方法,此操作将以name为key,FeignClientSpecification对象为value添加到NamedContextFactory的configurations属性中),后续在调用到NamedContextFactory的createContext方法时,会将configurations属性中的配置进行加载。

public class FeignAutoConfiguration {
    @Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
    FeignContext context = new FeignContext();
    context.setConfigurations(this.configurations);
    return context;
    }
}

以上就是spring NamedContextFactory在Fegin配置及使用详解的详细内容,更多关于spring NamedContextFactory Fegin配置的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文