Springboot1.x and 2.x through @ConfigurationProperties refresh implementation and use different custom attributes of the bean

background

  I believe we have encountered some scenes, the need for some bean custom property values ​​refreshed in the project, where we used propertySource data source may not come from the outside, but the middle of the process of running certain programs produced the result set. Like scenarios, such data may be started after pretreatment certain items, pre-field data signing requests, and the like, common to these scenarios are relatively fixed property value, in order to reduce unnecessary hard coded, the thought of using! ConfigurationProperties achieve bean refresh custom attributes.

  Another springboot loaded from an external configuration information can be external properties file, yaml files, environment variables, and platform configuration centers, there are many ways to load this class, but many go here, have questions, you can refer to this article: https: //www.cnblogs.com/onlymate/p/10110642.html

Stepped pit

  Back to the topic, achieved within the project bean custom properties refresh, due to differences in springboot 1.x and 2.x versions is quite large, so stepped on a pit framework version upgrade brings. Originally 1.x running very 2.x code in a variety of slip burst of red, the reason is that a lot package is no longer used in springboot 2.x years, among them org.springframework.boot.bind package, and I just use PropertiesConfigurationFactory in this package, it is not strange burst of red.

Little understanding

  For understanding the springboot 1.x and 2.x configure the binding part of the source principle, a simple mention. springboot since the release notes provides @ConfigurationProperties operating configuration classes loose binding (Relaxed Binding), interesting is the realization of two large versions Relaxed Binding is not the same, after reading parts of the document because they want to feel springboot 2.0 It provides the user with more stringent API, so redesigning the way the binding occurs. 2.0 We add several new abstract, and the development of a new binding API, and some old code no longer use the old package. The following main points

1、PropertySources和ConfigurationPropertySources

  For PropertySource You certainly no stranger to combine the interface Environment, this interface is PropertyResolverthat it can keep you from some of the underlying PropertySourceanalytical property implementation. Spring Framework provides a common configuration to PropertySourceachieve, such as system properties, command-line flags and properties files. Spring Boot for most applications will be a meaningful way to automatically configure these implementations (eg, load application.properties).

  In Spring Boot 2.0 no longer directly use the existing PropertySourceinterface bindings, but introduces a new ConfigurationPropertySourceinterface. While providing a reasonable way to implement relax binding rules that used to be part of the binder. The main API interface is very simple: ConfigurationProperty getConfigurationProperty (ConfigurationPropertyName name); there is a IterableConfigurationPropertySourcedisguised form of realization of Iterablethe interface, so that you can discover the configuration of all the names contained in the source.

By using the following code Iterable <ConfigurationPropertySource> sources = ConfigurationPropertySources.get ( environment); external source data may be acquired; or if necessary, to provide a simple MapConfigurationPropertySourcerealization, the reconstructed source used in this manner project, very easy to use.

2, Relaxed Binding of the specific implementation

   springboot 1.5 and 2.0, and the binding logic configuration attribute value function starts with postProcessBeforeInitialization ConfigurationPropertiesBindingPostProcessor class.

  Version 1.5 source code which look found that when postProcessBeforeInitialization function is executed, the attribute value binding work is delegated to the PropertiesConfigurationFactory <T> class (this man is fundamentally our goods can not be found in 2.0, so it does not start talking details below a);

  While version 2.0 postProcessBeforeInitialization function calls, bind property values ​​were assigned to work the ConfigurationPropertiesBinder class, call the bind function, but ConfigurationPropertiesBinder class is not a public class, in fact it is only the equivalent of a static internal ConfigurationPropertiesBindingPostProcessor class, surface the handles @ConfigurationProperties binding annotation task. As can be seen from the source code, the specific work delegated to another object class Binder. Binder class is added after SpringBoot version 2.0 class, which is responsible for the implementation of the binding process between the object and the multiple ConfigurationPropertySource, code samples back, we will see.

Thus the basic springboot 1.x and 2.x versions binding properties configuration on the difference between a brief description of 7788, we started back from the beginning to use to fill the pit:

Scene: signing request, the server needs to resolve the process header information in the signature field. Such key field must be pre-defined service end, the resolution process requires repeated use.

