本文采用的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 发现熔断可用: