An overview of the

Our Spring Boot applications often have some custom configuration in the application.yml configuration file, which sets different values for different environments. These configurations can then be injected as property values of the Spring Bean through the @ConfigurationProperties annotation, so this article briefly looks at how this annotation automatically sets the configuration to the Spring Bean.

Before I start, you should know how this works in light of my previous spring-related source analysis articles. This is done by parsing the annotation through the BeanPostProcessor at some point during the Spring Bean’s loading process (most likely during initialization). And get the corresponding property value set to it.

Take a look at this note first

@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
	/** * Specifies the configuration item prefix */
	@AliasFor("prefix")
	String value(a) default "";

	/** * Specifies the configuration item prefix */
	@AliasFor("value")
	String prefix(a) default "";

	/** * Whether to ignore invalid fields */
	boolean ignoreInvalidFields(a) default false;

	/** * Whether to ignore unknown fields */
	boolean ignoreUnknownFields(a) default true;
}
Copy the code

There are two ways to use it:

  • @ConfigurationProperties + @ComponentAnnotation (a class)
  • @EnableConfigurationProperties(a Bean) +@ConfigurationPropertiesAnnotations (another common class)

The second approach works the same way as the first, but registers a BeanPostProcessor to handle Spring beans with @ConfigurationProperties annotations, It also resolves the specified classes to BeanDefinition (the precursor of the Bean) and registers them. That’s why the second Class doesn’t use the @Component annotation

So where is the first way to register BeanPostProcessor? Because Spring Boot have a ConfigurationPropertiesAutoConfiguration automatic configuration class, as follows:

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration { }
Copy the code

Is very simple, is also the BeanPostProcessor objects by @ EnableConfigurationProperties annotations to register

Here’s a question: why don’t we just add an @Component to the @ConfigurationProperties annotation?

Perhaps this is because the purpose of this annotation is to externalize the configuration from the configuration class

@EnableConfigurationProperties

Org. Springframework. Boot. The context. The properties. EnableConfigurationProperties, Support for resolving specified classes with @ConfigurationProperties annotations to BeanDefinition (the precursor of the Bean) and registering them, Also register a BeanPostProcessor to handle beans with @ConfigurationProperties annotations

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {

	/** * The bean name of The configuration Properties validator. * @since 2.2.0 */
	String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

	/** * the specified Class objects */Class<? >[] value()default {};
}
Copy the code

As you can see, this annotation also uses the @import annotation to drive a function. Do you notice that the @enablexxx driver annotation is implemented in this way

So I’ve mentioned how @import annotations work in a number of places, but again, module-driven annotations usually need to be used in conjunction with @Configuration annotations, because they need to be treated as a Configuration class and then parsed to the @import annotation. There are three cases for the value of the @import annotation:

  1. This Class implements the ImportSelector interface, calling its selectImports(..). Method gets the name of the Class object to be processed, which can be managed as a Bean by Spring IoC

    • The Class object is implementedDeferredImportSelectorInterfaces, and upper execution timing is different at allThe configuration classExecute after processing, and support@OrderThe sorting
  2. The Class object implements ImportBeanDefinitionRegistrar interface, can call its registerBeanDefinitions (..) Method to customarily register BeanDefinition (the precursor of the Bean) with the BeanDefinitionRegistry registry

  3. The Class object is an @Configuration Configuration Class that will be managed as a Bean by Spring IoC

If you are not familiar with @import annotations, check out my previous article on how @Bean annotations are implemented

Here @ EnableConfigurationProperties annotations, Through @ Import Import EnableConfigurationPropertiesRegistrar this class (implements ImportBeanDefinitionRegistrar interface) to realize the function, the following will be analyzed

EnableConfigurationPropertiesRegistrar

Org. Springframework. Boot. The context. The properties. EnableConfigurationPropertiesRegistrar, implements the ImportBeanDefinitionRegistrar interface, Is the core of the @ EnableConfigurationProperties annotation class

