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.

FeignInjection via packet scanFeignClientBean, the source code inFeignClientsRegistrarClass:

  1. When the program starts, it checks for the @enableFeignClients annotation, and if it does, it starts a packet scan for interfaces annotated by @FeignClient.

  2. 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.

  3. 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.

  4. Interception in the SynchronousMethodHandler class generates a RequestTemplate object based on the parameters, which is the template for the HTTP request

  5. 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:

  1. @EnableFeignClientsAnnotations provide package names and@FeignClientAnnotated interfaces find all interfaces
  2. Based on these interface constructsFeignClientFactoryBeanthisFactoryBean
  3. FactoryBeanThe object that is really constructed internally is oneProxytheProxyIs through theTargeter#targetConstructed.TargeterInternal structure throughFeign.Builder#buildMethod completes,buildThe one returned by theFeignObject.

From the first@EnableFeignClientsStart 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.