当前位置:网站首页>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
边栏推荐
猜你喜欢

Detailed explanation of const usage in C language

477-82(236、61、47、74、240、93)

Mysql: summary of common sub database and sub table schemes of Internet companies

MySQL has a nonexistent error
![[1206. Design skip table]](/img/a9/ca45c9fedd6e48387821bdc7ec625c.png)
[1206. Design skip table]

Introduction to database - a brief introduction to MySQL

Duplicate disc: what are the basic attributes of an image? What do you know about images? What are the parameters of the image

The function and application of lpci-252 universal PCI interface can card

MySQL中文失败问题

app端接口用例设计方法和测试方法
随机推荐
Member array and pointer in banyan loan C language structure
Activiti5.22.0 extension supports domestic databases, taking gbase database as an example
数据库使用安全策略
JMeter distributed pressure measurement
MySQL underlying data structure
unity游戏,隐私协议最简单解决方案!仅3行代码就搞定!(转载)
【学习笔记之菜Dog学C】字符串+内存函数
mysql底层数据结构
FastBoot brush machine
shell awk
Does Oracle have a distributed database?
复盘:DFS与BFS的主要区别,在思想上的区别,代码实现上的区别
Explain tool actual operation
MySQL中文失败问题
Database usage security policy
[learn FPGA programming from scratch -54]: high level chapter - FPGA development based on IP core - principle and configuration of PLL PLL IP core (Altera)
J-3-point practice in the second game of 2022 Niuke multi school
智能体重秤方案主控采用CSU18M91
volatile关键字及其作用
Introduction to database - Introduction to database