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配置的资料请关注脚本之家其它相关文章!

相关文章

  • Spring jpa和mybatis整合遇到的问题解析

    Spring jpa和mybatis整合遇到的问题解析

    有朋友说jpa相比mybatis太难用,多表联合的查询写起来也比较费劲,所以便加入了mybatis的支持,在配置jpa时遇到各种问题,需要修改相关配置文件,下面小编给大家分享下修改配置文件的思路,感兴趣的朋友参考下
    2016-10-10
  • ReentrantReadWriteLock 读写锁分析总结

    ReentrantReadWriteLock 读写锁分析总结

    这篇文章主要介绍了ReentrantReadWriteLock 读写锁分析总结,ReentranReadWriteLock中有两把锁,一把读锁,一把写锁,关于这两把锁的介绍,需要的小伙伴可以参考一下
    2022-05-05
  • Java SpringBoot整合JSP和MyBatis

    Java SpringBoot整合JSP和MyBatis

    这篇文章主要介绍了SpringBoot如何整合JSP和MyBatis以及SpringBoot的基本设置,感兴趣的小伙伴可以参考阅读
    2023-03-03
  • 解决IDEA2020 创建maven项目没有src/main/java目录和webapp目录问题

    解决IDEA2020 创建maven项目没有src/main/java目录和webapp目录问题

    这篇文章主要介绍了IDEA2020 创建maven项目没有src/main/java目录和webapp目录问题解决方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • JVM类运行机制实现原理解析

    JVM类运行机制实现原理解析

    这篇文章主要介绍了JVM类运行机制实现原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • java实现超市商品库存管理平台

    java实现超市商品库存管理平台

    这篇文章主要为大家详细介绍了java实现超市商品库存管理平台,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • IDEA @SpringBootApplication报错原因及解决

    IDEA @SpringBootApplication报错原因及解决

    这篇文章主要介绍了IDEA @SpringBootApplication报错原因及解决方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 详解JAVA 常量池

    详解JAVA 常量池

    这篇文章主要介绍了JAVA 常量池的相关资料,文中讲解非常详细,示例代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • SpringBoot浅析安全管理之高级配置

    SpringBoot浅析安全管理之高级配置

    安全管理是软件系统必不可少的的功能。根据经典的“墨菲定律”——凡是可能,总会发生。如果系统存在安全隐患,最终必然会出现问题,这篇文章主要介绍了SpringBoot安全管理之高级配置
    2022-08-08
  • SpringSecurity自定义AuthenticationProvider无法@Autowire的解决

    SpringSecurity自定义AuthenticationProvider无法@Autowire的解决

    这篇文章主要介绍了SpringSecurity自定义AuthenticationProvider无法@Autowire的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12

最新评论