-1

Using Sprint Boot 2.0.4.RELEASE, I have the following properties class (simplified):

@Configuration
@EnableConfigurationProperties(MyOtherHierarchicalNonConstantProperties.class)
public class AppConfiguration {
    public static final String MY_CONSTANT = "${path.to.my-constant}";
}

and the application.yml (also simplified):

path.to.my-constant: mystring

The Problem

The value from application.yml never gets assigned to MY_CONSTANT, which is required for use as an argument to annotations. (I want all configurable options between build environments to be set in application.yml!) The value of MY_CONSTANT ends up being ${path.to.my-constant}.

I have been working with this issue for pretty much the entire day, also tried several different options after lots of searching, such as:

  • ${${path.to.my-constant}} #1
  • #{${path.to.my-constant}} #2
  • ${#{path.to.my-constant}}
  • #{'${path.to.my-constant}'}
  • and many many more...

Additional (Maybe Helpful) Information

In some cases I get the exact expression as the value of the string (as said above), but in some cases I get exceptions such as:

  • #1: IllegalArgumentException from PropertyPlaceholderHelper.parseStringValue saying Could not resolve placeholder 'mystring' in value "${${path.to.my-constant}}"
  • #2: SpelEvaluationException from PropertyOrFieldReference.readProperty saying EL1008E: Property or field 'mystring' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?

Both indicating that if the initial expression is further wrapped inside other SpEL braces, the inner expression gets translated to the desired value. But after that, it refuses to be extracted from the outer braces. (For quite a long time, I was trying hundreds of SpEL operations to try and somehow "extract" the value from the outer braces...)

One thing I had also tried, is moving the constant into the other properties class with the @ConfigurationProperties annotation hoping it would change something, but the results seem to be identical.

The Plea

I know the question about assigning configuration properties to constants in Spring Boot has been asked a lot, I have also tried every option on planet Earth after coming here to ask this and am hopelessly lost.

Someone please, for once and for all, put and end to this miserable lack of single source of truth about configurable constant expressions and tell us how to do this the right way! (And if Spring Boot never intended for it to be possible, that information could also be helpful to know...)

PS! No nasty hacks please...

Snackoverflow
  • 5,332
  • 7
  • 39
  • 69
  • 1
    have you tried `@Value(${"path.to.my-constant"})` in AppConfiguration ? – Jayesh Aug 27 '18 at 00:08
  • @Jayesh Yes, and tried it again, just to be sure. 1. If it is assigned, then `Annotations are not allowed here`. 2. If it is `@Autowired` from constructor, it becomes a class member and cannot be used as an argument to annotations. 3. In other cases it is not a constant (`final`). *Btw, I guess you meant for the dollar and braces to be inside of the string.* – Snackoverflow Aug 27 '18 at 00:15
  • I was recently using Spring Boot 2.0.2.RELEASE and I remember it worked fine. I feel very reluctant towards rolling back to an older version, because if this functionality was deprecated, it was for a reason. But then I was also using only the `@Configuration` class, and not `@ConfigurationProperties`... Anyway, tomorrow I am going to try everything else from my commit history.. – Snackoverflow Aug 27 '18 at 00:19
  • ok try adding @ConfigurationProperties back and try to get the value with `private String pathToMyConstant;` – Jayesh Aug 27 '18 at 00:25
  • 2
    Constants are _constants_. You can't overwrite them at compile-time or runtime, and you can't use `ConfigurationProperties` values as annotations. – chrylis -cautiouslyoptimistic- Aug 27 '18 at 00:59
  • @chrylis I thought similarly to annotations, the String gets parsed during compilation and the code gets slightly modified. Also there is a thing called reflection... – Snackoverflow Aug 27 '18 at 07:37
  • 1
    All of Spring's setup (including `@Value` and `ConfigurationProperties`) happens at runtime. It's exactly this that provides the ability to switch out profiles per-launch instead of requiring a rebuild to change settings. – chrylis -cautiouslyoptimistic- Aug 27 '18 at 09:00

3 Answers3

0

Annotations that reference a field property that is be both static & final. If you're using the Spring boot annotations (e.g. @Value, @Scheduled, etc); then just use the spel. Or if you needs to use @Autowired, make variables into beans (or a bean, your preference) and use autowire them to a factory service to create your objects.

But it looks like you're using @CrossOrigin which does not seem to handle spel & a factory doesn't quite fit this use case. So you're going to need to add to your configuration, like so

@Configuration
public class CrossConfig{

    private String[] crossEndPoints = ...;

    @Bean
    public WebMvcConfigurer corsConfigurer(@Value("${path.to.my-constant}") String crossOrigin) {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                for(String endpoint : crossEndPoints){
                    registry.addMapping(endpoint).allowedOrigins(crossOrigin);
                }
            }
        };
    }
}

As seen in SpringDocs for CrossOrigins. You're just going to need to manually list your endpoints, or programmatically find them some way. If you already have a WebMvcConfigurer bean, just added the addCorsMappings(CorsRegistry) to it.

Code Eyez
  • 313
  • 3
  • 14
-1

I think, for you this is the best possible option:

@Configuration
@EnableConfigurationProperties(prefix = "path.to")
public class AppConfiguration {
    public final String MY_CONSTANT;

    public UserService(@Value("${path.to.MY_CONSTANT}") String MY_CONSTANT) {
        this.MY_CONSTANT= MY_CONSTANT;
    }
}

Can you try this and let me know..

Anand Varkey Philips
  • 1,811
  • 26
  • 41
  • This way, `MY_CONSTANT` is a property of the object of type `AppConfiguration` and not static. Therefore it is not possible for it to be used as an annotation argument. – Snackoverflow Aug 27 '18 at 08:05
  • Can you please write code, how you expect it to use as an annotation argument? I'll try. – Anand Varkey Philips Aug 27 '18 at 08:40
  • `@RestController public class MyController { @RequestMapping("/resource") @CrossOrigin(origins = {MY_CONSTANT}) public String getResource() {return "Hello World!";}}` Save this as a separate file `MyController.java` and use `import static path.to.package.AppConfiguration.MY_CONSTANT;` on the top of it. Now if you run your application, you may connect to `http://localhost:8000/resource` to see `Hello World!`. *Yes, `MY_CONSTANT` is actually intended to store the IP of the front-end application for enabling CORS requests. But this is not what my question is about...* – Snackoverflow Aug 27 '18 at 16:22
-2

@anddero...it will help you...

@Configuration
@EnableConfigurationProperties(MyOtherHierarchicalNonConstantProperties.class)
public class AppConfiguration {
    public static final String MY_CONSTANT = "${path.to.my-constant}";

@Autowired
    public AppConfiguration (@Value("${path.to.my-constant}") String MY_CONSTANT ) {
        this.MY_CONSTANT = MY_CONSTANT ;
    }
}
kumar
  • 497
  • 4
  • 12