如何给动态创建的feignClient添加configurati

2019-11-30  本文已影响0人  牧羊人刘俏

继续的深入到FeignClientFactoryBean

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }

继续的跟下去

FeignContext context = applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);

可以看到,我们根据FeignContext创建了一个feign
继续的跟代码

    protected Feign.Builder feign(FeignContext context) {
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(this.type);

        // @formatter:off
        Feign.Builder builder = get(context, Feign.Builder.class)
                // required values
                .logger(logger)
                .encoder(get(context, Encoder.class))
                .decoder(get(context, Decoder.class))
                .contract(get(context, Contract.class));
        // @formatter:on

        configureFeign(context, builder);

        return builder;
    }

可以看到,里面有个方法

configureFeign(context, builder);

继续的跟进去

    protected void configureFeign(FeignContext context, Feign.Builder builder) {
        FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
        if (properties != null) {
            if (properties.isDefaultToProperties()) {
                configureUsingConfiguration(context, builder);
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(this.name), builder);
            } else {
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(this.name), builder);
                configureUsingConfiguration(context, builder);
            }
        } else {
            configureUsingConfiguration(context, builder);
        }
    }

然后可以看到

    protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
        Logger.Level level = getOptional(context, Logger.Level.class);
        if (level != null) {
            builder.logLevel(level);
        }
        Retryer retryer = getOptional(context, Retryer.class);
        if (retryer != null) {
            builder.retryer(retryer);
        }
        ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
        if (errorDecoder != null) {
            builder.errorDecoder(errorDecoder);
        }
        Request.Options options = getOptional(context, Request.Options.class);
        if (options != null) {
            builder.options(options);
        }
        Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
                this.name, RequestInterceptor.class);
        if (requestInterceptors != null) {
            builder.requestInterceptors(requestInterceptors.values());
        }

        if (decode404) {
            builder.decode404();
        }
    }
Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
                this.name, RequestInterceptor.class);

可以在这里面看到FeignContext的getInstances方法,会返回针对特定feignclient的拦截器

public <T> Map<String, T> getInstances(String name, Class<T> type) {
    AnnotationConfigApplicationContext context = getContext(name);
    if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
            type).length > 0) {
        return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
    }
    return null;
}
继续的跟进去
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);
}
这里面用到了缓存,其中name就是feignClient的名字
然后继续的的跟进去
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);
    }
    context.setDisplayName(generateDisplayName(name));
    context.refresh();
    return context;
}
所有的操作都是基于属性
private Map<String, C> configurations = new ConcurrentHashMap<>();
所以我们可以在这个configurations上面进行hack的操作。
比如我们需要对xxx这个feignClient添加configuration,只需要在configurations这个属性添加 一条信息就可以了。
代码如下

@Component
public class FeignContextPostProcessor implements BeanPostProcessor {

@Autowired
private XmlProcessLabelContextHolder xmlProcessLabelContextHolder;


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface FeignClientConfiguration {

    String value() default "";

    Class<?>[] configuration() default {};


}


class FeignConfiguration implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        // in sync case
        if(xmlProcessLabelContextHolder.getXmlProcessLabelInfo().getHeaderInfo() == null){

            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            template.header(AuthInfo.HEADER_X_ID_TOKEN_PLAIN_K, AuthInfo.HEADER_X_ID_TOKEN_PLAIN_V);
            template.header(HttpHeaders.HOST, HttpRequestUtils.getClientHost(request));
            template.header(AuthInfo.HEADER_X_ID_TOKEN_K,request.getHeader(AuthInfo.HEADER_X_ID_TOKEN_K));

         // async case
        }else{

            template.header(AuthInfo.HEADER_X_ID_TOKEN_PLAIN_K, xmlProcessLabelContextHolder.getXmlProcessLabelInfo().getHeaderInfo().get(AuthInfo.HEADER_X_ID_TOKEN_PLAIN_K));
            template.header(HttpHeaders.HOST, xmlProcessLabelContextHolder.getXmlProcessLabelInfo().getHeaderInfo().get(HttpHeaders.HOST));
            template.header(AuthInfo.HEADER_X_ID_TOKEN_K,xmlProcessLabelContextHolder.getXmlProcessLabelInfo().getHeaderInfo().get(AuthInfo.HEADER_X_ID_TOKEN_K));

        }


    }
}



@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

    if(bean instanceof FeignContext){

        FeignContext  feignContextBean =  (FeignContext)bean;

        FeignClientConfiguration con  = AnnotationUtils.findAnnotation(FeignClientDelegate.class, FeignClientConfiguration.class);


        List bffSpecificationInnerLists = Lists.newArrayList();
        bffSpecificationInnerLists.add(new SpecificationInner(con.value(),con.configuration()));
        feignContextBean.setConfigurations(bffSpecificationInnerLists);

        return feignContextBean;

    }
    return bean;
}


@Data
public class SpecificationInner implements NamedContextFactory.Specification {

    private String name;
    private  Class<?>[] configuration;

    public SpecificationInner(String name,Class<?>[] configuration){

        this.configuration = configuration;
        this.name = name;


    }


    @Override
    public String getName() {
        return name;
    }

    @Override
    public Class<?>[] getConfiguration() {
        return configuration;
    }
}

}

主要的思想就是在get FeignContext这个bean之后,动态的注册一些信息。
ok完!!!!
上一篇下一篇

猜你喜欢

热点阅读