一. @EnableFeignClients
1.1.類介紹
從上面註釋可以看出是掃描宣告瞭@FeignClient介面的類,還引入了 FeignClientsRegistrar類,從字面意思可以看出是進行了 FeignClient 客戶端類的註冊。
1.2.FeignClientsRegistrar 詳解
最主要的一個方法:registerBeanDefinitions註冊bean定義資訊,主要功能是實現向容器註冊Feign客戶端配置資訊和所有的使用了@FeignClient的類;
1.2.1.registerDefaultConfiguration()
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 獲取@EnableFeignClients註解的屬性和值 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); // 獲取屬性中的預設配置 defaultConfiguration,name是main主程式 if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } //預設配置資訊進行容器註冊 registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }
1.2.1.1.registerClientConfiguration
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { // 建立一個 FeignClientSpecification.class 構造器 BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); // 向容器中註冊預設配置 registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); }
1.2.1.2.registerBeanDefinition
核心程式碼在:org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { ............ if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { //放到beanDefinitionMap,到AbstractApplicationContext#finishBeanFactoryInitialization(beanFactory)中取出進行bean例項化 //此時是處於invokeBeanFactoryPostProcessors(beanFactory);階段,進行BeanDefinitionRegistryPostProcessor的處理 this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } .......... }
1.2.2.registerFeignClients()
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //獲取一個掃描器 ClassPathScanningCandidateComponentProvider scanner = getScanner(); //設定資源路徑 scanner.setResourceLoader(this.resourceLoader); //包路徑 Set<String> basePackages; //獲取EnableFeignClients註解的引數和值,有5個值,其中clients對應的class[0]是沒有值的 Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); //定義一個 FeignClient 註解型別過濾器 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); //clients為class[0],長度為0 final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { //FeignClient 註解過濾器新增到掃描器中 scanner.addIncludeFilter(annotationTypeFilter); //獲取Application對應的根路徑包 basePackages = getBasePackages(metadata); } //clients不為空:FeignClient 註解過濾器新增到掃描器中、獲取包路徑 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( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } //遍歷包路徑,獲取標記@FeignClient註解的介面向容器中注入 for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); //獲取@FeignClient的引數和值 Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); //再次更新配置 registerClientConfiguration(registry, name, attributes.get("configuration")); //註冊 registerFeignClient(registry, annotationMetadata, attributes); } } } }
1.2.2.2.registerFeignClient
//定義一個 FeignClientFactoryBean bean定義構造器(重點) BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); .......... //前面根據attributes進行屬性賦值後注入到Spring容器中 BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
二.@FeignClient
從上面可以看到在註冊客戶端時註冊了一個FeignClientFactoryBean(對於FactoryBean的擴充套件知識和案例),所以FeignClient的獲取是從getObject()獲取的:
2.1.getTarget()
<T> T getTarget() {
//獲取建立feign例項的工廠
FeignContext context = this.applicationContext.getBean(FeignContext.class);
//獲取feign構造器
Feign.Builder builder = feign(context);
//url拼接
if (!StringUtils.hasText(this.url)) {
//拼接name
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
//拼接path
this.url += cleanPath();
//建立例項
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
2.1.1. feign()
//新增日誌物件、編碼器、解碼器、解析規則器 protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); // @formatter:off Feign.Builder builder = get(context, Feign.Builder.class) // required values .logger(logger) .encoder(get(context, Encoder.class)) .decoder(get(context, Decoder.class)) //SpringMvcContract:對RequestMapping、RequestParam、RequestHeader等註解進行解析 .contract(get(context, Contract.class)); // @formatter:on configureFeign(context, builder); return builder; }
2.1.2. loadBalance()
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { //根據context上下文獲取客戶端例項,client:TraceLoadBalancerFeignClient,負載均衡 Client client = getOptional(context, Client.class); if (client != null) { //客戶端建立 builder.client(client); //獲取HystrixTargerer Targeter targeter = get(context, Targeter.class); //呼叫HystrixTargeter#target進行例項建立 return targeter.target(this, builder, context, target); } throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); }
2.1.3. HystrixTargeter#target
class HystrixTargeter implements Targeter { @Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { // 不是同一種型別,進入feign#target方法 if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } ...... }
2.1.4. Feign.Builder#target
public <T> T target(Target<T> target) { return build().newInstance(target); }
2.1.4.1. Feign.Builder#target
public Feign build() { //同步方法處理工廠,構建了日誌級別資訊 SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); //解析資訊:編碼、解密、同步方法處理工廠 ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); //new物件 return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
2.1.4.2.ReflectiveFeign#newInstance
@Override public <T> T newInstance(Target<T> target) { //得到feign類的定義方法 Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); //定義方法存放集合 Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); //預設方法存放集合 List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); //對feign類方法進行遍歷 for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class) { continue; //預設方法 } else if (Util.isDefault(method)) { DefaultMethodHandler handler = new DefaultMethodHandler(method); defaultMethodHandlers.add(handler); methodToHandler.put(method, handler); } else { methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } //jdk動態代理建立物件 InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; }
三.總結
主要應用了FactoryBean的特性getObject()進行jdk動態建立一個ReflectiveFeign的代理物件。