class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		// <1> First register two internal beans
		registerInfrastructureBeans(registry);
		/ / < 2 > create a ConfigurationPropertiesBeanRegistrar object
		ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
		/ / < 3 > get ` @ EnableConfigurationProperties ` annotations to specify the Class Class object
		// <4> Register the beanDefinitions of the specified Class in turn
		// This Class can inject the configuration property object without the '@Component' tag
		getTypes(metadata).forEach(beanRegistrar::register);
	}

	privateSet<Class<? >> getTypes(AnnotationMetadata metadata) {return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
				.flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
				.filter((type) -> void.class ! = type).collect(Collectors.toSet()); }/ * * * for reference ConfigurationPropertiesAutoConfiguration automatic configuration class * /
	@SuppressWarnings("deprecation")
	static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
		/ / registered a BeanDefinition ConfigurationPropertiesBindingPostProcessor type (internal), if it doesn't exist
		/ / will also register ConfigurationPropertiesBinder and ConfigurationPropertiesBinder. Two Bean Factory, if does not exist
		ConfigurationPropertiesBindingPostProcessor.register(registry);
		/ / registered a BeanDefinition ConfigurationBeanFactoryMetadata type (internal)
		// This Bean is deprecated since Spring 2.2.0
		ConfigurationBeanFactoryMetadata.register(registry); }}Copy the code

The process for registering a BeanDefinition (a precursor of a Bean) is as follows:

  1. Start by registering two internal beans

    • Registered a BeanDefinition ConfigurationPropertiesBindingPostProcessor type (internal), if it doesn’t exist
public static void register(BeanDefinitionRegistry registry) {
            Assert.notNull(registry, "Registry must not be null");
            / / register ConfigurationPropertiesBindingPostProcessor type BeanDefinition (internal)
            if(! registry.containsBeanDefinition(BEAN_NAME)) { GenericBeanDefinition definition =new GenericBeanDefinition();
                definition.setBeanClass(ConfigurationPropertiesBindingPostProcessor.class);
                definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                registry.registerBeanDefinition(BEAN_NAME, definition);
            }
            / / register ConfigurationPropertiesBinder and ConfigurationPropertiesBinder. Factory two BeanDefinition (internal)
            ConfigurationPropertiesBinder.register(registry);
        }
Copy the code
* registered a BeanDefinition ConfigurationBeanFactoryMetadata type (internal), from Spring 2.2.0 beginning were abandoned, ignoredCopy the code
  1. Create a ConfigurationPropertiesBeanRegistrar object

  2. Get @ EnableConfigurationProperties annotations to specify the Class Class object

  3. Call ConfigurationPropertiesBeanRegistrar register (Class
    type), which in turn registers the BeanDefinition of the specified Class so that the Class can inject the configuration property object without the @Component annotation

ConfigurationPropertiesBeanRegistrar

Org. Springframework. Boot. The context. The properties. ConfigurationPropertiesBeanRegistrar, Is EnableConfigurationPropertiesRegistrar auxiliary class

final class ConfigurationPropertiesBeanRegistrar {

	private final BeanDefinitionRegistry registry;

	private final BeanFactory beanFactory;

	ConfigurationPropertiesBeanRegistrar(BeanDefinitionRegistry registry) {
		this.registry = registry;
		this.beanFactory = (BeanFactory) this.registry;
	}

	void register(Class
        type) {
		// <1> First get the @ConfigurationProperties annotation for this Class object
		MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations
				.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
		// <2> Register a BeanDefinition for this Class object
		register(type, annotation); }}Copy the code

The process is as follows:

  1. Get the @ConfigurationProperties annotation for this Class object

  2. Call register (..) Method to register a BeanDefinition for this Class object

void register(Class
        type, MergedAnnotation
       
         annotation)
        {
        // <1> Generate a Bean name for the '@ConfigurationProperties' annotation' ${prefix}- class full ', or 'class full name'
        String name = getName(type, annotation);
        if(! containsBeanDefinition(name)) {// <2> If there is no Bean with that name, register a BeanDefinition of type 'type'registerBeanDefinition(name, type, annotation); }}private String getName(Class
        type, MergedAnnotation
       
         annotation)
        {
        String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";
        return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());
    }
Copy the code
1. Generate a Bean name as' @configurationProperties' annotated '${prefix}- class full' or 'class full name' 2. If there is no Bean with that name, register a BeanDefinition of type 'type'Copy the code

RegisterBeanDefinition method

Register the Class object with the @ConfigurationProperties annotation

