当前位置:网站首页>Source code analysis of openfeign
Source code analysis of openfeign
2022-07-27 03:40:00 【Anxious two dogs】
Core flow chart

The first is the initialization process
When we are using it, first enable Feign, namely Add comments to the startup class @EnableFeignClients
@EnableFeignClients
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
Get into @EnableFeignClients In annotation class
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
About @Import You can view the @Import annotation understand , Load here FeignClientsRegistrar class , We can continue to enter this category
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
}
FeignClientsRegistrar There are multiple interface implementations ,ImportBeanDefinitionRegistrar Used to customize the implementation Bean Defining information 、ResourceLoaderAware、EnvironmentAware
Let's focus on that ImportBeanDefinitionRegistrar Interface method of registerBeanDefinitions The implementation of the
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
among registerDefaultConfiguration(metadata, registry); The main way is to get @EnableFeignClients Annotation parameters defaultConfiguration Value …
Let's focus on that registerFeignClients(metadata, registry);
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// Get the attribute information in the annotation
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");
// without clients Configuration information , Then get all @FeignClient Class marked
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
// Store quilt @FeignClient The definition information of the annotated class (BeanDefinition)
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
// If there is a designated clients Information Will come to this logic
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// Loop traversal @FeignClient Of the marked class BeanDefinition
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// Verify whether the 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");
// obtain @FeignClient Attribute information
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
// Get the name
String name = getClientName(attributes);
// Add some configuration information to registry Of BeanDefinition Collection ( Encoder 、 Decoder, etc )
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// register FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
Let's focus on registerFeignClient Method call , We need to focus on Set up FeignClientFactoryBean( Indicates registered FeignClient All are FeignClientFactoryBean Type of )
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
// Get the current @FeignClient Annotated class name
String className = annotationMetadata.getClassName();
// Create... Through builder mode BeanDefinition
//BeanDefinitionBuilder It has properties beanDefinition(GenericBeanDefinition type )
//beanDefinition Set up beanClass(Class type ) by FeignClientFactoryBean.class
**BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);**
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
// establish BeanDefinitionHolder
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] {
alias });
// register FeignClient Of BeanDefinition Information
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
Let's continue to focus on BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); Method
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
// register
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
About calling process
Suppose we have a IRemoteCallService class
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name="server-user")
public interface IRemoteCallService {
@RequestMapping(value="/user/getNameById",method = RequestMethod.POST)
String getName(@RequestParam("userId") String userId);
}
When we execute context.getBean(IRemoteCallService.class); when
@EnableFeignClients
public class GatewayApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(GatewayApplication.class, args);
IRemoteCallService bean = context.getBean(IRemoteCallService.class);
}
}
The specific call process in the middle is :
org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.Class)
org.springframework.beans.factory.support.DefaultListableBeanFactory#getBean(java.lang.Class)
org.springframework.beans.factory.support.DefaultListableBeanFactory#getBean(java.lang.Class, java.lang.Object…)
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveBean
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveNamedBean(org.springframework.core.ResolvableType, java.lang.Object[], boolean)
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class, java.lang.Object…)
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
Let's take a look at doGetBean Part of the method :
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//mbd yes GenericBeanDefinition Transformed from
RootBeanDefinition mbdToUse = mbd;
// get beanClass, namely :FeignClientFactoryBean
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
try {
mbdToUse.prepareMethodOverrides();
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// You can call BeanPostProcessors To complete the instance
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
try {
// call doCreateBean Method ,
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
}
Let's keep looking doCreateBean Method
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// Instantiation
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
}
createBeanInstance Method call
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//beanClass :FeignClientFactoryBean
Class<?> beanClass = resolveBeanClass(mbd, beanName);
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// Call constructor instantiation
return instantiateBean(beanName, mbd);
}
We continue back to doGetBean In the method , Call complete createBean After the method , Continue to call getObjectForBeanInstance Method
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// At this point we get beanInstance yes FeignClientFactoryBean Example ,FeignClientFactoryBean Is to implement the FactoryBean Interface
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// Access to the cache
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Cache get Bean Defining information
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// obtain Object
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
else {
// call doGetObjectFromFactoryBean Method
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
try {
else {
// call FeignClientFactoryBean Of getObject Method
object = factory.getObject();
}
}
}
About the agent creation process
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
// obtain FeignClientSpecification Configuration information stored in ( Encoder 、 decoder )
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
// assemble url,(http://server-user)
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));
}
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 LoadBalancerFeignClient) {
client = ((LoadBalancerFeignClient) client).getDelegate();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
//
Targeter targeter = get(context, Targeter.class);
// Build agent
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(type, name, url));
}
call org.springframework.cloud.openfeign.DefaultTargeter#target
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
call feign.Feign.Builder#target(feign.Target)
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
call feign.Feign.Builder#build
private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder,
errorDecoder, synchronousMethodHandlerFactory);
// Generate ReflectiveFeign,
return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
}
Back to feign.Feign.Builder#target(feign.Target), Continue to look at newInstance Method call
@Override
public <T> T newInstance(Target<T> target) {
// call targetToHandlersByName.apply, For the FeignClient All methods in generate MethodHandler (SynchronousMethodHandler type )
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
// Traverse all Method
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 {
// take FeignClient Methods and corresponding MethodHandler Save up
//nameToHandler What is kept is Based on the class name, method name and other information and the corresponding MethodHandler The aggregate information of
//methodToHandler What is kept is method Class and corresponding MethodHandler The aggregate information of
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// Generate handler (FeignInvocationHandler type ), It has two properties target namely FeignClient The corresponding class ,dispatch That is, the method in the class and the corresponding MethodHandler(SynchronousMethodHandler type )
InvocationHandler handler = factory.create(target, methodToHandler);
// be based on Proxy Generate JDK A dynamic proxy
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{
target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
// Return proxy object
return proxy;
}
targetToHandlersByName.apply Call to
public Map<String, MethodHandler> apply(Target key) {
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
About the proxy call process
Above, we have made it clear that handler (FeignInvocationHandler type ), It has two properties target namely FeignClient The corresponding class ,dispatch That is, the method in the class and the corresponding MethodHandler(SynchronousMethodHandler type )
1、 Let's take a look at FeignInvocationHandler Specific calling process
feign.ReflectiveFeign.FeignInvocationHandler#invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object
otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// be based on dispatch Find the corresponding MethodHandler, call invoke Method
return dispatch.get(method).invoke(args);
}
2、 Let's take a look at SynchronousMethodHandler Specific calling process
@Override
public Object invoke(Object[] argv) throws Throwable {
// Build based on parameters RequestTemplate
RequestTemplate template = buildTemplateFromArgs.create(argv);
// Clone the class of retry mechanism
Retryer retryer = this.retryer.clone();
while (true) {
try {
// Perform calling and decoding
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template) throws Throwable {
// obtain Request object
// Here is also the implementation of the custom interceptor
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// adopt client To initiate
response = client.execute(request, options);
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
}
//
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return Response.create(response.status(), response.reason(), response.headers(), bodyData);
}
// Successful decoding returns
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404) {
return decoder.decode(response, metadata.returnType());
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
// Response flow closes
ensureClosed(response.body());
}
}
}
You can check the definitions of some core classes link To learn
Operation examples can be viewed feign Custom request interceptor 、 Encoder 、 decoder
OpenFeign Interceptor RequestInterceptor Use
边栏推荐
- Common weak password Encyclopedia
- 【正则】判断, 手机号,身份证号
- Redis源码学习(33),命令执行过程
- easyui中textbox在光标位置插入内容
- redis入门练习
- [flask] the server obtains the file requested by the client
- Penetration test - post penetration - Trace cleaning
- 智能体重秤方案主控采用CSU18M91
- 微信小程序生成Excel
- The function and application of lpci-252 universal PCI interface can card
猜你喜欢

