What is the easiest way to change the prefix and suffix for the property placeholder in Spring Boot?
The default is #Value("${some.property}"), however this looks ugly in Kotlin, since it needs to be escaped - ${something} is a language feature in Kotlin for String templates.
It is possible to customize the prefix used by declaring the following beans in your configuration:
#Bean
fun propertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
setPlaceholderPrefix("%{")
}
if you have any existing code (like Spring Boot actuators or #LocalServerPort) that is using the ${...} syntax, you should declare:
#Bean
fun kotlinPropertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
setPlaceholderPrefix("%{")
setIgnoreUnresolvablePlaceholders(true)
}
#Bean
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
Escaping the dollar like in #Value("\${some.property}") is another possible option that require no #Bean declaration.
For Spring Boot tests configured with #SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) you can use #LocalServerPort instead of #Value("\${local.server.port}").
#ConfigurationProperties would be a better alternative, especially with Kotlin data classes, but currently you have to use Kotlin classes with nullable var properties since only getter/setter are supported. You can vote for this issue or comment to show your interest in getting that supported in Spring Boot 2.x.
They have a new feature using java classes annotated with #ConfigurationProperties. This looks nice in Kotlin and is refactoring save. You should give it a try:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-properties
Using advice from answer provided by dox, I ended up going with something like:
public interface TokenAuthenticationConfig {
public fun apiKey() : String
}
#Component
#ConfigurationProperties(prefix = "service.api")
public open class TokenAuthenticationConfigImpl : TokenAuthenticationConfig
{
public var apiKey : String
constructor() {
this.apiKey = ""
}
override fun apiKey(): String
{
return this.apiKey
}
}
In Spring #ConfigurationProperties objects are required to follow the Java Beans pattern and be, therefore, mutable. To me configuration seems as though it should be generally static throughout app life-cycle, and so rather than add the complexity of reasoning about state, injected the immutable interface instead.
Related
My problem references to this issue here on github https://github.com/mapstruct/mapstruct/issues/1427
I got at least two versions of mappers with the same name. I want to use springs getBean/Autowired possibilities but this doesn't work out of the mapstructs box yet. :-)
I followed the second workaround mentioned in the upper link: extend Springs bean naming strategy. Did someone ever get this well ment proposal working?
If i follow the code parts from there the bean naming doesn't take place. For me its's clear why not: there aren't any components to scan and especially to find.
If i add a componenModel = "spring" to the mapper annotation i get a ConflictingBeanDefinitionException. Don't know why. Maybe there's a cat in the tail problem?
As stated from Filip here https://github.com/mapstruct/mapstruct/issues/1427 i followed his approach and with a few modifications it worked. I added a solution comment in the link.
The main changes are:
i added componentModel = "spring" to my mappers and used a filter to exclude all of my mapper classes (the interface all of my mappers are implementing: MapperInterface.class) within the Spring Boot application.
To my Spring Boot application class i added:
#ComponentScan(basePackages = { "com.application.spring_boot_class" }, excludeFilters = { #ComponentScan.Filter(value = { MapperInterface.class }, type = FilterType.ASSIGNABLE_TYPE) })
I had this issue before, and I resolved it using the Spring bean definition in a configuration class, a class annotated with #Configuration, with the Mapstruct mapper call like below:
#Bean
public IMyMapper offerWebMapper() {
return Mappers.getMapper(IMyMapper.class);
}
And then you can inject the mapper using #Autowired or getBean.
I have some common interface for refactoring logic in my project. It looks about like this:
public interface RefactorAwareEntryPoint {
default boolean doRefactor() {
if (EventLogService.wasEvent(getEventType())) {
return true;
}
boolean result = doRefactorInternal();
if (result) {
EventLogService.registerEvent(eventType);
}
return result;
}
String getEventType();
boolean doRefactorInternal();
}
And than, when I need to write some refactoring - I implement this interface with methods, mark class like #Component, and Spring in loop evaluate each interface implementation and register it in database.
But we have a lot of refactors (every year - 200-300 new). It's hard to disable old implementations manualy, and we have a lot of beans in our spring-context.
Can we do something, for example, use some annotation - which will disable component creation by some condition?
For example:
#Component
#Enabled(YEAR.2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
And this annotation will work like this (a pseudocode):
if (YEAR.2020) {
create bean -> new CustomRefactor()
}
And when it will be YEAR.2021 - we will have no beans from YEAR.2020 in spring-context.
Use the annotation #Profile that makes application configuration and beans available in certain environments.
You can find more at Spring Boot 2.4.0 reference documentation: 3. Profiles
Spring Profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments. Any #Component, #Configuration or #ConfigurationProperties can be marked with #Profile to limit when it is loaded
Consider each year as a separate environment.
#Component
#Profile("2020")
public class CustomRefactor2020 implements RefactorAwareEntryPoint {
// Code implementation
}
#Component
#Profile("2021")
public class CustomRefactor2021 implements RefactorAwareEntryPoint {
// Code implementation
}
In addition to the answers provided by our colleagues, consider the feature of spring called "Stereotype annotations". This is how well-known annotations like #Service are defined in spring.
In general, the fact that you mark your class with #Component annotation allows you to load the class as a spring bean because the annotated class becomes a subject to a process called "component scanning" - a process happens when you start the application context.
Since spring 4 there is a conditional interface that basically makes possible implementing a logic similar to what you refer to as #Enabled(YEAR.2020).
You might use a built-in "#ConditionalOnProperty" to map the 2020 year to property or even implement a custom conditional logic. I'll assume that you've implemented a custom conditional as #ConditionalOnYear
Now, what's interesting (and this is a "stereotype" feature that I've mentioned at the beginning of the post) is that you may create your own "component" annotation with a custom "conditional" logic and use it "as if" its a regular bean:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#ConditionalOnYear(2020)
#Component
public #interface Year2020OnlyComponent {
#AliasFor(annotation = Component.class)
String value() default "";
}
#Year2020OnlyComponent
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
You can also improve that by clever usage of #AliasFor annotation to be something like:
#SinceYearComponent(2020)
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
But this is kind of out of scope for this question - so I just mention a direction here.
Of course, it's possible to merely use two annotations as you've suggested even without this "Stereotype" annotation feature:
#Component
#SinceYear(2020) // a custom conditional
public class CustomRefactor implements RefactorAwareEntryPoint {
// Code implementation
}
Check out the BeanFactoryPostprocessor interface. Probably you can remove a bean before it‘s creation.
Else you might implement your own BeanFactory and create the ApplicationContext with your implementation.
You can use excludeFilter annotations provided by spring boot .
As mentioned by others you can always use #Profile annotation to
enable/disable profiles.
Another option is excludeFilter
I've created a lot of common small bean-definition containers (#Configuration) which I use to rapidly develop applications with Spring Boot like:
#Import({
FreemarkerViewResolver.class, // registers freemarker that auto appends <#escape etc.
ConfigurationFromPropertiesFile.class, // loads conf/configuration.properties
UtfContentTypeResponse.class, // sets proper Content-language and Content-type
LocaleResolverWithLanguageSwitchController // Locale resolver + switch controller
);
class MySpringBootApp ...
For example, one of such #Configurations can set up session storage for locale cookie with web controller to switch to selected language etc.
They are very fun to work with and reuse, but it would be really great to make it parametrized, which could allow lot more reusege. I mean something like:
Pseudo code:
#Imports( imports = {
#FreemarkerViewResolver( escapeHtml = true, autoIncludeSpringMacros = true),
#ConfigurationFromProperties( path = "conf/configuration.properties" ),
#ContentTypeResponse( encoding = "UTF-8" ),
#LocaleResolver( switchLocaleUrl = "/locale/{loc}", defaultLocale = "en"
})
So, I basically mean "configurable #Configurations". What would be the best way to make the configuration that way?
Maybe something more like this (again, pseudo code):
#Configuration
public class MyAppConfiguration {
#Configuration
public FreemarkerConfiguration freemarkerConfiguration() {
return FreemarkerConfigurationBuilder.withEscpeAutoAppend();
}
#Configuration
public ConfigurationFromPropertiesFile conf() {
return ConfigurationFromPropertiesFile.fromPath("...");
}
#Configuration
public LocaleResolverConfigurator loc() {
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale("en").withSwitchUrl("/switchlocale/{loc}");
}
Let me quote Spring Boot Reference Guide - Externalized Configuration:
"Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments."
In my opinion the customization is not done at import time via annotation parameters like in your 2nd pseudo code block, instead the customization happens at run time e.g. in the configuration classes. Let me adapt your 3rd code block (only one function):
#Configuration
public class MyAppConfiguration {
#Autowired
private Environment env;
// Provide a default implementation for FreeMarkerConfigurer only
// if the user of our config doesn't define her own configurer.
#Bean
#ConditionalOnMissingBean(FreeMarkerConfigurer.class)
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setTemplateLoaderPath("/WEB-INF/views/");
return result;
}
...
#Bean
public LocaleResolverConfigurator loc() {
String defaultLocale = env.getProperty("my.app.config.defaultlocale", "en");
String switchLocale = env.getProperty("my.app.config.switchlocale", "/switchlocale/{loc}");
return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale(defaultLocale).withSwitchUrl(switchLocale);
}
For LocaleResolverConfigurator the configuration is read from the environment, meaningful default values are defined. It is easy to change the default value(s) by providing a different value for a config parameter in any of the supported ways (documented in the first link) - via command line or a yaml file. The advantage over annotation parameters is that you can change the behavior at run time instead of compile time.
You could also inject the config parameters (if you prefer to have them as instance variable) or use a lot of other conditions, e.g. #ConditionalOnMissingBean, #ConditionalOnClass, #ConditionalOnExpression and so on. For example with #ConditionalOnClass you could check if a particular class is on your class path and provide a setting for the library identified by this class. With #ConditionalOnMissingClass you could provide an alternative implementation. In the example above I used ConditionalOnMissingBean to provide a default implementation for the FreeMarkerConfigurer. This implementation is only used when no FreeMarkerConfigurer bean is available thus can be overridden easily.
Take a look at the starters provided by Spring Boot or the community. A good read is also this blog entry. I learned a lot from spring-boot-starter-batch-web, they had an article series in a German Java magazine, but parts are also online, see Boot your own infrastructure – Extending Spring Boot in five steps (MUST READ) and especially the paragraph "Make your starter configurable by using properties".
Though I like the idea of having imports be parameterized, I think that as it stands now using #Import and #Configuration not a good fit.
I can think of two ways to use dynamic configurations, that don't rely on PropertySource style configuration.
Create a custom #ImportConfig annotation and annotation processor that accepts configuration properties that are hard-coded into the generated source files.
Use a BeanFactoryPostProcessor or BeanPostProcessor to add or manipulate your included beans respectively.
Neither is particularly simple IMO, but since it looks like you have a particular way of working. So it could be worth the time invested.
I like many features of Play Framework 2 (I'm using it with Java) but, as a fan of Dependency Injection, I love also Spring and particularly, its way to inject configuration into objects by just using the #Value annotation.
Therefore, I would love to know how to inject into an instance variable the value of a property using Play's built-in property resolution mechanism. Something like this:
#Component
public class SpringBeanWithinAPlay2Application {
#Value("${application.timeout:10}")
private int timeout;
}
Any clue anyone?
Many thanks in advance.
I had the same problem a while ago and this was my way of making this work:
Firstly, when you boostrap your Spring Application context (I use annotation based configuration but the same should work for XML based), you have to add a custom PropertySource, which is the way Spring enables the addition of new way of resolving properties. Something like this:
public static void initialize() {
ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().getPropertySources().addFirst(new PlayFrameworkPropertySource());
ctx.scan("somepackage");
ctx.refresh();
}
The custom class PlayFrameworkPropertySource is the one that does the magic:
public class PlayFrameworkPropertySource extends PropertySource<Object> {
public PlayFrameworkPropertySource() {
super("Play Framework properties resolution mechanism");
}
#Override
public Object getProperty(String propertyName) {
// or ConfigFactory.load().getString(propertyName), as you prefer...
return Configuration.root().getString(propertyName);
}
}
In order for all this to work, you just need to do one more thing: explicitly declare a bean of type PropertySourcesPlaceholderConfigurer in some #Configuration class you might be using:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Important Note: this bean must be static, as it's a BeanFactoryPostProcessor and it should be loaded prior to any other regular #Bean.
This worked like a charm for me, hope this is helpful to someone else!
Cheers,
Jonathan
My goal is a framework where concrete types of beans can be easily changed by a properties file. I also prefer annotations to XML. Ideally I'd to use a combination of #Resource and SpEL like this:
#Resource(type="#{myProperties['enabled.subtype']}")
SomeInterface foo;
where I've loaded myProperties with a PropertiesFactoryBean or <util:properties> from a file that includes:
enabled.type = com.mycompany.SomeClassA; // which implements SomeInterface
This doesn't work because the argument of type must be a literal, i.e., no SpEL allowed. What's the best practice here?
Update: See my answer below.
This is exactly the use case for Spring Java Configuration.
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java
Or you can alternatively make a Factory.
Using: org.springframework.beans.factory.FactoryBean<SomeInterface>
The name of the bean that implements FactoryBean will be seen as a "SomeInterface" even though its not.
I think it is not possible, the solution I tend to adopt is to use a factory that creates the different objects depending on a configuration property (enabled.type in your example).
A second alternative could be to use injection by name:
#Resource(name="beanName")
And last, if you use Spring 3.1+ you can try to use profiles, and have different bean sets in different profiles, if that solves your problem.
Spring's Java Configuration and Bean Definition Profiles turn out to be exactly what I was looking for (thanks #Adam-Gent and #Guido-Garcia). The former seems necessary for the dynamic element, and the latter promotes a better practice.
Here's a solution with Java config and properties:
#Configuration
public class SomeClassConfig {
#Value("#{myProperties['enabled.subtype']}")
public Class enabledClass;
#Bean SomeInterface someBean()
throws InstantiationException, IllegalAccessException {
return (SomeInterface) enabledClass.newInstance();
}
}
Here's a slightly less dynamic solution with profiles.
#Configuration
#Profile("dev")
public class DevelopmentConfig {
#Bean SomeInterface someBean() {
return new DevSubtype();
}
}
#Configuration
#Profile("prod")
public class ProductionConfig {
#Bean SomeInterface someBean() {
return new ProdSubtype();
}
}
With profiles, the active profile(s) are declared using one of a variety of methods such as via system property, JVM property, web.xml, etc. For example, with a JVM property:
-Dspring.profiles.active="dev"