private void registerBeanDefinition(String beanName, Class
        type, MergedAnnotation
       
         annotation)
        {
    // The Class object must have an '@ConfigurationProperties' annotation
    Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName()
            + " annotation found on '" + type.getName() + "'.");
    // Register a GenericBeanDefinition with 'type' as beanClass
    this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
}

private BeanDefinition createBeanDefinition(String beanName, Class
        type) {
    if (BindMethod.forType(type) == BindMethod.VALUE_OBJECT) {
        return new ConfigurationPropertiesValueObjectBeanDefinition(this.beanFactory, beanName, type);
    }
    // Create a GenericBeanDefinition object with Class set to type
    GenericBeanDefinition definition = new GenericBeanDefinition();
    definition.setBeanClass(type);
    return definition;
}
Copy the code

The logic is simple: The @ConfigurationProperties annotation Class object generates a BeanDefinition and registers it

ConfigurationPropertiesBindingPostProcessor

Org. Springframework. Boot. The context. The properties. ConfigurationPropertiesBindingPostProcessor, Bind the configuration to the configuration class of the @ConfigurationProperties annotation

public class ConfigurationPropertiesBindingPostProcessor
		implements BeanPostProcessor.PriorityOrdered.ApplicationContextAware.InitializingBean {

	public static final String BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class.getName();

	/** * The bean name of The configuration properties validator. * @deprecated since 2.2.0 in favor of * {@link EnableConfigurationProperties#VALIDATOR_BEAN_NAME} */
	@Deprecated
	public static final String VALIDATOR_BEAN_NAME = EnableConfigurationProperties.VALIDATOR_BEAN_NAME;

	/** Spring application context */
	private ApplicationContext applicationContext;

	/** BeanDefinition registry */
	private BeanDefinitionRegistry registry;

	/** Attribute binder */
	private ConfigurationPropertiesBinder binder;

	/ * * * Create a new {@ link ConfigurationPropertiesBindingPostProcessor} instance. * @ deprecated since 2.2.0 in favor of *  {@link EnableConfigurationProperties @EnableConfigurationProperties} or * {@link ConfigurationPropertiesBindingPostProcessor#register(BeanDefinitionRegistry)} */
	@Deprecated
	public ConfigurationPropertiesBindingPostProcessor(a) {}}Copy the code

SetApplicationContext method

ApplicationContextAware callback

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    // Aware interface callback to get Spring application context
    this.applicationContext = applicationContext;
}
Copy the code

The afterPropertiesSet method

InitializingBean initialization method

/** * Initializes the current Bean */
@Override
public void afterPropertiesSet(a) throws Exception {
    // We can't use constructor injection of the application context because
    // it causes eager factory bean initialization
    // Get the BeanDefinition registry from the Spring application context
    this.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory();
    / / get ConfigurationPropertiesBinder this Bean, in this class ` register ` method to register the oh
    this.binder = ConfigurationPropertiesBinder.get(this.applicationContext);
}
Copy the code

GetOrder method

PriorityOrdered priority

// Second to the highest priority
@Override
public int getOrder(a) {
    return Ordered.HIGHEST_PRECEDENCE + 1;
}
Copy the code

1. PostProcessBeforeInitialization method

Pre-initialization operations for BeanPostProcessor

/** * this method is called before the Bean is initialized * reference {@link AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization(Object, String)} */
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    / / < 1 > try according to Bean parsing out a ConfigurationPropertiesBean object, contains ` @ ConfigurationProperties ` annotation information
    // <2> Then start getting the property value of the specified 'prefix' and set it to the Bean
    bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
    // <3> Returns the Bean populated with properties
    return bean;
}
Copy the code

The process is as follows:

  1. callConfigurationPropertiesBean#get(..)Method to try and according to the Bean parsing out a ConfigurationPropertiesBean object, contains@ConfigurationPropertiesAnnotation information
  2. callbind(..)Method to start getting the specifiedprefixPrefix property value, set to the Bean
  3. Returns the Bean populated with properties

4. The bind method

