本文作者:陈刚,叩丁狼高级讲师。原创文章,转载请注明出处。
回顾
我们还是在之前Feign的使用案例集成上来分析Feign源码,在分析 之前我们先简单来回顾一下Feign的用法,要用Feign首选是需要在配置类似开启Feign客户端支持
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
然后我们需要去定义Feign客户端接口
//PRODUCER:指向要访问的服务
//configuration = FooConfiguration.class Feign Client配置类,指定负载均衡策略
//fallback = MyFeignClientImpl.class: MyFeignClient结构的实现类,需要复写 provide方法,并实现错误处理逻辑
//当访问出现故障,程序会自动调用实现类的 provide方法的错误处理逻辑。
@FeignClient(value = "PRODUCER",configuration = FooConfiguration.class,fallback = MyFeignClientImpl.class)
public interface MyFeignClient {
//当此方法别调用会自动请求到 PRODUCER服务的 /provide 资源
@RequestMapping(value = "/provide")
public String provide(@RequestParam("name") String name);
}
在使用的时候就只需要调用 MyFeignClient 接口即可,SpringCloud会自动去调用对应的目标服务。
分析Feign我们还是从他的@EnableFeignClients标签开始,我们先看下他的源码
/**
* 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 {
该标签的注释告诉我们,它开启了对打了 @FeignClient 标签所在的接口的扫描,和对期配置组件的扫描支持 ,并且该标签import了一个FeignClientsRegistrar类,我们跟踪进去看一下
/**
* @author Spencer Gibb
* @author Jakub Narloch
* @author Venil Noronha
* @author Gang Li
*/
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
// patterned after Spring Integration IntegrationComponentScanRegistrar
// and RibbonClientsConfigurationRegistgrar
private ResourceLoader resourceLoader;
private Environment environment;
public FeignClientsRegistrar() {
他实现了 ImportBeanDefinitionRegistrar接口,我们继续看ImportBeanDefinitionRegistrar的源码
/**
* Interface to be implemented by types that register additional bean definitions when
* processing @{@link Configuration} classes. Useful when operating at the bean definition
* level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
*
* <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
* may be provided to the @{@link Import} annotation (or may also be returned from an
* {@code ImportSelector}).
*
* <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #registerBeanDefinitions}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
*
* <p>See implementations and associated unit tests for usage examples.
*
* @author Chris Beams
* @since 3.1
* @see Import
* @see ImportSelector
* @see Configuration
*/
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);
}
翻译“Interface to be implemented by types that register additional bean definitions when processing @{@link Configuration} classes.”得知,该接口提供了注册bean的功能支持,而registerBeanDefinitions则是bean注册的方法。 AnnotationMetadata是bean的元注解对象 ,BeanDefinitionRegistry是当前注册的bean的注册表,
我们继续跟踪一下 FeignClientsRegistrar#registerBeanDefinitions 方法
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
registerDefaultConfiguration(metadata, registry);注册默认配置,我们跟踪一下源码
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
//name以 "default."+类名
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
这里获取了主程序配置类的@EnableFeignClients注解中的feign默认配置 ,然后用 “default.” 开头加上类名作为名字来注册 ,继续跟踪
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
这里把 configuration 配置交给了 BeanDefinitionBuilder然后去创建了一个 FeignClientSpecification 对象出来 ,然后调用BeanDefinitionRegistry的registerBeanDefinition方法把 包装了配置类的FeignClientSpecification对象进行注册,注册到哪儿去了呢?最终会调用BeanDefinitionRegistryd的子类DefaultListableBeanFactory#registerBeanDefinition方法注册
...省略代码...
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...省略代码...
this.beanDefinitionMap.put(beanName, beanDefinition);
这里最终会put到一个线程安全的map:beanDefinitionMap = new ConcurrentHashMap<>(256); 中,这个东西就是ico容器o( ̄︶ ̄)o 。
回过头来我们继续看 FeignClientsRegistrar#registerBeanDefinitions 方法
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//注册默认配置到ioc容器中
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
到这里我们已经知道 registerDefaultConfiguration(metadata, registry);注册默认配置,那么 registerFeignClients(metadata, registry);方法看名字应该是注册所有的feignClient的bean,我们一步一步进去看
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//得到组件扫描器,resourceLoader是资源加载器,
//比如加载spring配置文件就会用到ResourceLoader
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
//获取元注解上EnableFeignClients的属性配置
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
//获取元注解所在的主程序配置类的包名即项目所在的包
basePackages = getBasePackages(metadata);
}
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(