Hot questions for Spring Expression Language

Top 10 Java Open Source / Spring / Spring Expression Language

Question:

I'm a little confused concerning when to use ${...} compared to #{...}. Spring's documentation only uses #{...}, but there are plenty of examples that use ${...}. Furthermore, when I started with SpEL I was told to use ${...} and it works fine.

For those who are confused, an example of how I use it would be

@Component
public class ProxyConfiguration {

    @Value("${proxy.host}")
    private String host;
    @Value("${proxy.port}")
    private String port;

    :
}

and some property file:

proxy.host=myproxy.host
proxy.port=8000

My questions are:

  • what are the differences or is it the same?
  • is one version deprecated so I should use the other one?

Answer:

${...} is the property placeholder syntax. It can only be used to dereference properties.

#{...} is SpEL syntax, which is far more capable and complex. It can also handle property placeholders, and a lot more besides.

Both are valid, and neither is deprecated.

Question:

Is it possible to use Spring @Value, to map values from properties file to the HashMap.

Currently I have something like this, and mapping one value is not a problem. But I need to map custom values in HashMap expirations. Is something like this possible?

@Service
@PropertySource(value = "classpath:my_service.properties")
public class SomeServiceImpl implements SomeService {


    @Value("#{conf['service.cache']}")
    private final boolean useCache = false;

    @Value("#{conf['service.expiration.[<custom name>]']}")
    private final HashMap<String, String> expirations = new HashMap<String, String>();

Property file: 'my_service.properties'

service.cache=true
service.expiration.name1=100
service.expiration.name2=20

Is it posible to map like this key:value set

  • name1 = 100

  • name2 = 20


Answer:

Is it possible to use Spring @Value, to map values from properties file to the HashMap?

Yes, it is. With a little help of code and Spel.

Firstly, consider this singleton Spring-bean (you should scan it):

@Component("PropertySplitter")
public class PropertySplitter {

    /**
     * Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
     */
    public Map<String, String> map(String property) {
        return this.map(property, ",");
    }

    /**
     * Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
     */
    public Map<String, List<String>> mapOfList(String property) {
        Map<String, String> map = this.map(property, ";");

        Map<String, List<String>> mapOfList = new HashMap<>();
        for (Entry<String, String> entry : map.entrySet()) {
            mapOfList.put(entry.getKey(), this.list(entry.getValue()));
        }

        return mapOfList;
    }

    /**
     * Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
     */
    public List<String> list(String property) {
        return this.list(property, ",");
    }

    /**
     * Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
     */
    public List<List<String>> groupedList(String property) {
        List<String> unGroupedList = this.list(property, ";");

        List<List<String>> groupedList = new ArrayList<>();
        for (String group : unGroupedList) {
            groupedList.add(this.list(group));
        }

        return groupedList;

    }

    private List<String> list(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
    }

    private Map<String, String> map(String property, String splitter) {
        return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
    }

}

Note: PropertySplitter class uses Splitter utility from Guava. Please refer to its documentation for further details.

Then, in some bean of yours:

@Component
public class MyBean {

