交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
Fegin 第2 章:FeignClientFactoryBean
分享
未结
0
1228
李延
LV6
2021-06-19
悬赏:20积分
# 作用 之前我们了解到最后的bean定义创建的对象就是FeignClientFactoryBean。它是FactoryBean的子类。所以最后我们创建的Feign对象就是通过它的getObject方法生成的对象。 # getObject ```java @Override public Object getObject() { return getTarget(); } /** * @param <T> the target type of the Feign client * @return a {@link Feign} client created with the specified data and the context * information */ <T> T getTarget() { //从上下文中获取context对象 FeignContext context = applicationContext.getBean(FeignContext.class); //创建Feign.Builder Feign.Builder builder = feign(context); //当没有设置url值时,根据name进行拼接 if (!StringUtils.hasText(url)) { if (!name.startsWith("http")) { url = "http://" + name; } else { url = name; } url += cleanPath(); return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url)); } // 否则 直接使用url if (StringUtils.hasText(url) && !url.startsWith("http")) { url = "http://" + url; } String url = this.url + cleanPath(); Client client = getOptional(context, Client.class); if (client != null) { if (client instanceof FeignBlockingLoadBalancerClient) { // not load balancing because we have a url, // but Spring Cloud LoadBalancer is on the classpath, so unwrap client = ((FeignBlockingLoadBalancerClient) client).getDelegate(); } builder.client(client); } //获取Targeter 对象 Targeter targeter = get(context, Targeter.class); // 通过targeter.target 生成对象。 return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url)); } protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); Targeter targeter = get(context, Targeter.class); return targeter.target(this, builder, context, target); } throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?"); } ``` 上面代码主要逻辑如下: 1. 通过上下文获取FeignContext 2. 根据name和url拼接请求路径 3. 从FeignContext中获取Feign.Builder 4. 从FeignContext中获取Client 5. 从FeignContext中获取Targeter 6. 通过Targeter的target方法获取我们需要的对象 ## FeignContext 在上面代码中我们看到有很多对象都是通过FeignContext来获取的,我们具体看一下这个类的作用 ### 初始化 ```java @Bean public FeignContext feignContext() { FeignContext context = new FeignContext(); context.setConfigurations(this.configurations); return context; } ``` 在代码中我们找到它是通过 @Bean加载到spring中的而它需要一个configurations参数。我们看一下这个参数的加载 ```java @Autowired(required = false) private List<FeignClientSpecification> configurations = new ArrayList<>(); ``` 这个直接就是通过Autowired注解注入的,而FeignClientSpecification对象我们在第一章中已经分析过了,就是每个fegin的配置。 ### FeignClientFactoryBean的get方法 在前面的Client对象、Targeter对象都是通过FeignClientFactoryBean的get方法来获取的。我们看一下这个方法 ```java protected <T> T get(FeignContext context, Class<T> type) { T instance = context.getInstance(contextId, type); if (instance == null) { throw new IllegalStateException("No bean found of type " + type + " for " + contextId); } return instance; } ``` 我们看到就是调用的FeignContext对象方法。我们跟进 ```java public <T> T getInstance(String name, Class<T> type) { AnnotationConfigApplicationContext context = getContext(name); try { return context.getBean(type); } catch (NoSuchBeanDefinitionException e) { // ignore } return null; } public <T> T getInstance(String name, Class<T> type) { AnnotationConfigApplicationContext context = getContext(name); try { return context.getBean(type); } catch (NoSuchBeanDefinitionException e) { // ignore } return null; } protected AnnotationConfigApplicationContext createContext(String name) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); if (this.configurations.containsKey(name)) { for (Class<?> configuration : this.configurations.get(name).getConfiguration()) { context.register(configuration); } } for (Map.Entry<String, C> entry : this.configurations.entrySet()) { if (entry.getKey().startsWith("default.")) { for (Class<?> configuration : entry.getValue().getConfiguration()) { context.register(configuration); } } } context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType); context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.<String, Object>singletonMap(this.propertyName, name))); if (this.parent != null) { // Uses Environment from parent as well as beans context.setParent(this.parent); // jdk11 issue // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101 context.setClassLoader(this.parent.getClassLoader()); } context.setDisplayName(generateDisplayName(name)); context.refresh(); return context; } ``` 通过上面一系列的方法。我们看到如果我们在注解上配置了Configuration。就说明当前的fegin有自己单独的组建配置。那么就是创建一个自己的上下文,并扫描指定的路径。并且将我们的上下文设置为父上下文。 所以我们通过get方法获取我们需要的对象时,会优先读取我们指定路径下的对象,如果没有才从我们主上下文中获取。 ## Feign.Builder 在前面获取完FeignContext后我们执行下面代码 ```java Feign.Builder builder = feign(context); ``` 我们具体看一下 ```java protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(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)) .contract(get(context, Contract.class)); // @formatter:on configureFeign(context, builder); applyBuildCustomizers(context, builder); return builder; } ``` 这里主要就是从上下文中获取Feign.Builder对象。并4同样的方式获取了一些组件:Encoder、Decoder、Contract ## Targeter 最后我们看到通过上下文获取Targeter,并通过它生成对象。这里我们使用的是DefaultTargeter。我们直接看它的代码 ```java @Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { return feign.target(target); } ``` 直接跟进feign的方法 ```java public <T> T target(Target<T> target) { return build().newInstance(target); } public Feign build() { Client client = Capability.enrich(this.client, capabilities); Retryer retryer = Capability.enrich(this.retryer, capabilities); List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream() .map(ri -> Capability.enrich(ri, capabilities)) .collect(Collectors.toList()); Logger logger = Capability.enrich(this.logger, capabilities); Contract contract = Capability.enrich(this.contract, capabilities); Options options = Capability.enrich(this.options, capabilities); Encoder encoder = Capability.enrich(this.encoder, capabilities); Decoder decoder = Capability.enrich(this.decoder, capabilities); InvocationHandlerFactory invocationHandlerFactory = Capability.enrich(this.invocationHandlerFactory, capabilities); QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities); SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); } ``` 这里有几个对象InvocationHandlerFactory、SynchronousMethodHandler.Factory、ParseHandlersByName我们在其单独方法中说明 我们关注ReflectiveFeign对象 ### ReflectiveFeign的newInstance ```java public <T> T newInstance(Target<T> target) { Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); 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))); } } 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; } ``` 这里我们看到最后是通过jdk代理来完成对象的创建的并且创建的对象是InvocationHandler。 # 总结 这里我们看到了Feign对象创建的整个过程。以及最后它生成的代理。但是在这期间使用的很多组件我们还不清楚。有: - Encoder - Decoder - Contract - Client 以及再创建InvocationHandler事一些处理我们都不清楚,这些内容。我们在调用Fegin对象的时候,在它使用过程中说明,可以更加清晰
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号