首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >聊聊spring cloud的FeignClientBuilder

聊聊spring cloud的FeignClientBuilder

原创
作者头像
code4it
修改2019-07-15 11:30:49
修改2019-07-15 11:30:49
1.6K0
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究一下spring cloud的FeignClientBuilder

FeignClientBuilder

spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/FeignClientBuilder.java

代码语言:javascript
复制
public class FeignClientBuilder {
​
    private final ApplicationContext applicationContext;
​
    public FeignClientBuilder(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
​
    public <T> Builder<T> forType(final Class<T> type, final String name) {
        return new Builder<>(this.applicationContext, type, name);
    }
​
    /**
     * Builder of feign targets.
     *
     * @param <T> type of target
     */
    public static final class Builder<T> {
​
        private FeignClientFactoryBean feignClientFactoryBean;
​
        private Builder(final ApplicationContext applicationContext, final Class<T> type,
                final String name) {
            this.feignClientFactoryBean = new FeignClientFactoryBean();
​
            this.feignClientFactoryBean.setApplicationContext(applicationContext);
            this.feignClientFactoryBean.setType(type);
            this.feignClientFactoryBean.setName(FeignClientsRegistrar.getName(name));
            this.feignClientFactoryBean.setContextId(FeignClientsRegistrar.getName(name));
            // preset default values - these values resemble the default values on the
            // FeignClient annotation
            this.url("").path("").decode404(false).fallback(void.class)
                    .fallbackFactory(void.class);
        }
​
        public Builder url(final String url) {
            this.feignClientFactoryBean.setUrl(FeignClientsRegistrar.getUrl(url));
            return this;
        }
​
        public Builder contextId(final String contextId) {
            this.feignClientFactoryBean.setContextId(contextId);
            return this;
        }
​
        public Builder path(final String path) {
            this.feignClientFactoryBean.setPath(FeignClientsRegistrar.getPath(path));
            return this;
        }
​
        public Builder decode404(final boolean decode404) {
            this.feignClientFactoryBean.setDecode404(decode404);
            return this;
        }
​
        public Builder fallback(final Class<T> fallback) {
            FeignClientsRegistrar.validateFallback(fallback);
            this.feignClientFactoryBean.setFallback(fallback);
            return this;
        }
​
        public Builder fallbackFactory(final Class<T> fallbackFactory) {
            FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
            this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
            return this;
        }
​
        /**
         * @param <T> the target type of the Feign client to be created
         * @return the created Feign client
         */
        public <T> T build() {
            return this.feignClientFactoryBean.getTarget();
        }
​
    }
​
}
  • FeignClientBuilder提供了forType静态方法用于创建Builder;Builder的构造器创建了FeignClientFactoryBean,其build方法使用FeignClientFactoryBean的getTarget()来创建目标feign client

实例

代码语言:javascript
复制
public class FeignClientBuilderTests {
​
    @Rule
    public ExpectedException thrown = ExpectedException.none();
​
    private FeignClientBuilder feignClientBuilder;
​
    private ApplicationContext applicationContext;
​
    private static Object getDefaultValueFromFeignClientAnnotation(
            final String methodName) {
        final Method method = ReflectionUtils.findMethod(FeignClient.class, methodName);
        return method.getDefaultValue();
    }
​
    private static void assertFactoryBeanField(final FeignClientBuilder.Builder builder,
            final String fieldName, final Object expectedValue) {
        final Field factoryBeanField = ReflectionUtils
                .findField(FeignClientBuilder.Builder.class, "feignClientFactoryBean");
        ReflectionUtils.makeAccessible(factoryBeanField);
        final FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) ReflectionUtils
                .getField(factoryBeanField, builder);
​
        final Field field = ReflectionUtils.findField(FeignClientFactoryBean.class,
                fieldName);
        ReflectionUtils.makeAccessible(field);
        final Object value = ReflectionUtils.getField(field, factoryBean);
        assertThat(value).as("Expected value for the field '" + fieldName + "':")
                .isEqualTo(expectedValue);
    }
​
    @Before
    public void setUp() {
        this.applicationContext = Mockito.mock(ApplicationContext.class);
        this.feignClientBuilder = new FeignClientBuilder(this.applicationContext);
    }
​
    @Test
    public void safetyCheckForNewFieldsOnTheFeignClientAnnotation() {
        final List<String> methodNames = new ArrayList();
        for (final Method method : FeignClient.class.getMethods()) {
            methodNames.add(method.getName());
        }
        methodNames.removeAll(
                Arrays.asList("annotationType", "value", "serviceId", "qualifier",
                        "configuration", "primary", "equals", "hashCode", "toString"));
        Collections.sort(methodNames);
        // If this safety check fails the Builder has to be updated.
        // (1) Either a field was removed from the FeignClient annotation and so it has to
        // be removed
        // on this builder class.
        // (2) Or a new field was added and the builder class has to be extended with this
        // new field.
        assertThat(methodNames).containsExactly("contextId", "decode404", "fallback",
                "fallbackFactory", "name", "path", "url");
    }
​
    @Test
    public void forType_preinitializedBuilder() {
        // when:
        final FeignClientBuilder.Builder builder = this.feignClientBuilder
                .forType(FeignClientBuilderTests.class, "TestClient");
​
        // then:
        assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
        assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class);
        assertFactoryBeanField(builder, "name", "TestClient");
        assertFactoryBeanField(builder, "contextId", "TestClient");
​
        // and:
        assertFactoryBeanField(builder, "url",
                getDefaultValueFromFeignClientAnnotation("url"));
        assertFactoryBeanField(builder, "path",
                getDefaultValueFromFeignClientAnnotation("path"));
        assertFactoryBeanField(builder, "decode404",
                getDefaultValueFromFeignClientAnnotation("decode404"));
        assertFactoryBeanField(builder, "fallback",
                getDefaultValueFromFeignClientAnnotation("fallback"));
        assertFactoryBeanField(builder, "fallbackFactory",
                getDefaultValueFromFeignClientAnnotation("fallbackFactory"));
    }