private void bind(ConfigurationPropertiesBean bean) {
    // <1> If the 'bean' is empty or has already been processed, it returns directly
    if (bean == null || hasBoundValueObject(bean.getName())) {
        return;
    }
    // <2> Validation of '@constructorBinding', where an exception is thrown if the annotation is used but no suitable constructor is found
    Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"
            + bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
    try {
        // <3> Bind the Bean with the specified 'prefix' attribute to Binder. Bind the Bean with the specified 'prefix' attribute to Binder.
        this.binder.bind(bean);
    }
    catch (Exception ex) {
        throw newConfigurationPropertiesBindException(bean, ex); }}Copy the code

Can see the final by ConfigurationPropertiesBinder binder to attribute is bound to the bean

ConfigurationPropertiesBean

Org. Springframework. Boot. The context. The properties. ConfigurationPropertiesBean, is @ ConfigurationProperties annotation corresponding Bean packaging, Used to bind the corresponding property value to the Bean

public final class ConfigurationPropertiesBean {
	/** * The name of the Bean */
	private final String name;
	/** * the instance object of the Bean */
	private final Object instance;
	/** * the '@configurationProperties' annotation for the Bean */
	private final ConfigurationProperties annotation;
	/** * '@bean' corresponds to the method resource object, including the instance object and annotation information */
	private finalBindable<? > bindTarget;/** * '@bean' corresponds to the method */
	private final BindMethod bindMethod;

	private ConfigurationPropertiesBean(String name, Object instance, ConfigurationProperties annotation, Bindable
        bindTarget) {
		this.name = name;
		this.instance = instance;
		this.annotation = annotation;
		this.bindTarget = bindTarget;
		this.bindMethod = BindMethod.forType(bindTarget.getType().resolve()); }}Copy the code

Refer to the comments above for a description of each property

2. The get method

To obtain a @ ConfigurationProperties ConfigurationPropertiesBean annotation corresponding Bean

public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) {
    // <1> Find the factory method corresponding to the 'beanName'. For example, the method labeled '@bean' is a factory method. It is empty if it is not '@bean'
    Method factoryMethod = findFactoryMethod(applicationContext, beanName);
    / / < 2 > create a ConfigurationPropertiesBean object that contains the Bean ` @ ConfigurationProperties ` annotation information
    return create(beanName, bean, bean.getClass(), factoryMethod);
}
Copy the code

The process is as follows:

  1. To find thebeanNameCorresponding factory method, for example@BeanThe labeled method is a factory method, not@BeanThis is empty
  2. callcreate(..)Methods, create a ConfigurationPropertiesBean object, contains the Bean@ConfigurationPropertiesAnnotation information

3. The create method

private static ConfigurationPropertiesBean create(String name, Object instance, Class
        type, Method factory) {
    // <1> Find the '@ConfigurationProperties' annotation on this Bean
    // If it is a method Bean annotated with '@bean', it will also try to fetch it from its Class
    ConfigurationProperties annotation = findAnnotation(instance, type, factory, ConfigurationProperties.class);
    // <2> Null is returned if the @configurationProperties annotation is not configured
    if (annotation == null) {
        return null;
    }
    // <3> Find the '@Validated' annotation on this Bean
    Validated validated = findAnnotation(instance, type, factory, Validated.class);
    // <4> Bind the '@ConfigurationProperties',' Validated 'annotation information, the target Bean, and its Class object to a Bindable objectAnnotation[] annotations = (validated ! = null) ?new Annotation[] { annotation, validated }
            : newAnnotation[] { annotation }; ResolvableType bindType = (factory ! = null) ? ResolvableType.forMethodReturnType(factory) : ResolvableType.forClass(type); Bindable<Object> bindTarget = Bindable.of(bindType).withAnnotations(annotations);if(instance ! = null) { bindTarget = bindTarget.withExistingValue(instance); }/ / < 5 > will ` beanName `, target Bean, ` ConfigurationProperties ` annotations, step 4 ` ` Bindable object wrapped in a ConfigurationPropertiesBean object
    return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);
}
Copy the code

The process is as follows:

  1. Find the one above the Bean@ConfigurationPropertiesNotes, if yes@BeanThe annotated method Bean will also try to fetch it from its Class
  2. If not configured@ConfigurationPropertiesAnnotation, returns directlynull
  3. Find the one above the Bean@Validatedannotations
  4. will@ConfigurationProperties,ValidatedThe annotation information, the target Bean and its Class object, are bound to a Bindable object
  5. willbeanName, target Bean,ConfigurationPropertiesNote, the first4Step of Bindable object wrapped in a ConfigurationPropertiesBean object

