【OpenFeign】【源码+图解】【二】注册OpenFeign接口的实例

【OpenFeign】【源码+图解】【一】HelloWorld及其工作原理

3. 注册OpenFeign接口的实例

从HelloWorld中我们看到需要显示加入**@EnableFeignClients注解才能开启openFeign的功能,因此它就成为我们分析openFeign**的入口,先看下这个Annotation的属性

@Import(FeignClientsRegistrar.class) // 这个注册类是关键
public @interface EnableFeignClients {
    // 扫描的包,用于寻找open-feign客户端
	String[] value() default {};
    // 与value()一样
	String[] basePackages() default {};
    // 与value()的字符串不同,这里用的是Class类
	Class<?>[] basePackageClasses() default {};
    // 这里声明的配置会在所有的open-feign的clients生效
	Class<?>[] defaultConfiguration() default {};
    // @FeignClient声明的类,如果不为空的话前面的包扫描会失效
	Class<?>[] clients() default {};
}

先看下FeignClientsRegistrar的类图

在这里插入图片描述

从类图中可以看出FeignClientsRegistrar有以下作用

  1. 注册beans
  2. 获取配置Environment
  3. 获取资源加载器ResourceLoader

那么,接下来我们就开始分析FeignClientsRegistrar要注册哪些beans,入口是实现ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    @Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 1、注册默认的配置,即@EnableFeignClients中的defaultConfiguration,这个是所有FeignClients都有的
		registerDefaultConfiguration(metadata, registry);
        // 2、注册FeignClients
		registerFeignClients(metadata, registry);
	}
}

整体过程如下

在这里插入图片描述

中间涉及的源码过多,不做过多的具体分析,只探讨绿色的两部分,因为这两部分也是后续的开始

在开始之前我们先认识下**@FeignClient**

public @interface FeignClient {
	// client名字
	@AliasFor("name")
	String value() default "";
	// context名字
	String contextId() default "";
	@AliasFor("value")
	String name() default "";
	// 别名
	String[] qualifiers() default {};
	// 类似@RequestMapping声明的base url
	String url() default "";
	boolean decode404() default false;
	// client的配置,不同于@EnableFeignClients的defaultConfiguration,这个configuration只属于某个client
	Class<?>[] configuration() default {};
    // 降级处理的实例,需实现FeignClient
	Class<?> fallback() default void.class;
    // 降级处理实例的创建工厂,需实现FallbackFactory接口
	Class<?> fallbackFactory() default void.class;
	String path() default "";
	boolean primary() default true;

}

在使用中**@FeignClient**是用在接口上,而接口需要实例化才能使用,而绿色的两部分就是创建接口实例的过程,接下来分析是怎么利用其参数来创建实例的

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map<String, Object> attributes) {
		......
        // 本代码以HelloWorld中的ProductFeignClient为例探讨
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);  // DefaultListableBeanFactory
		factoryBean.setName(name); // product
		factoryBean.setContextId(contextId); // product
		factoryBean.setType(clazz); // interface com.lanna.openfeign.ProductFeignClient
		factoryBean.setRefreshableClient(isClientRefreshEnabled()); // yml文件中的feign.client.refresh-enabled
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
			factoryBean.setUrl(getUrl(beanFactory, attributes)); // @FeignClient.url,默认""
			factoryBean.setPath(getPath(beanFactory, attributes)); // @FeignClient.path,默认""
            // @FeignClient.decode404,默认false。如果为true,当遇到404错误时不直接抛异常
			factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
			Object fallback = attributes.get("fallback");
            // @FeignClient.fallback。fallback类必须实现@FeignClient的接口并且在spring的容器中
			if (fallback != null) {
				factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
						: ClassUtils.resolveClassName(fallback.toString(), null));
			}
            // @FeignClient.fallbackFactory。fallbackFactory: 
            // 1、必须实现FallbackFactory接口,2、必须能创建@FeignClient接口的实例,3、必须在spring的容器中
			Object fallbackFactory = attributes.get("fallbackFactory");
			if (fallbackFactory != null) {
				factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
						: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
			}
            // 创建@FeignClient接口(即ProductFeignClient)的proxy实例
			return factoryBean.getObject();
		});
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        // 设置延迟初始化。如果声明的FeignClient没有使用的话不会初始化
		definition.setLazyInit(true);
        // 验证参数
		validate(attributes);

		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        // factoryBeanObjectType: com.lanna.openfeign.ProductFeignClient
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
        // feignClientsRegistrarFactoryBean: factoryBean
		beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

		// @FeignClient.primary,默认true
		beanDefinition.setPrimary(primary);

		// 别名qualifiers:@FeignClient.qualifiers
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
        // 注册com.lanna.openfeign.ProductFeignClient
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        // 如果feign.client.refresh-enabled开启(默认false),注册名为contextId的OptionsFactoryBean
		registerOptionsBeanDefinition(registry, contextId);
	}
}

注册OpenFeign的beans实例过程就分析到此,代码中definition.setLazyInit(true)设置了延迟加载,通俗点理解就是如果代码中没有通过@Autowired注入bean的话是不会调用factoryBean.getObject()的。接下来我们就看看factoryBean.getObject()是如何创建FeignClient的client,即ProductFeignClient的实例。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值