spring NamedContextFactory在Fegin配置及使用详解

 更新时间:2023年11月06日 14:06:29   作者:头饰 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配置的资料请关注脚本之家其它相关文章!

相关文章

  • mybatisPlus条件构造器常用方法小结

    mybatisPlus条件构造器常用方法小结

    这篇文章主要介绍了mybatisPlus条件构造器常用方法,首先是.select和其他条件,本文结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • Java实现快速排序过程分析

    Java实现快速排序过程分析

    今天小编就为大家分享一篇关于Java实现快速排序过程分析,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • SpringBoot请求参数相关注解说明小结

    SpringBoot请求参数相关注解说明小结

    这篇文章主要介绍了SpringBoot请求参数相关注解说明,主要包括@PathVariable,@RequestHeader、@CookieValue、@RequestBody和@RequestParam,本文结合实例代码给大家讲解的非常详细,需要的朋友可以参考下
    2022-05-05
  • 详解SpringMVC中设置静态资源不被拦截的问题

    详解SpringMVC中设置静态资源不被拦截的问题

    这篇文章主要介绍了详解SpringMVC中设置静态资源不被拦截的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Java简单实现约瑟夫环算法示例

    Java简单实现约瑟夫环算法示例

    这篇文章主要介绍了Java简单实现约瑟夫环算法,简单描述了约瑟夫环问题,并结合实例形式分析了Java实现约瑟夫环的具体操作技巧,需要的朋友可以参考下
    2017-09-09
  • Java中this,static,final,const用法详解

    Java中this,static,final,const用法详解

    这篇文章主要介绍了Java中this,static,final,const用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • java Disruptor构建高性能内存队列使用详解

    java Disruptor构建高性能内存队列使用详解

    这篇文章主要为大家介绍了java Disruptor构建高性能内存队列使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • java8 对象转Map时重复 key Duplicate key xxxx的解决

    java8 对象转Map时重复 key Duplicate key xxxx的解决

    这篇文章主要介绍了java8 对象转Map时重复 key Duplicate key xxxx的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 如何将Set直接转成数组

    如何将Set直接转成数组

    这篇文章主要介绍了如何将Set直接转成数组,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • 微信支付java版V3验证数据合法性(Deom)

    微信支付java版V3验证数据合法性(Deom)

    这篇文章主要介绍了微信支付java版V3验证数据合法性(Deom)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09

最新评论