Signature header information categories:

@Data
@ToString
@ConfigurationProperties(prefix="openapi.validate")
public class SignatureHeaders {
    private static final String SIGNATURE_HEADERS_PREFIX = "openapi-validate-";
    
    public static final Set<String> SIGNATURE_PARAMETER_SET = new HashSet<String>();
    private static String HEADER_APPID = SIGNATURE_HEADERS_PREFIX + "appid";
    private static String HEADER_TIMESTAMP = SIGNATURE_HEADERS_PREFIX + "timestamp";
    private static String HEADER_NONCE = SIGNATURE_HEADERS_PREFIX + "nonce";
    private static String HEADER_SIGNATURE = SIGNATURE_HEADERS_PREFIX + "signature";
    
    
    static {
        SIGNATURE_PARAMETER_SET.add(HEADER_APPID);
        SIGNATURE_PARAMETER_SET.add(HEADER_TIMESTAMP);
        SIGNATURE_PARAMETER_SET.add(HEADER_NONCE);
        SIGNATURE_PARAMETER_SET.add(HEADER_SIGNATURE);
    }
    
    /** 分配appid */
    private String appid;
    /** 分配appsecret */
    private String appsecret;
    /** 时间戳:ms */
    privateTimestamp String;
     / ** serial / random string: at least 16 bits, the validity anti resubmit * / 
    Private String the nonce;
     / ** signature * / 
    Private String Signature; 
    
    
}

 

A, 1.x use

Parsing header information

// 筛选头信息
Map<String, Object> headerMap = Collections.list(request.getHeaderNames())
                .stream()
                .filter(headerName -> SignatureHeaders.HEADER_NAME_SET.contains(headerName))
                .collect(Collectors.toMap(headerName -> headerName.replaceAll("-", "."), headerName -> request.getHeader(headerName)));
PropertySource propertySource = new MapPropertySource("signatureHeaders", headerMap);
SignatureHeaders signatureHeaders = RelaxedConfigurationBinder.with(SignatureHeaders.class).setPropertySources(propertySource).doBind();

Binding helper class

public class RelaxedConfigurationBinder<T> {
    private final PropertiesConfigurationFactory<T> factory;

    public RelaxedConfigurationBinder(T object) {
        this(new PropertiesConfigurationFactory<>(object));
    }

    public RelaxedConfigurationBinder(Class<T> type) {
        this(new PropertiesConfigurationFactory<>(type));
    }

    public static <T> RelaxedConfigurationBinder<T> with(T object) {
        return new RelaxedConfigurationBinder<>(object);
    }

    public static <T> RelaxedConfigurationBinder<T> with(Class<T> type) {
        return new RelaxedConfigurationBinder<>(type);
    }

    public RelaxedConfigurationBinder(PropertiesConfigurationFactory<T> factory) {
        this.factory = factory;
        ConfigurationProperties properties = getMergedAnnotation(factory.getObjectType(), ConfigurationProperties.class);
        javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        factory.setValidator(new SpringValidatorAdapter(validator));
        factory.setConversionService(new DefaultConversionService());
        if (null != properties) {
            factory.setIgnoreNestedProperties(properties.ignoreNestedProperties());
            factory.setIgnoreInvalidFields(properties.ignoreInvalidFields());
            factory.setIgnoreUnknownFields(properties.ignoreUnknownFields());
            factory.setTargetName(properties.prefix());
            factory.setExceptionIfInvalid(properties.exceptionIfInvalid());
        }
    }

    public RelaxedConfigurationBinder<T> setTargetName(String targetName) {
        factory.setTargetName(targetName);
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(PropertySource<?>... propertySources) {
        MutablePropertySources sources = new MutablePropertySources();
        for (PropertySource<?> propertySource : propertySources) {
            sources.addLast(propertySource);
        }
        factory.setPropertySources(sources);
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(Environment environment) {
        factory.setPropertySources(((ConfigurableEnvironment) environment).getPropertySources());
        return this;
    }

    public RelaxedConfigurationBinder<T> setPropertySources(PropertySources propertySources) {
        factory.setPropertySources(propertySources);
        return this;
    }

    public RelaxedConfigurationBinder<T> setConversionService(ConversionService conversionService) {
        factory.setConversionService(conversionService);
        return this;
    }

    public RelaxedConfigurationBinder<T> setValidator(Validator validator) {
        factory.setValidator(validator);
        return this;
    }

    public RelaxedConfigurationBinder<T> setResolvePlaceholders(boolean resolvePlaceholders) {
        factory.setResolvePlaceholders(resolvePlaceholders);
        return this;
    }

    public T doBind() throws GeneralException {
        try {
            return factory.getObject();
        } catch (Exception ex) {
            throw new GeneralException("配置绑定失败!", ex);
        }
    }
}

In front of the pits mentioned in the need to use a helper class to specify configurationPropertySource PropertiesConfigurationFactory other settings, and the like to complete the binding operation, while in PropertiesConfigurationFactory 2.x does not exist.

Two, 2.x use

Parsing header information

 // Filter header 
  the Map <String, Object> = headerMap Collections.list (request.getHeaderNames ()) 
                .stream () 
                .filter (headerName -> SignatureHeaders.SIGNATURE_PARAMETER_SET.contains (headerName)) 
                .collect (Collectors.toMap (headerName -> headerName.replaceAll (-. "" "",), headerName -> request.getHeader (headerName)));
   // custom ConfigurationProperty source information 
  ConfigurationPropertySource sources = new new MapConfigurationPropertySource (headerMap);
   // create Binder binding class 
  = Binder Binder new new Binder (Sources);
   // binding properties
  SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get();

2.x uses aside to build the factory configuration attributes, through our own MapConfigurationPropertySource implemented a custom attribute configuration source, then directly through the newly added binding classes Binder load the source information, identify bind directly to do after the bean properties from code implementation point of view save a lot of initialization code.

2.x load external attribute configuration to achieve:

// read from the configuration file / // Center Environment Configuration automatic injection or direct access context 
the Iterable <ConfigurationPropertySource> Sources = ConfigurationPropertySources.get (Environment); // set Binder 
Binder Binder = new new Binder (Sources);
 // property tied given 
SignatureHeaders signatureHeaders binder.bind = (. "openapi.validate", Bindable.of (SignatureHeaders class .)) GET ();

 

Example demo: custom Map data loading configuration properties to the header information of the class

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SignatureApp.class)
@Slf4j
public class ConfigurationPropertyTest {
    
    @Test
    public void testConfigurationPropertySources() {
        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("openapi.validate.appid", "123456789");
        dataMap.put("openapi.validate.timestamp", "1565062140111");
        dataMap.put("openapi.validate.nonce", "20190805180100102030");
        dataMap.put ("openapi.validate.signature", "vDMbihw6uaxlhoBCBJAY9xnejJXNCAA0QCc+I5X9EYYwAdccjNSB4L4mPZXymbH+fwm3ulkuY7UBNZclV1OBoELCSUMn7VRLAVqBS4bKrTA=");
        
        ConfigurationPropertySource sources = new MapConfigurationPropertySource(dataMap);
        Binder binder = new Binder(sources);
        SignatureHeaders signatureHeaders = binder.bind("openapi.validate", Bindable.of(SignatureHeaders.class)).get(); 
        
        log.info("###Parse Result: {}", signatureHeaders);
    }
}

 

Summary, the above is my main problem encountered in the course of the analysis, but it is still affecting the framework of version differences caused, it also tells us that once again can not deal with new problems and old thinking and old code is not able to use a copy of. Also between space, a lot more basic knowledge, to use, not too spread speaking, otherwise it is not the space, there will be a little bit of time to perfect the back.

ps: Gossip more, because he is looking at other people step by step Bowen times over, without any kind of description, up is a pass code technology blog yet not end, it is really a headache.

 

Appendix: 1.x and 2.x source code analysis principles in different implementations Spring Boot Relaxed Binding Mechanism - Jane book https://www.jianshu.com/p/a1fbfc4f9e12

Guess you like

Origin www.cnblogs.com/xufzhou/p/11384681.html