This is the 20th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021″

preface

In the previous analysis we know:

  • As long as it is @ the Import, so the final execution is necessarily ImportBeanDefinitionRegistrar, otherwise it will continue to recursion, down until it is this class.

Here we deal with the leftovers:

Class: [ImportBeanDefinitionRegistrar]

  • How are subclasses implemented?
  • Where is it processed?
  • ConfigurationClass class, loadBeanDefinitionsForConfigurationClass where, when the called?

Subclass implementation and interface definition

Let’s start with the methods given in the interface:

public interface ImportBeanDefinitionRegistrar {

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}
	
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {}}Copy the code

You can’t tell much from here, except that two methods are overloaded and the one above calls the one below, without using the bean name constructor above.

There are only two classes to rewrite, which are closely related to SpringData:

  • AbstractRepositoryConfigurationSourceSupport
  • RepositoryBeanDefinitionRegistrarSupport
  • AbstractRepositoryConfigurationSourceSupport

This class is also not a concrete implementation class, but an abstract class, but it basically provides most of the functionality for storing data and integrating SpringData:

public abstract class AbstractRepositoryConfigurationSourceSupport
      implements ImportBeanDefinitionRegistrar.BeanFactoryAware.ResourceLoaderAware.EnvironmentAware {}Copy the code

So we’ll focus on the following subclass override of this method to see what happens.

Before looking at subclass method overrides, take a look at the comment for this method:

Register bean definitions as necessary based on the given annotation metadata of
the importing @Configuration class.
Note that BeanDefinitionRegistryPostProcessor types may not be
registered here, due to lifecycle constraints related to @Configuration class processing.
Copy the code

In simple terms, this method registers the bean definition as needed, based on the given annotation metadata imported into the @Configuration class.

Remember the beginning? Let’s take OpenFeign as an example.

FeignClientsRegisrar

One thing to note here is that this class is imported into the Spring container by @import in the EnableFeignClients annotation:

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/ /...
}
Copy the code

To get straight to the overriding method of the above subclass:

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   registerDefaultConfiguration(metadata, registry);
   registerFeignClients(metadata, registry);
}
Copy the code

Register the default configuration

registerDefaultConfiguration(metadata, registry);

	private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // The metadata interrupts refer to classes that are started and those that are collected as annotated by feignClient annotations
		Map<String, Object> defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if(defaultAttrs ! =null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration")); }}private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}
Copy the code

The code here looks easy throughout:

  • Configuration refers to the @enableFeignClients defaultConfiguration, if not configured.
  • This is the process of building a BeanDefinition into the context based on the given metadata. The beanDefinition here corresponds to the corresponding configuration in the @EnableFeignClients annotation of the start class annotation. If it is not configured, this Configuration is useless (because it will be placed in the container, but the properties are empty).

Registered feign class

In this method, all interfaces in the package labeled ** @feignClient ** are initialized here.

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // This canner is a scan based on classpath
   ClassPathScanningCandidateComponentProvider scanner = getScanner();
   scanner.setResourceLoader(this.resourceLoader);

    // Here is the top-level directory of packages to scan
   Set<String> basePackages;

   Map<String, Object> attrs = metadata
         .getAnnotationAttributes(EnableFeignClients.class.getName());
   AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
         FeignClient.class);

    
   finalClass<? >[] clients = attrs ==null ? null: (Class<? >[]) attrs.get("clients");
    // If you can't find clients in the enable annotation, use metadata to find the corresponding top-level directory name
   if (clients == null || clients.length == 0) {
      scanner.addIncludeFilter(annotationTypeFilter);
      basePackages = getBasePackages(metadata);
   }
   else {
      final Set<String> clientClasses = new HashSet<>();
      basePackages = new HashSet<>();
      for(Class<? > clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } AbstractClassTestingTypeFilter filter =new AbstractClassTestingTypeFilter() {
         @Override
         protected boolean match(ClassMetadata metadata) {
            String cleaned = metadata.getClassName().replaceAll(\ \ "$".".");
            returnclientClasses.contains(cleaned); }}; scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
   }

    // This is to find the interfaces corresponding to the feignClient according to the package of the top-level directory, and put the information of these interfaces into the container
    // feignClient, the corresponding beanDefinition is derived from FeignClientFactoryBean
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidateComponents = scanner
            .findCandidateComponents(basePackage);
      for (BeanDefinition candidateComponent : candidateComponents) {
         if (candidateComponent instanceof AnnotatedBeanDefinition) {
            // verify 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");

            Map<String, Object> attributes = annotationMetadata
                  .getAnnotationAttributes(
                        FeignClient.class.getCanonicalName());

            String name = getClientName(attributes);
            registerClientConfiguration(registry, name,
                  attributes.get("configuration"));
		// This method is converted and registered, and the interesting thing is that it also initializes the ConfigurationregisterFeignClient(registry, annotationMetadata, attributes); }}}}Copy the code

summary

You can see it here:

  • In effect, these importBdregistrArs receive parameters in the specified schema and register the corresponding concepts and information (such as feignClients above) as BeanDefinitions into the container.

    This might involve scanning, factoryBeans, and so on.

  • Here are some points to explore further, including:

    • Now that Feign’s beanDefinition is actually registered as a FeignClientFactoryBean, how is it invoked?
    • At which step of bean instantiation is this BDRegistrar called?