ConfigurationPropertiesBinder

Org. Springframework. Boot. The context. The properties. ConfigurationPropertiesBinder, properties of ConfigurationPropertiesBean binding

5. The bind method

To attribute ConfigurationPropertiesBean binding, as follows:

BindResult<? >bind(ConfigurationPropertiesBean propertiesBean) {
    // <1> Get the Bindable object for this Bean (containing the '@ConfigurationProperties',' @Validated 'configuration information and the Bean)Bindable<? > target = propertiesBean.asBindTarget();// <2> Get the '@ConfigurationProperties' annotation information for this Bean
    ConfigurationProperties annotation = propertiesBean.getAnnotation();
    // <3> get a BindHandler binding handler
    BindHandler bindHandler = getBindHandler(target, annotation);
    // <4> Get a Binder object that contains all configuration information for the Spring application context, placeholder handlers, and type converters
    // <5> Bind the Bean with the specified 'prefix' property by Binder to the Bean with the Conversion type converter.
    return getBinder().bind(annotation.prefix(), target, bindHandler);
}
Copy the code

The process is as follows:

  1. Gets the Bindable object for the Bean (containing the @ConfigurationProperties, @Validated configuration information, and the Bean)

  2. Gets the @ConfigurationProperties annotation information for this Bean

  3. Gets a BindHandler binding handler

private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperties annotation) {
        // <1> Get several validators
        List<Validator> validators = getValidators(target);
        // <2> Create a top-level BindHandler
        BindHandler handler = new IgnoreTopLevelConverterNotFoundBindHandler();
        // <3> If invalid fields are ignored (default 'false')
        if (annotation.ignoreInvalidFields()) {
            handler = new IgnoreErrorsBindHandler(handler);
        }
        // <4> If you do not ignore the unknown field (default will not enter here)
        if(! annotation.ignoreUnknownFields()) { UnboundElementsSourceFilter filter =new UnboundElementsSourceFilter();
            handler = new NoUnboundElementsBindHandler(handler, filter);
        }
        // <5> If the Validator is not empty, encapsulate it into a ValidationBindHandler object that holds the validators
        if(! validators.isEmpty()) { handler =new ValidationBindHandler(handler, validators.toArray(new Validator[0]));
        }
        / / < 6 > obtain ConfigurationPropertiesBindHandlerAdvisor ` handler ` application, ignored
        for (ConfigurationPropertiesBindHandlerAdvisor advisor : getBindHandlerAdvisors()) {
            handler = advisor.apply(handler);
        }
        // <7> returns this' handler 'configuration binding handler
        return handler;
    }

    private List<Validator> getValidators(Bindable
        target) {
        List<Validator> validators = new ArrayList<>(3);
        if (this.configurationPropertiesValidator ! = null) { validators.add(this.configurationPropertiesValidator);
        }
        if (this.jsr303Present && target.getAnnotation(Validated.class) ! = null) { validators.add(getJsr303Validator()); }if(target.getValue() ! = null && target.getValue().get() instanceof Validator) { validators.add((Validator) target.getValue().get()); }return validators;
    }
Copy the code
  1. Gets a Binder object that contains all configuration information, placeholder handlers, and type converters for the Spring application context
private Binder getBinder(a) {
        if (this.binder == null) {
            this.binder = new Binder(getConfigurationPropertySources(), // The Spring application's PropertySource property resource
                    getPropertySourcesPlaceholdersResolver(), // Placeholder handler
                    getConversionService(),  // Type converter
                    getPropertyEditorInitializer(), // Property editor
                    null,
                    ConfigurationPropertiesBindConstructorProvider.INSTANCE);
        }
        return this.binder;
    }
Copy the code
  1. Through thisBinderWill specifyprefixThe attribute value of the prefix is set to the Bean, which is converted using the ConversionService type converter

The whole processing process is mainly in the fifth step, which is a bit complicated, with the help of Binder Binder, which is not described here. If you are interested, you can go to 😄

snacks

When we were writing the application.yml file, did the IDE give you many options to choose from when you typed a letter? Meta-inf /spring-configuration-metadata.json, meta-INF /additional spring-configuration-metadata.json In these two files you can define the configuration information you need, such as Spring Boot provides:

