SpringCloud openFeign注入原理解析-手写实现仿Feign

一、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(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值