本文采用的springcloud 版本 Dalston.SR4 全部例子以Dalston.SR4 版本爲準java
spring cloud Dalston.SR4 feign 實際開發中踩坑(一)spring
http://www.javashuo.com/article/p-dxecfgvv-kv.htmlexpress
坑四、 沒法掃描到引用包的feign接口apache
在實際的生產中 咱們服務模塊是有不少的, 若是 A的接口 B要調用 咱們聲明一次feigin客戶端 api 而後C也要調A的接口 咱們還要聲明一次feigin客戶端api 若是還有更多 就會有不少重複代碼 爲了提升代碼複用,咱們每每單獨聲明一個包來引入feigin 的api 其餘包若是調用的化引入到工程中就好了 可是 問題來了 引用包沒法被直接掃描到 咱們知道 一個微服務模塊 若是須要開啓feign調用功能 須要加上json
@EnableFeignClients
源碼:api
/* * Copyright 2013-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.netflix.feign; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; /** * Scans for interfaces that declare they are feign clients (via {@link FeignClient * <code>@FeignClient</code>}). Configures component scanning directives for use with * {@link org.springframework.context.annotation.Configuration * <code>@Configuration</code>} classes. * * @author Spencer Gibb * @author Dave Syer * @since 1.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { /** * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of * {@code @ComponentScan(basePackages="org.my.pkg")}. * @return the array of 'basePackages'. */ String[] value() default {}; /** * Base packages to scan for annotated components. * <p> * {@link #value()} is an alias for (and mutually exclusive with) this attribute. * <p> * Use {@link #basePackageClasses()} for a type-safe alternative to String-based * package names. * * @return the array of 'basePackages'. */ String[] basePackages() default {}; /** * Type-safe alternative to {@link #basePackages()} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. * <p> * Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * * @return the array of 'basePackageClasses'. */ Class<?>[] basePackageClasses() default {}; /** * A custom <code>@Configuration</code> for all feign clients. Can contain override * <code>@Bean</code> definition for the pieces that make up the client, for instance * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. * * @see FeignClientsConfiguration for the defaults */ Class<?>[] defaultConfiguration() default {}; /** * List of classes annotated with @FeignClient. If not empty, disables classpath scanning. * @return */ Class<?>[] clients() default {}; }
其中basePackages 聲明 掃描 feignclient 註解所在的包的包路徑 聲明後就能掃描到你在該包下@FeignClient 標記的 feign接口springboot
坑五、 沒法掃描到引入包的服務降級實現,大多數狀況 咱們要對feignClient接口 顯式聲明一個fallback 以便進行服務降級 可是若是你的feignclient 接口 不在 springboot 的啓動類的子類 會沒法啓動 顯示app
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.xxx.feign.api.UserService': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: No fallback instance of type class com.xxx.feign.api.UserServiceHystrix found for feign client MEMBER-SERVICE
也就是你feign 接口的實現類 沒法被注入less
先看一下 源碼:ide
/* * Copyright 2013-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.netflix.feign; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; /** * Annotation for interfaces declaring that a REST client with that interface should be * created (e.g. for autowiring into another component). If ribbon is available it will be * used to load balance the backend requests, and the load balancer can be configured * using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client. * * @author Spencer Gibb * @author Venil Noronha */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FeignClient { /** * The name of the service with optional protocol prefix. Synonym for {@link #name() * name}. A name must be specified for all clients, whether or not a url is provided. * Can be specified as property key, eg: ${propertyKey}. */ @AliasFor("name") String value() default ""; /** * The service id with optional protocol prefix. Synonym for {@link #value() value}. * * @deprecated use {@link #name() name} instead */ @Deprecated String serviceId() default ""; /** * The service id with optional protocol prefix. Synonym for {@link #value() value}. */ @AliasFor("value") String name() default ""; /** * Sets the <code>@Qualifier</code> value for the feign client. */ String qualifier() default ""; /** * An absolute URL or resolvable hostname (the protocol is optional). */ String url() default ""; /** * Whether 404s should be decoded instead of throwing FeignExceptions */ boolean decode404() default false; /** * A custom <code>@Configuration</code> for the feign client. Can contain override * <code>@Bean</code> definition for the pieces that make up the client, for instance * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. * * @see FeignClientsConfiguration for the defaults */ Class<?>[] configuration() default {}; /** * Fallback class for the specified Feign client interface. The fallback class must * implement the interface annotated by this annotation and be a valid spring bean. */ Class<?> fallback() default void.class; /** * Define a fallback factory for the specified Feign client interface. The fallback * factory must produce instances of fallback classes that implement the interface * annotated by {@link FeignClient}. The fallback factory must be a valid spring * bean. * * @see feign.hystrix.FallbackFactory for details. */ Class<?> fallbackFactory() default void.class; /** * Path prefix to be used by all method-level mappings. Can be used with or without * <code>@RibbonClient</code>. */ String path() default ""; /** * Whether to mark the feign proxy as a primary bean. Defaults to true. */ boolean primary() default true; }
* Fallback class for the specified Feign client interface. The fallback class must
implement the interface annotated by this annotation and be a valid spring bean. *
fallback 上會有這樣的註釋 說的是 聲明feign客戶端接口 的降級類 並且 這個降級類必須實現 該feign 接口 而且必須是一個可用的spring bean
若是你僅僅在這個實現類上加入spring bean 聲明註解 好比 @Component 你會發現依然 沒法注入 來大體猜測一下流程 熟悉springboot 的 應該清楚 springboot 啓動的時候 會掃描其main類 所在包的子包進行 bean 實例化 若是不在子包 默認是掃描不到的 那麼如何掃描到呢 聲明掃描的路徑 也就是須要在main類上使用註解@ComponentScan 註解 可是 若是 咱們僅僅聲明瞭 feign 降級實現的路徑 你會發現 main類的子包沒法掃描到了 因此 此處應該
@ComponentScan(basePackages = {"main 所在的包","降級類所在的包"})
配置好後 咱們寫一個降級類:
@Component public class UserServiceHystrix implements UserService { @Override public User get(User user) { System.out.println("<><><><><><><><><><> MEMBER-SERVICE 掛了<><><><><><><><><><> "); user.setAge(20017); user.setGender("male"); user.setName("服務掛了"); return user; } }
而後咱們測試一下 啓動消費方 不啓動 提供方MEMBER-SERVICE 發現熔斷可用: