Spring property value - res: URI - spring

I've been using spring for years but can't seem to find documentation on this one. If I have a spring bean configured like this:
<bean id="myBeanInstance" class="org.mybean">
<property name="path" value="res:a-string-goes-here"/>
</bean>
Is the value being interpreted as purely a string? How does 'res:' affect anything and where is it being interpreted? Any pointers to the appropriate docs would be fantastic.
Thanks!

Yes, the value is interpreted purely as string...
Unless you have a BeanFactoryPostProcessor, such as weirdly configured PropertyPlaceholderConfigurer or some custom BeanFactoryPostProcessor to take care of such values.
Note also, that since Spring 3 the ':' is the default separator for default property values, i.e. if you have PropertyPlaceholderConfigurer with default setting, the value "${res:whatever}" would be treated as a property 'res' with default value 'whatever'.

Related

Optional environment variables in Spring app

In my Spring Boot app's application.properties I have this definition:
someProp=${SOME_ENV_VARIABLE}
But this is an optional value only set in certain environments, I use it like this
#Value("${someProp:#{null}}")
private String someProp;
Surprisingly I get this error when the env. var doesn't exist
Could not resolve placeholder 'SOME_ENV_VARIABLE' in string value "${SOME_ENV_VARIABLE}"
I was expecting Spring to just set a blank value if not found in any PropertySource.
How to make it optional?
Provide a default value in the application.properties
someProp=${SOME_ENV_VARIABLE:#{null}}
When used like #Value("${someProp}), this will correctly evaluate to null. First, if SOME_ENV_VARIABLE is not found when application.properties is being processed, its value becomes the string literal "#{null}". Then, #Value evaluates someProp as a SpEL expression, which results in null. The actual value can be verified by looking at the property in the Environment bean.
This solution utilizes the default value syntax specified by the PlaceholderConfigurerSupport class
Default property values can be defined globally for each configurer
instance via the properties property, or on a property-by-property
basis using the default value separator which is ":" by default and
customizable via setValueSeparator(String).
and Spring SpEL expression templating.
From Spring Boot docs on externalized configuration
Finally, while you can write a SpEL expression in #Value, such
expressions are not processed from Application property files.
This work for me:
spring.datasource.url=jdbc:mysql://${DB_IP:localhost}:3306/app
spring.datasource.username=${SPRING_DATASOURCE_USERNAME:mylocaluser}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:localpass}
Because this is probably interesting to others who come here, you can override any properties file w/ an env variable implicitly. Let's say you have property.
someapp.foo
Then you can define an env variable SOMEAPP_FOO (capital letters and . -> _ ) and spring will implicitly set the property from the env. variable.
Described further here: https://hughesadam87.medium.com/how-to-override-spring-properties-with-env-vars-82ee1db2ae78

How to find out if a property is defined in placeholder in Spring?

Due to some backward compatible reasons, some old apps didn't define a new property in its app.properties, which is referred in a shared Spring context.xml thru placeholders. Is there a way in Spring to find out if a property is defined in the placeholder?
There are posts about setting null default values using Spring expression language. But, I don't see an answer on how to determine if a property is defined in the placeholder, or you'd get "Could not resolve placeholder ...".
I'm looking for something like
<constructor-arg value="#{someAPI('my.new.prop') ? ${my.new.prop} : #null}" />
Your input is highly appreciated!

How is the following property resolved ${sys:foo} by Log4j2

I am looking at a log4j2.xml configuration from Spring Boot and see the following configuration (unimportant parts removed):
<Property name="PID">????</Property>
<Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
<Property name="LOG_LEVEL_PATTERN">%5p</Property>
<Property name="LOG_PATTERN">
... %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta}
... %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
</Property>
Lets take a look: ${LOG_LEVEL_PATTERN} is clear, it resolves to the property defined in the line above. But The next two variable expansions have a sys: prefixed, what exactly does that do?
More importantly who does the resolution of these variables (in particular sys:)? Is it Log4j2 specific? Is this expansion done by Maven or maybe the Spring Framework which I am using?
Take a look at org.apache.logging.log4j.core.lookup. In particular the Interpolator class and the SystemPropertiesLookup class.
The Interpolator will attempt to use the SystemPropertiesLookup which returns
return System.getProperty(key);
If the result is null it will then try a default lookup, which is a MapLookup.
if (map == null) {
return null;
}
return map.get(key);
This map is the map defined by the properties section in the config.
TLDR
The lookups are handled by log4j2 core. First it will try and get the system property. if no system property exists it will then look for a property defined in the properties section of your config.

