Skip to content

Commit

Permalink
Fixes #95. Fixes #84. Allow the activation of the SpringEL compiler b…
Browse files Browse the repository at this point in the history
…y means of a flag at the SpringTemplateEngine or the SpringStandardDialect.
  • Loading branch information
danielfernandez committed Feb 11, 2016
1 parent 83b993c commit 02c1c29
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
*/
package org.thymeleaf.spring4;

import java.util.Set;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.dialect.IDialect;
import org.thymeleaf.messageresolver.IMessageResolver;
import org.thymeleaf.messageresolver.StandardMessageResolver;
import org.thymeleaf.spring4.dialect.SpringStandardDialect;
Expand Down Expand Up @@ -63,6 +66,8 @@ public class SpringTemplateEngine
private MessageSource templateEngineMessageSource = null;




public SpringTemplateEngine() {
super();
// This will set the SpringStandardDialect, overriding the Standard one set in the super constructor
Expand Down Expand Up @@ -112,6 +117,83 @@ public void setTemplateEngineMessageSource(final MessageSource templateEngineMes



/**
* <p>
* Returns whether the SpringEL compiler should be enabled in SpringEL expressions or not.
* </p>
* <p>
* (This is just a convenience method, equivalent to calling
* {@link SpringStandardDialect#getEnableSpringELCompiler()} on the dialect instance itself. It is provided
* here in order to allow users to enable the SpEL compiler without
* having to directly create instances of the {@link SpringStandardDialect})
* </p>
* <p>
* Expression compilation can significantly improve the performance of Spring EL expressions, but
* might not be adequate for every environment. Read
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation">the
* official Spring documentation</a> for more detail.
* </p>
* <p>
* Also note that although Spring includes a SpEL compiler since Spring 4.1, most expressions
* in Thymeleaf templates will only be able to properly benefit from this compilation step when at least
* Spring Framework version 4.2.4 is used.
* </p>
* <p>
* This flag is set to <tt>false</tt> by default.
* </p>
*
* @return <tt>true</tt> if SpEL expressions should be compiled if possible, <tt>false</tt> if not.
*/
public boolean getEnableSpringELCompiler() {
final Set<IDialect> dialects = getDialects();
for (final IDialect dialect : dialects) {
if (dialect instanceof SpringStandardDialect) {
return ((SpringStandardDialect) dialect).getEnableSpringELCompiler();
}
}
return false;
}


/**
* <p>
* Sets whether the SpringEL compiler should be enabled in SpringEL expressions or not.
* </p>
* <p>
* (This is just a convenience method, equivalent to calling
* {@link SpringStandardDialect#setEnableSpringELCompiler(boolean)} on the dialect instance itself. It is provided
* here in order to allow users to enable the SpEL compiler without
* having to directly create instances of the {@link SpringStandardDialect})
* </p>
* <p>
* Expression compilation can significantly improve the performance of Spring EL expressions, but
* might not be adequate for every environment. Read
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation">the
* official Spring documentation</a> for more detail.
* </p>
* <p>
* Also note that although Spring includes a SpEL compiler since Spring 4.1, most expressions
* in Thymeleaf templates will only be able to properly benefit from this compilation step when at least
* Spring Framework version 4.2.4 is used.
* </p>
* <p>
* This flag is set to <tt>false</tt> by default.
* </p>
*
* @param enableSpringELCompiler <tt>true</tt> if SpEL expressions should be compiled if possible, <tt>false</tt> if not.
*/
public void setEnableSpringELCompiler(final boolean enableSpringELCompiler) {
final Set<IDialect> dialects = getDialects();
for (final IDialect dialect : dialects) {
if (dialect instanceof SpringStandardDialect) {
((SpringStandardDialect) dialect).setEnableSpringELCompiler(enableSpringELCompiler);
}
}
}




public void afterPropertiesSet() throws Exception {

final MessageSource messageSource =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
package org.thymeleaf.spring4.dialect;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.thymeleaf.expression.IExpressionObjectFactory;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.spring4.expression.SPELVariableExpressionEvaluator;
import org.thymeleaf.spring4.expression.SpringStandardConversionService;
import org.thymeleaf.spring4.expression.SpringStandardExpressionObjectFactory;
import org.thymeleaf.spring4.expression.SpringStandardExpressions;
import org.thymeleaf.spring4.processor.SpringActionTagProcessor;
import org.thymeleaf.spring4.processor.SpringErrorClassTagProcessor;
import org.thymeleaf.spring4.processor.SpringErrorsTagProcessor;
Expand Down Expand Up @@ -87,6 +89,8 @@ public class SpringStandardDialect extends StandardDialect {
public static final int PROCESSOR_PRECEDENCE = 1000;


private boolean enableSpringELCompiler = false;


private final IExpressionObjectFactory SPRING_STANDARD_EXPRESSION_OBJECTS_FACTORY = new SpringStandardExpressionObjectFactory();
private final IStandardConversionService SPRING_STANDARD_CONVERSION_SERVICE = new SpringStandardConversionService();
Expand All @@ -101,6 +105,60 @@ public SpringStandardDialect() {



/**
* <p>
* Returns whether the SpringEL compiler should be enabled in SpringEL expressions or not.
* </p>
* <p>
* Expression compilation can significantly improve the performance of Spring EL expressions, but
* might not be adequate for every environment. Read
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation">the
* official Spring documentation</a> for more detail.
* </p>
* <p>
* Also note that although Spring includes a SpEL compiler since Spring 4.1, most expressions
* in Thymeleaf templates will only be able to properly benefit from this compilation step when at least
* Spring Framework version 4.2.4 is used.
* </p>
* <p>
* This flag is set to <tt>false</tt> by default.
* </p>
*
* @return <tt>true</tt> if SpEL expressions should be compiled if possible, <tt>false</tt> if not.
*/
public boolean getEnableSpringELCompiler() {
return enableSpringELCompiler;
}


/**
* <p>
* Sets whether the SpringEL compiler should be enabled in SpringEL expressions or not.
* </p>
* <p>
* Expression compilation can significantly improve the performance of Spring EL expressions, but
* might not be adequate for every environment. Read
* <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation">the
* official Spring documentation</a> for more detail.
* </p>
* <p>
* Also note that although Spring includes a SpEL compiler since Spring 4.1, most expressions
* in Thymeleaf templates will only be able to properly benefit from this compilation step when at least
* Spring Framework version 4.2.4 is used.
* </p>
* <p>
* This flag is set to <tt>false</tt> by default.
* </p>
*
* @param enableSpringELCompiler <tt>true</tt> if SpEL expressions should be compiled if possible, <tt>false</tt> if not.
*/
public void setEnableSpringELCompiler(final boolean enableSpringELCompiler) {
this.enableSpringELCompiler = enableSpringELCompiler;
}




@Override
public IStandardVariableExpressionEvaluator getVariableExpressionEvaluator() {
return SPELVariableExpressionEvaluator.INSTANCE;
Expand Down Expand Up @@ -129,6 +187,19 @@ public Set<IProcessor> getProcessors(final String dialectPrefix) {



@Override
public Map<String, Object> getExecutionAttributes() {

final Map<String,Object> executionAttributes = super.getExecutionAttributes();
executionAttributes.put(
SpringStandardExpressions.ENABLE_SPRING_EL_COMPILER_ATTRIBUTE_NAME, Boolean.valueOf(getEnableSpringELCompiler()));

return executionAttributes;

}




/**
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
Expand All @@ -38,11 +40,13 @@
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.expression.IExpressionObjects;
import org.thymeleaf.spring4.util.FieldUtils;
import org.thymeleaf.spring4.util.SpringVersionUtils;
import org.thymeleaf.standard.expression.IStandardConversionService;
import org.thymeleaf.standard.expression.IStandardVariableExpressionEvaluator;
import org.thymeleaf.standard.expression.StandardExpressionExecutionContext;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.thymeleaf.standard.util.StandardExpressionUtils;
import org.thymeleaf.util.ClassLoaderUtils;

/**
* <p>
Expand Down Expand Up @@ -71,9 +75,48 @@ public class SPELVariableExpressionEvaluator

private static final Logger logger = LoggerFactory.getLogger(SPELVariableExpressionEvaluator.class);

private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private static final SpelExpressionParser PARSER_WITHOUT_COMPILED_SPEL = new SpelExpressionParser();
private static final SpelExpressionParser PARSER_WITH_COMPILED_SPEL;


/*
* INITIALIZATION OF THE Spring EL parser.
* Two parsers will be always initialized: one with expression compilation enabled (if the Spring version allows)
* and another one without. Then during template execution we will check which one should be used.
*/
static {

SpelExpressionParser spelCompilerExpressionParser = null;
if (SpringVersionUtils.isSpring41AtLeast()) {
try {
final SpelParserConfiguration spelParserConfiguration =
new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, // Enable the SpEL compiler
ClassLoaderUtils.getClassLoader(SPELVariableExpressionEvaluator.class));
spelCompilerExpressionParser = new SpelExpressionParser(spelParserConfiguration);
} catch (final Throwable t) {
if (logger.isDebugEnabled()) {
// We are issuing a WARN even if we checked for DEBUG, but in this case we will log the entire
// exception trace (if DEBUG is not available, we will avoid polluting the log).
logger.warn(
"An error happened during the initialization of the Spring EL expression compiler. " +
"However, initialization was completed anyway. Note that compilation of SpEL expressions " +
"will not be available even if you configure your Spring dialect to use them.", t);
} else {
logger.warn(
"An error happened during the initialization of the Spring EL expression compiler. " +
"However, initialization was completed anyway. Note that compilation of SpEL expressions " +
"will not be available even if you configure your Spring dialect to use them. For more " +
"info, set your log to at least DEBUG level: " + t.getMessage());
}
}
}

PARSER_WITH_COMPILED_SPEL = spelCompilerExpressionParser;

}



protected SPELVariableExpressionEvaluator() {
super();
}
Expand Down Expand Up @@ -118,10 +161,17 @@ public final Object evaluate(
final IEngineConfiguration configuration = context.getConfiguration();


/*
* SELECT THE ADEQUATE SpEL EXPRESSION PARSER depending on whether SpEL compilation is enabled
*/
final SpelExpressionParser spelExpressionParser =
PARSER_WITH_COMPILED_SPEL != null && SpringStandardExpressions.isSpringELCompilerEnabled(configuration)?
PARSER_WITH_COMPILED_SPEL : PARSER_WITHOUT_COMPILED_SPEL;

/*
* OBTAIN THE EXPRESSION (SpelExpression OBJECT) FROM THE CACHE, OR PARSE IT
*/
final ComputedSpelExpression exp = getExpression(configuration, spelExpression);
final ComputedSpelExpression exp = getExpression(configuration, spelExpressionParser, spelExpression);


/*
Expand Down Expand Up @@ -237,7 +287,8 @@ public final Object evaluate(
}


private static ComputedSpelExpression getExpression(final IEngineConfiguration configuration, final String spelExpression) {
private static ComputedSpelExpression getExpression(
final IEngineConfiguration configuration, final SpelExpressionParser spelParser, final String spelExpression) {

ComputedSpelExpression exp = null;
ICache<ExpressionCacheKey, Object> cache = null;
Expand All @@ -252,7 +303,7 @@ private static ComputedSpelExpression getExpression(final IEngineConfiguration c

if (exp == null) {

final SpelExpression spelExpressionObject = (SpelExpression) PARSER.parseExpression(spelExpression);
final SpelExpression spelExpressionObject = (SpelExpression) spelParser.parseExpression(spelExpression);
final boolean mightNeedExpressionObjects = StandardExpressionUtils.mightNeedExpressionObjects(spelExpression);

exp = new ComputedSpelExpression(spelExpressionObject, mightNeedExpressionObjects);
Expand Down
Loading

0 comments on commit 02c1c29

Please # to comment.