简体   繁体   中英

Spring profile is ignored when reading properties from application.yml

I have this code that scans Spring context:

public void scan() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

    context.register(SomeConfig.class);
    context.refresh();
}

I need properties to be read from application.yml file, so in SomeConfig class, I have this:

@Configuration
@PropertySource(value = "classpath:application.yml", factory = YamlPropertyLoaderFactory.class)
public class SomeConfig {
  //some beans
}

(I have copied YamlPropertyLoaderFactory class from here )

application.yml is a typical Spring Boot file with some properties by profile, and a default profile:

spring:
  profiles:
    active: p1

---

spring:
   profiles: p1

file: file1.txt

---

spring:
   profiles: p2

file: file2.txt

In some bean, I'm reading file property using @Value .

When I run my application, I'm passing -Dspring.profiles.active=p1 variable, but I'm getting an error:

Could not resolve placeholder 'file' in value "${file}"

(It should work even if I don't pass any profile since application.yml has default profile set to p1)

If I remove all profiles config from application.yml , it works fine:

file: file1.txt

So, it means that context scan is not reading the profile variable.

Also, if I set active profile "programatically", it doesn't resolve the properties either:

context.getEnvironment().setActiveProfiles("p1");

The YamlPropertyLoaderFactory you refer to has the following code:

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null);
    }
}

The third parameter to the YamlPropertySourceLoader.load() method is actually the profile name that you want the properties for. As this example passes in null it just returns the set of properties from the yml file not for a specific profile.

ie

spring:
  profiles:
    active: p1

---

I don't think it's easy to pick up the active profile name in the YamlPropertyLoaderFactory , although you could try something like...

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        String activeProfile = System.getProperty("spring.profiles.active");
        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), activeProfile);
    }
}

Or as you have the active profile name in the yml file, you could call YamlPropertySourceLoader().load with null to get the spring.profiles.active property then call it again to load the actual part of the yml file you want.

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }
        PropertySource<?> source = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null);
        String activeProfile = source.getProperty("spring.profiles.active");
        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), activeProfile);
    }
}

YamlPropertySourceLoader was changed back in feb 2018 ( YamlPropertySourceLoader blame view in Git repo ). It now returns a list of propertySource and does not have the third parameter on the load method.

Provided you have the spring.profiles.active property in the yml file you'd be able to do the following with the newer version of YamlPropertySourceLoader

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }
        List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
        for (PropertySource<?> checkSource : sources) {
            if (checkSource.containsProperty("spring.profiles.active")) {
                String activeProfile = (String) checkSource.getProperty("spring.profiles.active");
                for (PropertySource<?> source : sources) {
                    if (activeProfile.trim().equals(source.getProperty("spring.profiles"))) {
                        return source; 
                    }
                }
            }
        }
        return sources.get(0);
    }

}

@pcoates I tried with newer version of YamlPropertySourceLoader still failing to load profile.

Always failing in condition below with source returned with 2 elements without source "spring.profiles.active",

if (checkSource.containsProperty("spring.profiles.active"))

Below is my yaml and configuration for yaml property loader

  profiles: dev
  excelPath :  /data/excel
  excecutionPath: http://localhost:8080/server/execute
  memberIP: localhost
  profilerPortNum: 3031

--- 

spring:
  profiles: uat
  excelPath :  /data/uat/excel
  excecutionPath: http://localhost:8080/server/execute
  memberIP: localhost
  profilerPortNum: 3032```

@Configuration
@PropertySource(value = "classpath:application.yml", factory = YamlPropertyLoaderFactory.class)
public class ReadYamlProperties {
}


To set a property only for a specific profile, the correct indentation is:

spring:
   profiles: p1
   file: file1.txt

In the above case, you can access file1.txt with ${spring.file} EL.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM