Spring Boot Nacos configuration cannot override the default value defined by @Value

origin

A new function has been added. @Value("${xxx.aaa:b}")This form is used to obtain a variable. The default value b is assigned a default value when the corresponding value cannot be found in the configuration. The project also integrates Nacos, which is configured in Nacos. After startup, it is found to xxx.aaa=cobtain The value of is the default, not Nacos. When the default value is removed, the @Value("${xxx.aaa}")value of Nacos can be obtained correctly.

version agreement

Spring Cloud version Dalston.SR4
Swagger2 version 2.6.0

analyze

1. Is it a problem with Nacos?

Obviously not, because after removing the default value, the value configured in Nacos can be obtained correctly, indicating that the value of Nacos can be obtained, but it is not the first priority

2. Check whether it is a value acquisition sequence problem

There is a rule for value acquisition in Spring Boot

#默认情况下,远程配置会优先于本地配置
spring.cloud.config.allowOverride
spring.cloud.config.overrideNone
spring.cloud.config.overrideSystemProperties

These values ​​are default, and it is not configured in the configuration, so the remote takes precedence over the default value

process

@ValueThis annotation can be added to the class attribute or to the method, because the change of the class attribute is not easy to monitor, so write a variable to be assigned and setteradd the annotation to setterit

package org.springframework.beans.factory.annotation;
//省略import 
//可以看到Target可以是ElementType.METHOD
@Target({
    
    ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
    
    
    String value();
}

So, will

@Value("${mock.open:N}")
private String mockOpen;

changed to

@Value("${mock.open:N}")
public void setMockOpen(String mockOpen) {
    
    
	this.mockOpen = mockOpen;
}

Then add a breakpoint in this.mockOpen = mockOpen;this sentence, the configuration file configuration corresponding to Nacos mock.open=Y. Start the service and wait for the endpoint to stay at . this.mockOpen = mockOpen;
You can see:
insert image description here
We followed setMockOpenthe method stack online to find this place in the figure and found that the parameter passed in is , which means that this value Nhas been obtained by the time this step is reached . The signature of this method is:N

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject

Looking up the parameter in the figure above arguments, you can see that this parameter is calculated internally, then the calculation logic is in the current method, and the method logic is as follows:

protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    
    
  if (!this.checkPropertySkipping(pvs)) {
    
    
       Method method = (Method)this.member;
       Object[] arguments;
       if (this.cached) {
    
    
       //可能是这里获取的
           arguments = this.resolveCachedArguments(beanName);
       } else {
    
    
           Class<?>[] paramTypes = method.getParameterTypes();
           //可能是这里获取的
           arguments = new Object[paramTypes.length];
           DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
           Set<String> autowiredBeans = new LinkedHashSet(paramTypes.length);
           TypeConverter typeConverter = AutowiredAnnotationBeanPostProcessor.this.beanFactory.getTypeConverter();

           for(int ixx = 0; ixx < arguments.length; ++ixx) {
    
    
               MethodParameter methodParam = new MethodParameter(method, ixx);
               DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
               currDesc.setContainingClass(bean.getClass());
               descriptors[ixx] = currDesc;

               try {
    
    
                   Object arg = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
                   if (arg == null && !this.required) {
    
    
                       arguments = null;
                       break;
                   }

                   arguments[ixx] = arg;
               } catch (BeansException var17) {
    
    
                   throw new UnsatisfiedDependencyException((String)null, beanName, new InjectionPoint(methodParam), var17);
               }
           }

           synchronized(this) {
    
    
               if (!this.cached) {
    
    
                   if (arguments != null) {
    
    
                       this.cachedMethodArguments = new Object[paramTypes.length];

                       for(int i = 0; i < arguments.length; ++i) {
    
    
                           this.cachedMethodArguments[i] = descriptors[i];
                       }

                       AutowiredAnnotationBeanPostProcessor.this.registerDependentBeans(beanName, autowiredBeans);
                       if (autowiredBeans.size() == paramTypes.length) {
    
    
                           Iterator<String> it = autowiredBeans.iterator();

                           for(int ix = 0; ix < paramTypes.length; ++ix) {
    
    
                               String autowiredBeanName = (String)it.next();
                               if (AutowiredAnnotationBeanPostProcessor.this.beanFactory.containsBean(autowiredBeanName) && AutowiredAnnotationBeanPostProcessor.this.beanFactory.isTypeMatch(autowiredBeanName, paramTypes[ix])) {
    
    
                                   this.cachedMethodArguments[ix] = new ShortcutDependencyDescriptor(descriptors[ix], autowiredBeanName, paramTypes[ix]);
                               }
                           }
                       }
                   } else {
    
    
                       this.cachedMethodArguments = null;
                   }

                   this.cached = true;
               }
           }
       }

       if (arguments != null) {
    
    
           try {
    
    
               ReflectionUtils.makeAccessible(method);
               method.invoke(bean, arguments);
           } catch (InvocationTargetException var15) {
    
    
               throw var15.getTargetException();
           }
       }

   }
}

