This is the 19th day of my participation in the Genwen Challenge
One, foreword
The interfaces declared by OpenFeign, whether SpringMVC or JAX-RS, are parsed into MethodMetadata by the underlying layer, which generates proxies for the interfaces through dynamic proxies, and makes Rest calls based on MethodMetadata.
Background: In the Spring Cloud integration.
Steps 1-3 create Feign and steps 4-5 request processing mechanism.
Feign
Injection via packet scanFeignClient
的 Bean
, the source code inFeignClientsRegistrar
Class:
-
When the program starts, it checks for the @enableFeignClients annotation, and if it does, it starts a packet scan for interfaces annotated by @FeignClient.
-
When an application has the @enableFeignClients annotation on its startup class. After the program starts, the program takes the @FeignClient annotated interface, along with the interface name and annotation information, and assigns it to BeanDefinitionBuilder through a package scan. Then BeanDefinition is obtained according to BeanDefinitionBuilder, and finally injected into the IOC container.
-
After injecting BeanDefinition, when a method is called in the Feign Client interface through the JDK proxy, the method will be intercepted, and the source code is in the ReflectiveFeign class.
-
Interception in the SynchronousMethodHandler class generates a RequestTemplate object based on the parameters, which is the template for the HTTP request
-
The executeAndDecode() method, which generates a Request object from a RequestTemplate and then uses the Http Client to get a Response, that is, an Http Request from the Http Client
Second, direct hate source code
Before the discovery of the source code, is to draw a similar flow chart, but the feeling is not particularly good, it is not convenient to review later, this time try the sequence diagram.
Create Feign sequence diagram as follows:
Steps:
@EnableFeignClients
Annotations provide package names and@FeignClient
Annotated interfaces find all interfaces- Based on these interface constructs
FeignClientFactoryBean
thisFactoryBean
FactoryBean
The object that is really constructed internally is oneProxy
theProxy
Is through theTargeter#target
Constructed.Targeter
Internal structure throughFeign.Builder#build
Method completes,build
The one returned by theFeign
Object.
From the first@EnableFeignClients
Start with:
/ / positioning: org. Springframework. Cloud. Openfeign
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// Important: Import Bean
// The FeignClientsRegistrar is the registrar
@Import(FeignClientsRegistrar.class)
public @interfaceEnableFeignClients { ... . }Copy the code
Take a look atFeignClientsRegistrar
:
/ / positioning: org. Springframework. Cloud. Openfeign
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar.ResourceLoaderAware.EnvironmentAware {
// Spring will inject parameters and call this method
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 1. Register the default configuration
registerDefaultConfiguration(metadata, registry);
// 2. Scan @feignClient and register
registerFeignClients(metadata, registry);
}
// 1. Register the default configuration
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// Get the values of all attributes of the @enableFeignClients annotation
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if(defaultAttrs ! =null && defaultAttrs.containsKey("defaultConfiguration")) {
// Get the fully qualified name of the Application startup class:
// default.cn.donald.dolphin.oceanfile.OceanFileApplication
// ...
// Register in BeanDefinitionRegistry
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration")); }}// 2. Scan @feignClient and register
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 2.1 Get scanner: Used to scan the Bean class associated with the specified condition in the specified path
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
/ / 2.2
/ /... A large pile of code: used to scan the specified packet path, there will be a filter for the specified filter
// 2.3 Processing package path
for (String basePackage : basePackages) {
// This is the candidate BeanDeifinition (@feignClient) annotation
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 2.3.1 Checking whether the interface is an interface
// 2.3.2 Obtaining attributes in @feignClient
// ...
// 2.3.3 Registering configuration
registerClientConfiguration(registry, name,
attributes.get("configuration"));
2.3.4 Registering this feignClientregisterFeignClient(registry, annotationMetadata, attributes); }}}}2.3.4 Registering this feignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map
attributes)
,> {
Important: / /
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
/ /... A bunch of configuration Settings
/ /... Register with the Spring container}}Copy the code
Look atFeignClientFactoryBean
:
/ / positioning: org. Springframework. Cloud. Openfeign
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean.ApplicationContextAware {
// Reload the method where the entry is:
@Override
public Object getObject(a) throws Exception {
return getTarget();
}
<T> T getTarget(a) {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
/ /... A bunch of URLS, HTTP processing
// target manifest dynamic proxy:
// Get instance first
Targeter targeter = get(context, Targeter.class);
// regenerates the corresponding
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url)); }}Copy the code
Targeter.target () defaults to DefaultTargeter:
/ / positioning: org. Springframework. Cloud. Openfeign
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget
target)
{
returnfeign.target(target); }}// Enter the feign class
// Location: feign
public abstract class Feign {
/ / 1
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
// 2. Subclass ReflectiveFeign
public abstract <T> T newInstance(Target<T> target);
}
// By default, 'ReflectiveFeign' is returned as a subclass of the 'Feign' object
// Enter the ReflectiveFeign class
// Location: feign
public class ReflectiveFeign extends Feign {
@Override
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(),
newClass<? >[] {target.type()}, handler);for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
returnproxy; }}Copy the code
As you can see from this code, the JDK’s built-in dynamic Proxy classes, InvocationHandler and Proxy, do this.