一、Feign的基本使用
1.1、编写Feign客户端
假设现在我们有一个UserClient,如下:
@FeignClient(value = Services.SYSTEM_SERVER)
public interface UserClient {
/**
* 根据用户名,获取用户信息
* @param account
* @return
*/
@RequestMapping(value = "/user/getByAccount", method = RequestMethod.GET)
LoginInfo getUser(@RequestParam("account") String account);
}
1.2、 使用FeignClient远程调用
feign的调用也非常的简单,如下:
public class FeignClientTests {
@Autowired
private UserClient userClient;
public void getUser() {
LoginInfo userInfo = userClient.getUser("zhangsan");
}
}
1.3、UserClient测试调用引发的疑问
可以看出来 UserClient使用的时候是使用@autowired注解注入的,但是很明显的是,UserClient是一个接口(interface),它并没有实现类,比如(UserClientImpl implements UserClient)。那么它是如何被注入并且可以直接使用的呢?
3 二、验证接口在Spring注入
2.1、 Spring的Class类型组件注入
编写SpringComponent组件
@Component
public class SpringComponent {
public void helloWorld() {
System.out.println("Hello World");
}
}
编写测试类调用组件
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpApplicationTests {
@Autowired
SpringComponent springComponent;
@Test
public void contextLoads() {
springComponent.helloWorld();
}
}
执行一下测试类,输出:
Hello World
2.2、 Spring注入接口组件尝试
编写SpringInterfaceComponent接口组件
@Component
public interface SpringInterfaceComponent {
default void helloWorld() {
System.out.println("Hello World");
}
}
编写测试类调用组件
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpApplicationTests2 {
@Autowired
SpringInterfaceComponent springInterfaceComponent;
@Test
public void contextLoads() {
springInterfaceComponent.helloWorld();
}
}
执行一下测试类,输出:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.jumper.lib.http.demo.SpringInterfaceComponent' available
很明显,注入不了,因为他没有实现类。
2.3、 对Spring注入接口类改写
SpringInterfaceComponent移除@Component注解
public interface SpringInterfaceComponent {
default void helloWorld() {
System.out.println("Hello World");
}
}
编写SpringInterfaceComponent的实现类
@Component
public class SpringInterfaceComponentImpl implements SpringInterfaceComponent {
}
测试类调用组件
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpApplicationTests2 {
@Autowired
SpringInterfaceComponent springInterfaceComponent;
@Test
public void contextLoads() {
springInterfaceComponent.helloWorld();
}
}
执行一下测试类,输出:
Hello World
2.4、 验证猜想(Feign配置源码解析)
可以猜测,Feign一定是为接口生成了实现类注入到Spring容器里了。
那我们一起来看看Feign的注入原理
2.4.1 Feign的启动入口(@EnableFeignClients)
@EnableFeignClients(basePackages = ProjectConstants.baseScanPackage)
@SpringBootApplication(scanBasePackages = ProjectConstants.baseScanPackage)
public class AuthServer {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(AuthServer.class, args);
String port = run.getEnvironment().getProperty("server.port");
log.info("Auth Server is running --> http://localhost:{}", port);
}
}
编写启动类的时候我们会加一个@EnableFeignClients注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] basePackages() default {
};
}
进入@EnableFeignClients注解可以看到重点就在于存在一个@Import(FeignClientsRegistrar.class)
Feigin通过@Import来导入FeignClientsRegistrar.class(FeignClient解析客户端并注册到Spring容器的实现类)
2.4.2 FeiginClient注册器(FeignClientsRegistrar.class)
2.4.2.1、FeignClientsRegistrar声明
FeignClientsRegistrar 他分别实现了ImportBeanDefinitionRegistrar、ResourceLoaderAware、EnvironmentAware三个接口
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
}
我们看看ImportBeanDefinitionRegistrar源码:
可以看出它声明一个注册Bean的接口
public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
我们再看看ResourceLoaderAware源码:
就是一个注入ResourceLoader的接口
public interface ResourceLoaderAware extends Aware {
/**
* Set the ResourceLoader that this object runs in.
* <p>This might be a ResourcePatternResolver, which can be checked
* through {@code instanceof ResourcePatternResolver}. See also the
* {@code ResourcePatternUtils.getResourcePatternResolver} method.
* <p>Invoked after population of normal bean properties but before an init callback
* like InitializingBean's {@code afterPropertiesSet} or a custom init-method.
* Invoked before ApplicationContextAware's {@code setApplicationContext}.
* @param resourceLoader the ResourceLoader object to be used by this object
* @see org.springframework.core.io.support.ResourcePatternResolver
* @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
*/
void setResourceLoader(ResourceLoader resourceLoader);
}
再看看EnvironmentAware 源码:
是一个注入Environment 的接口
public interface EnvironmentAware extends Aware {
/**
* Set the {@code Environment} that this component runs in.
*/
void setEnvironment(