注解@Autowired和@Resource的区别总结

Practice of online problem feedback module (XV): realize the function of online updating feedback status

数字孪生应用及意义对电力的主要作用,概念价值。

架构基本概念和架构本质

带你了解什么是 Web3.0

Take you to know what Web3.0 is
![[从零开始学习FPGA编程-54]:高阶篇 - 基于IP核的FPGA开发-PLL锁相环IP核的原理与配置(Altera)](/img/4f/f75cfeb4422120ef9ac70cdeb0a840.png)
[从零开始学习FPGA编程-54]:高阶篇 - 基于IP核的FPGA开发-PLL锁相环IP核的原理与配置(Altera)

Win10/win11 lossless expansion of C disk space, cross disk consolidation of C and e disks

Graphic SQL, this is too vivid!

redis入门练习
随机推荐
How can you access the domestic server and overseas server quickly with one database?
智能体重秤方案主控采用CSU18M91
docker 创建mysql 8.x容器,支持mac ,arm架构芯片
Activiti5.22.0 extension supports domestic databases, taking gbase database as an example
Spark Learning Notes (IV) -- spark core programming RDD
常见弱口令大全
Jmeter分布式压测
国内服务器与海外服务器用1个数据库,怎样可以访问的快?
unity游戏,隐私协议最简单解决方案!仅3行代码就搞定!(转载)
mysql如何优化
How many implementation postures of delay queue? Daily essential skills!
Wechat applet generation Excel
PIP3 setting alicloud
如何进行 360 评估
[flask] the server obtains the file requested by the client
It's too strong. An annotation handles the data desensitization returned by the interface
LPCI-252通用型PCI接口CAN卡的功能和应用介绍
网络安全/渗透测试工具AWVS14.9下载/使用教程/安装教程
be based on. NETCORE development blog project starblog - (16) some new functions (monitoring / statistics / configuration / initialization)
深入理解Mysql索引底层数据结构与算法