There are two places where the above code may be obtained, or ifone elseof the two branches will be obtained. We Method method = (Method)this.member;add a breakpoint to this line of code after the method enters, set conditions for the breakpoint beanName.equalsIgnoreCase("ESAttribute"), ESAttributewhich is setMockOpen(String mockOpen)the class where the method is located, and then restart the service .
After the breakpoint takes effect, the single-step debugging found that it has entered elsethe logic:
insert image description here
step to the bottom and
insert image description here
find that the parameter argvalue is N, and there is an assignment statement below arguments[ixx] = arg;, which is most likely to be this,
discard it to the current call stack, re-enter, and go to the single line in the figure Enter the method step by step,
then
insert image description here
go here (method signature org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency) to continue to enter the method
and then
insert image description herethe method signature org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency, the method in the above figure enters the method in a single step , and you can see the calculated value in the first cycle of the cycle
insert image description here
here . Check the current Property parser , I found that the filler is actually , meow, why is it not Spring's own Property filler, why is it a filler, so I checked this The second is Spring's own Property filler, then go to Take a look at the order in which the container is started, and check which configuration the swagger filler is assembled in by the way. Find the next breakpoint method signature heredo whileresultN
insert image description here
swaggerPropertiesswaggerthis.embeddedValueResolvers
insert image description here
this.embeddedValueResolvers

insert image description here

org.springframework.beans.factory.support.AbstractBeanFactory#addEmbeddedValueResolver

After restarting the application, the first one that comes in is insert image description here
indeed Spring’s own, and then let the program continue. The second one that comes in is
insert image description here
Yes swaggerProperties, the assembled class
insert image description here
is The method to find the assembled beanFactoryclass can be seen in beanDefinitionMapthe execution expression below Information, found from , this class. But the strange thing is that at this time it is actually 0 , and then the program continues to run, and this time it is Spring’s Property filler (I compared the Spring’s filler that came in for the first time with the Spring’s filler that came in this time, for example Not the same one, which means that the one that came in for the first time was deleted. As for the reason, I didn’t go into the details. I personally guess that the first one may be put in by Spring Boot. Because Spring Cloud supports dynamic configuration like Nacos, the extension enhances Spring Boot’s parser, delete Spring Boot’s when assembling, and put in the enhanced one), I will not post the screenshot for the third time I come in. At this time, there are two fillers in , namely swagger and spring, swagger comes first, which leads to the problem at the beginning of the article. So let's look at this class((DefaultListableBeanFactory)((PropertyPlaceholderConfigurer)((PlaceholderResolvingStringValueResolver)valueResolver).this$0).beanFactory).beanDefinitionMap.get("swaggerProperties")swaggerPropertiesbeanDefinitionspringfox.documentation.swagger.configuration.SwaggerCommonConfiguration
this.embeddedValueResolvers
insert image description here

this.embeddedValueResolvers
springfox.documentation.swagger.configuration.SwaggerCommonConfiguration

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package springfox.documentation.swagger.configuration;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
//省略部分import
@Configuration
@ComponentScan(
    basePackages = {
    
    "springfox.documentation.swagger.schema", "springfox.documentation.swagger.readers", "springfox.documentation.swagger.web"}
)
public class SwaggerCommonConfiguration {
    
    
    public SwaggerCommonConfiguration() {
    
    
    }

    @Bean
    public static PropertyPlaceholderConfigurer swaggerProperties() {
    
    
        PropertyPlaceholderConfigurer propertiesPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
        propertiesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
        return propertiesPlaceholderConfigurer;
    }
}

Good guy, one is defined directly PropertyPlaceholderConfigurer , and this PropertyPlaceholderConfigurer is still Spring, I don’t know why.

in conclusion

From the analysis so far, we can see that the problem has been found, then the solution is ready to come out

  1. If you don't need to use swagger, just remove the swagger configuration
  2. If you still need to use swagger, upgrade the swagger version to 2.7.0, 2.7.0 deletes this configuration

Guess you like

Origin blog.csdn.net/u013014691/article/details/125113121