    @Value("#{PropertySplitter.map('${service.expiration}')}")
    Map<String, String> propertyAsMap;

}

And finally, the property:

service.expiration = name1:100,name2:20

It's not exactly what you've asked, because this PropertySplitter works with one single property that is transformed into a Map, but I think you could either switch to this way of specifying properties, or modify the PropertySplitter code so that it matches the more hierarchical way you desire.

Question:

I have a XML that looks like this

<element1>
    <element2>
        <element3>    
            <element4>Hello</element4>
            <element5>World</element5>
        </element3>
        <element3>    
            <element4>Hello2</element4>
            <element5>World2</element5>
        </element3>
        <element3>    
            <element4>Hello3</element4>
            <element5>World3</element5>
        </element3>
    </element2>
</element1>

I am trying to use Xpath to get a result like this:

Hello.World
Hello2.World2
Hello3.World3

I used concat function below but I did not get correct result.

Concat function:

concat(/element1/element2/element3/element4/text(),".", /element1/element2/element3/element5/text())

Result I got:

Hello.World

How can I get the correct result? I am using XPath with Camel Spring DSL.

Edit:

Solutions in XQuery, XSLT and SPel are also appreciated.

Edit

I tried string-join and it did not work:

string-join function:

string-join((/element1/element2/element3/element4/text(), /element1/element2/element3/element5/text()),".")

Result I got:

Hello.Hello2.Hello3.World.World2.World3

Answer:

Try this expression...

string-join(//element3/(concat(element4/text(), '.', element5/text())), "&#10;")

Question:

I'm looking for a way to extend Spring Security Expressions to support an existing security infrastructure. I'm aware you can extend the MethodSecurityExpressionRoot as described here, but I also found reference to directly calling static methods through Spring Expression Language (Spring EL or SpEL). Unfortunately the official page on Spring Expression methods doesn't directly describe how to do this.

How can I invoke a static method through Spring Expression methods?


Answer:

By using the T(fully.qualified.name).methodName() syntax:

You can use the special T operator to specify an instance of java.lang.Class (the type). Static methods are invoked by using this operator as well. The StandardEvaluationContext uses a TypeLocator to find types, and the StandardTypeLocator (which can be replaced) is built with an understanding of the java.lang package. This means that T() references to types within java.lang do not need to be fully qualified, but all other type references must be.

The T element returns a reference to the type instead of an instance. For example, the equivalent of Collections.singleton("Hello") is

T(java.util.Collections).singleton('Hello')

Question:

I want to use the @Value annotation to inject a Double property such as:

@Service
public class MyService {

    @Value("${item.priceFactor}")
    private Double priceFactor = 0.1;

// ...

and using Spring property placeholder (Properties files):

item.priceFactor=0.1

I get Exception:

org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.lang.Double'; nested exception is java.lang.NumberFormatException: For input string: "${item.priceFactor}"

Is there a way to use a Double value coming from a properties file?


Answer:

Try changing the following line

@Value("${item.priceFactor}")

to

@Value("#{new Double('${item.priceFactor}')}")

Question:

Is there a simple way in thymeleaf to show the content of an attribute property if the property and the attribute exist? If there's an attribute "error" with a property "summary" in my html page, I'd like to show it:

<span th:text="${error.summary}">error summary</span>

If there is no attribute "error" the following error is raised:

org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Field or property 'summary' cannot be found on null

Currently I'm using the following approach, which just seems too complicated.

<span th:if="${error != null and error.summary != null}"><span th:text="${error.summary}">error summary</span></span>

Is there a simpler way to achieve that?


Answer:

Sure! Since the processor associated with the th:if attribute has a higher precedence than the one associated with the th:text attribute, it will be evaluated first. Thus you can write:

<span th:if="${error != null && error.summary != null}" th:text="${error.summary}">Static summary</span>

You could even shorten it using:

<span th:text="${error?.summary}">Static summary</span>

But I think in this case, whether the summary exist or not, the span tag will be created, which is a bit ugly.

See more info about conditional expressions here.

Question:

I'm using Thymeleaf template engine with spring and I'd like to display text stored throught a multiline textarea.

In my database multiline string are store with "\n" like this : "Test1\nTest2\n...."

With th:text i've got : "Test1 Test2" with no line break.

How I can display line break using Thymeleaf and avoid manually "\n" replacing with < br/> and then avoid using th:utext (this open form to xss injection) ?

Thanks !


Answer:

Two of your options:

  1. Use th:utext - easy setup option, but harder to read and remember
  2. Create a custom processor and dialect - more involved setup, but easier, more readable future use.

Option 1:

You can use th:utext if you escape the text using the expression utility method #strings.escapeXml( text ) to prevent XSS injection and unwanted formatting - http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#strings

To make this platform independent, you can use T(java.lang.System).getProperty('line.separator') to grab the line separator.

Using the existing Thymeleaf expression utilities, this works:

<p th:utext="${#strings.replace( #strings.escapeXml( text ),T(java.lang.System).getProperty('line.separator'),'&lt;br /&gt;')}" ></p>

Option 2:

The API for this is now different in 3 (I wrote this tutorial for 2.1) Hopefully you can combine the below logic with their official tutorial. One day maybe I'll have a minute to update this completely. But for now: Here's the official Thymeleaf tutorial for creating your own dialect.

Once setup is complete, all you will need to do to accomplish escaped textline output with preserved line breaks:

<p fd:lstext="${ text }"></p>

The main piece doing the work is the processor. The following code will do the trick:

package com.foo.bar.thymeleaf.processors 

import java.util.Collections;
import java.util.List;

import org.thymeleaf.Arguments;
import org.thymeleaf.Configuration;
import org.thymeleaf.dom.Element;
import org.thymeleaf.dom.Node;
import org.thymeleaf.dom.Text;
import org.thymeleaf.processor.attr.AbstractChildrenModifierAttrProcessor;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.unbescape.html.HtmlEscape;

public class HtmlEscapedWithLineSeparatorsProcessor extends
        AbstractChildrenModifierAttrProcessor{

    public HtmlEscapedWithLineSeparatorsProcessor(){
        //only executes this processor for the attribute 'lstext'
        super("lstext");
    }

    protected String getText( final Arguments arguments, final Element element,
            final String attributeName) {

        final Configuration configuration = arguments.getConfiguration();

        final IStandardExpressionParser parser =
            StandardExpressions.getExpressionParser(configuration);

        final String attributeValue = element.getAttributeValue(attributeName);

        final IStandardExpression expression =
            parser.parseExpression(configuration, arguments, attributeValue);

        final String value = (String) expression.execute(configuration, arguments);

        //return the escaped text with the line separator replaced with <br />
        return HtmlEscape.escapeHtml4Xml( value ).replace( System.getProperty("line.separator"), "<br />" );


    }



    @Override
    protected final List<Node> getModifiedChildren(
            final Arguments arguments, final Element element, final String attributeName) {

        final String text = getText(arguments, element, attributeName);
        //Create new text node signifying that content is already escaped.
        final Text newNode = new Text(text == null? "" : text, null, null, true);
        // Setting this allows avoiding text inliners processing already generated text,
        // which in turn avoids code injection.
        newNode.setProcessable( false );

        return Collections.singletonList((Node)newNode);


    }

    @Override
    public int getPrecedence() {
        // A value of 10000 is higher than any attribute in the SpringStandard dialect. So this attribute will execute after all other attributes from that dialect, if in the same tag.
        return 11400;
    }


}

Now that you have the processor, you need a custom dialect to add the processor to.

package com.foo.bar.thymeleaf.dialects;

import java.util.HashSet;
import java.util.Set;

import org.thymeleaf.dialect.AbstractDialect;
import org.thymeleaf.processor.IProcessor;

import com.foo.bar.thymeleaf.processors.HtmlEscapedWithLineSeparatorsProcessor;

public class FooDialect extends AbstractDialect{

    public FooDialect(){
        super();
    }

    //This is what all the dialect's attributes/tags will start with. So like.. fd:lstext="Hi David!<br />This is so much easier..."
    public String getPrefix(){
        return "fd";
    }

    //The processors.
    @Override
    public Set<IProcessor> getProcessors(){
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add( new HtmlEscapedWithLineSeparatorsProcessor() );
        return processors;
    }

}

Now you need to add it to your xml or java configuration:

If you are writing a Spring MVC application, you just have to set it at the additionalDialects property of the Template Engine bean, so that it is added to the default SpringStandard dialect:

    <bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
  <property name="templateResolver" ref="templateResolver" />
  <property name="additionalDialects">
    <set>
      <bean class="com.foo.bar.thymeleaf.dialects.FooDialect"/>
    </set>
  </property>
    </bean>

Or if you are using Spring and would rather use JavaConfig you can create a class annotated with @Configuration in your base package that contains the dialect as a managed bean:

package com.foo.bar;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.foo.bar.thymeleaf.dialects.FooDialect;

@Configuration
public class TemplatingConfig {

    @Bean
    public FooDialect fooDialect(){
        return new FooDialect();
    }
}

Here are some further references on creating custom processors and dialects: http://www.thymeleaf.org/doc/articles/sayhelloextendingthymeleaf5minutes.html , http://www.thymeleaf.org/doc/articles/sayhelloagainextendingthymeleafevenmore5minutes.html and http://www.thymeleaf.org/doc/tutorials/2.1/extendingthymeleaf.html

Question:

At Spring's @Cacheable annotation I want to specify an unless condition.

However my return value is neither a primitive type nor a Java bean, but an Enum.

How can I compare for equality with another Enum in SpEL (Spring Expression Language)?


Answer:

#result == T(fully.qualified.path.to.AnEnum).A_VALUE

Question:

I'm looking for 2 things:

  1. How to disable all caching during development with Spring boot "dev" profile. There doesn't seam to be a general setting to turn it all off in application.properties. What's the easiest way?

  2. How to disable caching for a specific method? I tried to use SpEl like this:

    @Cacheable(value = "complex-calc", condition = "#{${spring.profiles.active} != 'dev'}") public String someBigCalculation(String input){ ... }

But I can get it to work. There are a couple of questions on SO related to this, but they refer to XML config or other things, but I'm using Spring Boot 1.3.3 and this uses auto-configuration.

I don't want to over-complicate things.


Answer:

The type of cache is by default automatically detected and configured. However you can specify which cache type to use by adding spring.cache.type to your configuration. To disable it set the value to NONE.

As you want to do it for a specific profile add it to that profiles application.properties in this case modify the application-dev.properties and add

spring.cache.type=NONE

This will disable caching.

Question:

I followed the guide at http://spring.io/guides/gs/batch-processing/ but it describes a job with no configurable parameters. I'm using Maven to build my project.

I'm porting an existing job that I have defined in XML and would like to pass-in the jobParameters through the command.

I tried the following :

@Configuration
@EnableBatchProcessing
public class MyBatchConfiguration {

    // other beans ommited

    @Bean 
    public Resource destFile(@Value("#{jobParameters[dest]}") String dest) {
        return new FileSystemResource(dest);
    }

}

Then I compile my project using :

mvn clean package

Then I try to launch the program like this :

java my-jarfile.jar dest=/tmp/foo

And I get an exception saying :

[...]
Caused by: org.springframework.expression.spel.SpelEvaluationException: 
EL1008E:(pos 0): Field or property 'jobParameters' cannot be found on object of 
type 'org.springframework.beans.factory.config.BeanExpressionContext'

Thanks !


Answer:

Parse in job parameters from the command line and then create and populate JobParameters.

public JobParameters getJobParameters() {
    JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
    jobParametersBuilder.addString("dest", <dest_from_cmd_line);
    jobParametersBuilder.addDate("date", <date_from_cmd_line>);
    return jobParametersBuilder.toJobParameters();
}

Pass them to your job via JobLauncher -

JobLauncher jobLauncher = context.getBean(JobLauncher.class);
JobExecution jobExecution = jobLauncher.run(job, jobParameters);

Now you can access them using code like -

@Bean 
@StepScope
public Resource destFile(@Value("#{jobParameters[dest]}") String dest) {
    return new FileSystemResource(dest);
}

Or in a @Configuration class that is configuring Spring Batch Job artifacts like - ItemReader, ItemWriter, etc...

@Bean
@StepScope
public JdbcCursorItemReader<MyPojo> reader(@Value("#{jobParameters}") Map jobParameters) {
    return new MyReaderHelper.getReader(jobParameters);
}

Question:

I need to access system properties in a Thymeleaf template. It would be nice if this was possible so that I don't have to populate the spring mvc model explicitly with properties. I'm trying to use SPEL for this purpose but it's not working.

<h2 th:text="${ systemProperties['serverName'] }">Service name</h2>

<h2 th:text="*{ systemProperties['serverName'] }">Service name</h2>

Both of these give me:

Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1012E:(pos 17): Cannot index into a null value

Even if I try to access a jdk property it gives the same error so I know it's not the fact that the property is missing. What am I doing wrong or is there another way to do this?


Answer:

I use

${@environment.getProperty('myPropertyName')}