reading a dynamic property list into a spring managed bean

I've been searching but cannot find these steps. I hope I'm missing something obvious.
I have a properties file with the following contents:
machines=A,B
I have another file like that but having a different number of members in the machines element like this:
machines=B,C,D
My question is how do I load this variable-length machines variable into a bean in my spring config in a generic way?
something like this:
<property name="machines" value="${machines}"/>
where machines is an array or list in my java code. I can define it however I want if I can figure out how to do this.
Basically I'd rather have spring do the parsing and stick each value into a list element instead of me having to write something that reads in the full machines string and do the parsing myself (with the comma delimiter) to put each value into an array or list. Is there an easy way to do this that I'm missing?
You may want to take a look at Spring's StringUtils class. It has a number of useful methods to convert a comma separated list to a Set or a String array. You can use any of these utility methods, using Spring's factory-method framework, to inject a parsed value into your bean. Here is an example:
<property name="machines">
<bean class="org.springframework.util.StringUtils" factory-method="commaDelimitedListToSet">
<constructor-arg type="java.lang.String" value="${machines}"/>
</bean>
</property>
In this example, the value for 'machines' is loaded from the properties file.
If an existing utility method does not meet your needs, it is pretty straightforward to create your own. This technique allows you to execute any static utility method.
Spring EL makes easier.
Java:
List <String> machines;
Context:
<property name="machines" value="#{T(java.util.Arrays).asList('${machines}')}"/>
If you make the property "machines" a String array, then spring will do it automatically for you
machines=B,C,D
<property name="machines" value="${machines}"/>
public void setMachines(String[] test) {
Since Spring 3.0, it is also possible to read in the list of values using the #Value annotation.
Property file:
machines=B,C,D
Java code:
import org.springframework.beans.factory.annotation.Value;
#Value("#{'${machines}'.split(',')}")
private List<String> machines;
You can inject the values to the list directly without boilerplate code.(Spring 3.1+)
#Value("${machines}")
private List<String> machines;
for the key "machines=B,C,D" in the properties file by creating following two instance in your configuration.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
Those will cover all the separate based split and whitespace trim as well.

Difference between using bean id and name in Spring configuration file

Is there any difference between using an id attribute and name attribute on a <bean> element in a Spring configuration file?
From the Spring reference, 3.2.3.1 Naming Beans:
Every bean has one or more ids (also
called identifiers, or names; these
terms refer to the same thing). These
ids must be unique within the
container the bean is hosted in. A
bean will almost always have only one
id, but if a bean has more than one
id, the extra ones can essentially be
considered aliases.
When using XML-based configuration
metadata, you use the 'id' or 'name'
attributes to specify the bean
identifier(s). The 'id' attribute
allows you to specify exactly one id,
and as it is a real XML element ID
attribute, the XML parser is able to
do some extra validation when other
elements reference the id; as such, it
is the preferred way to specify a bean
id. However, the XML specification
does limit the characters which are
legal in XML IDs. This is usually not
a constraint, but if you have a need
to use one of these special XML
characters, or want to introduce other
aliases to the bean, you may also or
instead specify one or more bean ids,
separated by a comma (,), semicolon
(;), or whitespace in the 'name'
attribute.
So basically the id attribute conforms to the XML id attribute standards whereas name is a little more flexible. Generally speaking, I use name pretty much exclusively. It just seems more "Spring-y".
Since Spring 3.1 the id attribute is an xsd:string and permits the same range of characters as the name attribute.
The only difference between an id and a name is that a name can contain multiple aliases separated by a comma, semicolon or whitespace, whereas an id must be a single value.
From the Spring 3.2 documentation:
In XML-based configuration metadata, you use the id and/or name attributes to specify the bean identifier(s). The id attribute allows you to specify exactly one id. Conventionally these names are alphanumeric ('myBean', 'fooService', etc), but may special characters as well. If you want to introduce other aliases to the bean, you can also specify them in the name attribute, separated by a comma (,), semicolon (;), or white space. As a historical note, in versions prior to Spring 3.1, the id attribute was typed as an xsd:ID, which constrained possible characters. As of 3.1, it is now xsd:string. Note that bean id uniqueness is still enforced by the container, though no longer by XML parsers.
Either one would work. It depends on your needs:
If your bean identifier contains special character(s) for example (/viewSummary.html), it wont be allowed as the bean id, because it's not a valid XML ID. In such cases you could skip defining the bean id and supply the bean name instead.
The name attribute also helps in defining aliases for your bean, since it allows specifying multiple identifiers for a given bean.
let me answer below question
Is there any difference between using an id attribute and using a name attribute on a <bean> tag,
There is no difference. you will experience same effect when id or name is used on a <bean> tag .
How?
Both id and name attributes are giving us a means to provide identifier value to a bean (For this moment, think id means id but not identifier). In both the cases, you will see same result if you call applicationContext.getBean("bean-identifier"); .
Take #Bean, the java equivalent of <bean> tag, you wont find an id attribute. you can give your identifier value to #Bean only through name attribute.
Let me explain it through an example :
Take this configuration file, let's call it as spring1.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="foo" class="com.intertech.Foo"></bean>
<bean id="bar" class="com.intertech.Bar"></bean>
</beans>
Spring returns Foo object for, Foo f = (Foo) context.getBean("foo");
. Replace id="foo" with name="foo" in the above spring1.xml, You will still see the same result.
Define your xml configuration like,
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="fooIdentifier" class="com.intertech.Foo"></bean>
<bean name="fooIdentifier" class="com.intertech.Foo"></bean>
</beans>
You will get BeanDefinitionParsingException. It will say, Bean name 'fooIdentifier' is already used in this element. By the way, This is the same exception you will see if you have below config
<bean name="fooIdentifier" class="com.intertech.Foo"></bean>
<bean name="fooIdentifier" class="com.intertech.Foo"></bean>
If you keep both id and name to the bean tag, the bean is said to have 2 identifiers. you can get the same bean with any identifier.
take config as
<?xml version="1.0" encoding="UTF-8"?><br>
<beans ...>
<bean id="fooById" name="fooByName" class="com.intertech.Foo"></bean>
<bean id="bar" class="com.intertech.Bar"></bean>
</beans>
the following code prints true
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(...);
Foo fooById = (Foo) context.getBean("fooById")// returns Foo object;
Foo fooByName = (Foo) context.getBean("fooByName")// returns Foo object;
System.out.println(fooById == fooByName) //true
Is there difference in defining Id & name in ApplicationContext xml ? No
As of 3.1(spring), id is also defined as an xsd:string type.
It means whatever characters allowed in defining name are also allowed in Id.
This was not possible prior to Spring 3.1.
Why to use name when it is same as Id ?
It is useful for some situations, such as allowing each component in an application to refer to a common dependency by using a bean name that is specific to that component itself.
For example, the configuration metadata for subsystem A may refer to a DataSource via the name subsystemA-dataSource. The configuration metadata for subsystem B may refer to a DataSource via the name subsystemB-dataSource. When composing the main application that uses both these subsystems the main application refers to the DataSource via the name myApp-dataSource. To have all three names refer to the same object you add to the MyApp configuration metadata the following
<bean id="myApp-dataSource" name="subsystemA-dataSource,subsystemB-dataSource" ..../>
Alternatively, You can have separate xml configuration files for each sub-system and then you can make use of
alias to define your own names.
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
Both id and name are bean identifiers in Spring IOC container/ApplicationContecxt. The id attribute lets you specify exactly one id but using name attribute you can give alias name to that bean.
You can check the spring doc here.

Resources