{
  "groups": [{"name": "logging"."type": "org.springframework.boot.context.logging.LoggingApplicationListener"}]."properties": [{"name": "logging.config"."type": "java.lang.String"."description": "Location of the logging configuration file. For instance, `classpath:logback.xml` for Logback."."sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
    },
    {
      "name": "spring.application.name"."type": "java.lang.String"."description": "Application name."."sourceType": "org.springframework.boot.context.ContextIdApplicationContextInitializer"
    },
    {
      "name": "spring.profiles"."type": "java.util.List<java.lang.String>"."description": "Comma-separated list of profile expressions that at least one should match for the document to be included."."sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener"
    },
    {
      "name": "spring.profiles.active"."type": "java.util.List<java.lang.String>"."description": "Comma-separated list of active profiles. Can be overridden by a command line switch."."sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener"}]."hints": [{"name": "logging.level.values"."values": [{"value": "trace"
        },
        {
          "value": "debug"
        },
        {
          "value": "info"
        },
        {
          "value": "warn"
        },
        {
          "value": "error"
        },
        {
          "value": "fatal"
        },
        {
          "value": "off"}]."providers": [{"name": "any"}]}]}Copy the code

This is just a partial list, and you can see that the name, type, description, and source of each configuration is defined, as well as the values that each configuration can enter, so that we can quickly enter the desired configuration items in the IDE.

This file is generated by the spring-boot-configuration-processor tool module provided by Spring Boot. With the aid of SPI mechanism is configured with a ConfigurationMetadataAnnotationProcessor annotations processor, it inherits javax.mail. The annotation. Processing. AbstractProcessor abstract classes. So this handler, at compile time, parses each class that the @ConfigurationProperties annotation annotates, Json file: meta-INF /spring-configuration-metadata.json file: meta-inf /spring-configuration-metadata.json file: meta-inf /spring-configuration-metadata.json file: meta-inf /spring-configuration-metadata.json file: meta-inf /spring-configuration-metadata.json file: meta-inf /spring-configuration-metadata.json file: meta-inf /spring-configuration-metadata.json

Also, when we use the @ConfigurationProperties annotation, the IDE prompts us to introduce this utility class:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency></code-pre> 

</pre></code-box> 
Copy the code

You can refer to the official Spring Boot documentation for this section

conclusion

This article analyzes the implementation of the @ConfigurationProperties annotation in Spring Boot. The principle is that a BeanPostProcessor is registered to preprocess the Spring Bean initialization when it is loaded. Parse out the @ConfigurationProperties annotation and find the property value for the corresponding prefix to bind to the Bean.

There are two ways to use this annotation:

  • @ConfigurationProperties + @ComponentAnnotation (a class)
  • @EnableConfigurationProperties(a Bean) +@ConfigurationPropertiesAnnotations (another common class)

About @ EnableConfigurationProperties annotation process is simple, through @ Import annotation methods, Register a BeanPostProcessor Bean that handles the @ConfigurationProperties annotation, The specified Class object with the @ConfigurationProperties annotation is also registered with the Spring IoC container. Is that why the @Component annotation is not used

On the above the first way is through a ConfigurationPropertiesAutoConfiguration automatic configuration by @ EnableConfigurationProperties annotations to register the BeanPostProcessor The Bean that handles the @ConfigurationProperties annotation

After learning Spring Boot source code, I think it is very helpful, so that I can clearly understand the operation principle of Sprig Boot application, and it will be easier to deal with problems and tune. In addition, writing a Spring Boot Starter is a breeze once you are familiar with the automatic configuration features of Spring Boot.

Now that the source code for Spirng and Spring Boot, two popular infrastructure frameworks, has been analyzed, the author will start to learn other things, such as MySQL, Dubbo and Spring Cloud, stay informed, come on 👨🎓

Apache Dubbo 3.0 is officially released, fully embracing cloud native. Learn more about Dubbo ~ first

The way ahead is so long without ending, yet high and low I’ll search with my will unbending.

Mysql, Netty, Spring, thread, Spring Cloud, JVM, source code, algorithm and so on. There are also detailed learning planning maps, interview questions sorting, etc., need to obtain these contents of the friend please add Q jj: 673927155