Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
459 views
in Technique[技术] by (71.8m points)

java - 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");
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.

i.e.

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);
    }

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...