Configuration file@Configuration Properties reads List, Map parameters

Keywords: Programming Java Spring SpringBoot xml


In the SpringBoot environment, we have "incomplete" annotations.This is why SpringBoot replaces the xml configuration in traditional Spring projects.When using these annotations, we must understand the principles and conventions behind these annotations.


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;

@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface ConfigurationProperties {

Supported Types


Corresponding Java implementation

@ConfigurationProperties(prefix = "custom.config.config1")
public class Config1Properties{
	private List<String> folders;

Corresponding Java implementation

@ConfigurationProperties(prefix = "custom.config.config1")
public class Config1Properties{
	private Map<String, String> map;

Corresponding Java implementation

@ConfigurationProperties(prefix = "custom.config.config1")
public class Config1Properties{
	private ServerProperties server;
	public static class ServerProperties {
		private String host;
		private int port;
		private String username;
		private String password;
Object List

Corresponding Java implementation

@ConfigurationProperties(prefix = "custom.config.config1")
public class Config1Properties{
	private List<ServerProperties> servers;
	public static class ServerProperties {
		private String host;
		private int port;
		private String username;
		private String password;

Use cases for Map

For example, if we need to connect multiple OSS (Ali Object Store) at the same time, then we can configure multiple in the way of Configuration Properties.It can also be dynamically injected into containers by Spring's loading.

Configuration center:

# OSS1 Configuration

# OSS2 Configuration

Corresponding Java implementation

@EqualsAndHashCode(callSuper = false)
@ConfigurationProperties(prefix = OssConstants.MULTI_CONFIG_PREFIX)
public class MultiOssProperties {
	private Map<String, OssProperties> clients;

	public static class OssProperties {
		private String accessKeyId;
		private String accessKeySecret;
		private String publicEndpoint;
		private String privateEndpoint;
		private String bucketName;
		private String object;

Dynamically define the BeanDefinition we need.

public class MultiOssScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

	private ApplicationContext applicationContext;

	private MultiOssProperties multiOssProperties;

	public void setBeanName(String name) {"init bean {}", name);

	public void afterPropertiesSet() throws Exception {
		Objects.requireNonNull(this.multiOssProperties, "multiOssProperties Cannot be empty");
		Objects.requireNonNull(this.applicationContext, "applicationContext Cannot be empty");

  // Dynamic Definition Bean
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
		String beanSuffixName = StringUtils.capitalize(OssConstants.BEAN_SUFFIX_NAME);
    // The productCodes actually match the xdtrans of oss.multi.clients.xdtrans
		multiOssProperties.getClients().forEach((productCode, ossProperties) -> {
			AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(OssClient.class,
					() -> OssClientUtils.buildOssClient(ossProperties))
			beanDefinitionRegistry.registerBeanDefinition(productCode + beanSuffixName, beanDefinition);

	public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;


binder to relate the configuration to the corresponding Java code:

@ConditionalOnProperty(prefix = OssConstants.MULTI_CONFIG_PREFIX, value = "enabled")
public class MultiOssAutoConfiguration {

	 * Initialize multiple ossClient autoconfigurations
	 * @param environment Environment variable properties
	 * @return OssClient Automatic Scan Registrar
	public MultiOssScannerConfigurer multiOssScannerConfigurer(Environment environment) {
		Binder binder = Binder.get(environment);
		MultiOssProperties properties = binder.bind(OssConstants.MULTI_CONFIG_PREFIX, MultiOssProperties.class).get();
		MultiOssScannerConfigurer multiOssScannerConfigurer = new MultiOssScannerConfigurer();
		return multiOssScannerConfigurer;

How to use it?

public enum OssTypeEnum {
		// Notice the beanName here to be consistent with the postProcessBeanDefinitionRegistry above
    XDtransOssClient("xdtransOssClient", "oss1"),
    DianDianOssClient("ddacctOssClient", "oss2"),

    private final String beanName;
    private final String desc;

    // Get it in the Spring container from BeanName
    public OssClient getBean() {
        return SpringContextHolder.getBean(beanName, OssClient.class);

How does Binder map?

Bid with the code binder.bind above (OssConstants.MULTI_CONFIG_PREFIX, MultiOssProperties.class). get(); and

protected final <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context, boolean allowRecursiveBinding) {
		try {
			target = handler.onStart(name, target, context);
			if (target == null) {
				return null;
			Object bound = bindObject(name, target, handler, context,allowRecursiveBinding);
			return handleBindResult(name, target, handler, context, bound);
		} catch (Exception ex) {
			return handleBindError(name, target, handler, context, ex);

If our key is:

It actually corresponds to Map, so its reference name is clients.The key is accout, and the corresponding value is OssProperties.

private Object bindBean(ConfigurationPropertyName name, Bindable<?> target,
		BindHandler handler, Context context, boolean allowRecursiveBinding) {
	if (containsNoDescendantOf(context.getSources(), name)
			|| isUnbindableBean(name, target, context)) {
		return null;
	BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(
			name.append(propertyName), propertyTarget, handler, context, false);
	Class<?> type = target.getType().resolve(Object.class);
	if (!allowRecursiveBinding && context.hasBoundBean(type)) {
		return null;
	return context.withBean(type, () -> {
		Stream<?> boundBeans =
				.map((b) -> b.bind(name, target, context, propertyBinder));
		return boundBeans.filter(Objects::nonNull).findFirst().orElse(null);

A specific bind case.

private static final List<BeanBinder> BEAN_BINDERS;

static {
	List<BeanBinder> binders = new ArrayList<>();
	binders.add(new JavaBeanBinder());
	BEAN_BINDERS = Collections.unmodifiableList(binders);

public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Context context,
		BeanPropertyBinder propertyBinder) {
	boolean hasKnownBindableProperties = hasKnownBindableProperties(name, context);
	Bean<T> bean = Bean.get(target, hasKnownBindableProperties);
	if (bean == null) {
		return null;
	BeanSupplier<T> beanSupplier = bean.getSupplier(target);
	boolean bound = bind(propertyBinder, bean, beanSupplier);
	return (bound ? beanSupplier.get() : null);
// Return the corresponding object
public BeanSupplier<T> getSupplier(Bindable<T> target) {
	return new BeanSupplier<>(() -> {
		T instance = null;
		if (target.getValue() != null) {
			instance = target.getValue().get();
		if (instance == null) {
			instance = (T) BeanUtils.instantiateClass(this.resolvedType);
		return instance;

Reference Address

If you like my article, you can focus on your personal subscription number.Welcome to leave a message and exchange at any time.If you want to join the WeChat group and discuss it together, add lastpass4u, the administrator's culture assistant, who will pull you into the group.

Posted by gtal3x on Wed, 06 May 2020 09:32:15 -0700