​
    @Test
    public void forType_allFieldsSetOnBuilder() {
        // when:
        final FeignClientBuilder.Builder builder = this.feignClientBuilder
                .forType(FeignClientBuilderTests.class, "TestClient").decode404(true)
                .fallback(Object.class).fallbackFactory(Object.class).path("Path/")
                .url("Url/");
​
        // then:
        assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
        assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class);
        assertFactoryBeanField(builder, "name", "TestClient");
​
        // and:
        assertFactoryBeanField(builder, "url", "http://Url/");
        assertFactoryBeanField(builder, "path", "/Path");
        assertFactoryBeanField(builder, "decode404", true);
        assertFactoryBeanField(builder, "fallback", Object.class);
        assertFactoryBeanField(builder, "fallbackFactory", Object.class);
    }
​
    @Test
    public void forType_build() {
        // given:
        Mockito.when(this.applicationContext.getBean(FeignContext.class))
                .thenThrow(new ClosedFileSystemException()); // throw an unusual exception
                                                                // in the
                                                                // FeignClientFactoryBean
        final FeignClientBuilder.Builder builder = this.feignClientBuilder
                .forType(TestClient.class, "TestClient");
​
        // expect: 'the build will fail right after calling build() with the mocked
        // unusual exception'
        this.thrown.expect(Matchers.isA(ClosedFileSystemException.class));
        builder.build();
    }
​
}
  • FeignClientBuilderTests验证了safetyCheckForNewFieldsOnTheFeignClientAnnotation、forType_preinitializedBuilder、forType_allFieldsSetOnBuilder、forType_build

小结

FeignClientBuilder提供了forType静态方法用于创建Builder;Builder的构造器创建了FeignClientFactoryBean,其build方法使用FeignClientFactoryBean的getTarget()来创建目标feign client

doc

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • FeignClientBuilder
  • 实例
  • 小结
  • doc
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档