本文采用的springcloud 版本 Dalston.SR4 所有例子以Dalston.SR4 版本为准
spring cloud Dalston.SR4 feign 实际开发中踩坑(一)
坑4、 无法扫描到引用包的feign接口
在实际的生产中 我们服务模块是有很多的, 如果 A的接口 B要调用 我们声明一次feigin客户端 api 然后C也要调A的接口 我们还要声明一次feigin客户端api 如果还有更多 就会有很多重复代码 为了提高代码复用,我们往往单独声明一个包来引入feigin 的api 其他包如果调用的化引入到工程中就行了 但是 问题来了 引用包无法被直接扫描到 我们知道 一个微服务模块 如果需要开启feign调用功能 需要加上
@EnableFeignClients
源码:
/* * 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 *@FeignClient
}). Configures component scanning directives for use with * {@link org.springframework.context.annotation.Configuration *@Configuration
} 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. ** {@link #value()} is an alias for (and mutually exclusive with) this attribute. *
* 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. *
* 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@Configuration
for all feign clients. Can contain override *@Bean
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接口
坑5、 无法扫描到引入包的服务降级实现,大多数情况 我们要对feignClient接口 显式声明一个fallback 以便进行服务降级 但是如果你的feignclient 接口 不在 springboot 的启动类的子类 会无法启动 显示
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 接口的实现类 无法被注入
先看一下 源码:
/* * 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@RibbonClient
with the same name (i.e. value) as the feign client. * * @author Spencer Gibb * @author Venil Noronha */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @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@Qualifier
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@Configuration
for the feign client. Can contain override *@Bean
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 *@RibbonClient
. */ 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 所在的包","降级类所在的包"})
配置好后 我们写一个降级类:
@Componentpublic 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 发现熔断可用: