关于 java:Spring StandardTypeLocator in StandardEvaluationContext: 注册新的导入前缀以在 Thymeleaf 模板中使用

Spring StandardTypeLocator in StandardEvaluationContext: registering new import prefixes for use in Thymeleaf templates

我们正在使用视图层中的 Thymeleaf 模板库和提供 SPEL 支持的 Thymeleaf SpringTemplateEngine 开发一个 Spring MVC (v4) Web 应用程序。

当我们在模板中引用类型时(例如,为了访问静态实用程序方法或枚举),我们必须包含完全限定名称,因为 Spring StandardEvaluationContext StandardTypeLocator 默认只知道 java.lang 包。我可以在 Spring API 中看到,我们需要使用 registerImport(String prefix) 方法将自己的包添加到类型定位器中,但我不知道如何获取模板中使用的默认评估上下文能够做到这一点。

我想通过替换这种东西来整理我们的 Thymeleaf HTML 模板:

1
T(org.apache.commons.io.FileUtils).byteCountToDisplaySize(1024)

与:

1
T(FileUtils).byteCountToDisplaySize(1024)

我尝试将一个 EvaluationContext 自动装配到一个控制器中,看看我是否可以得到它,但 Spring 告诉我没有找到符合条件的 bean。任何建议表示赞赏!


我正在使用基于 JAVA 的 spring 配置。所以在 spring 安全配置类中(必须有一个 @EnableGlobalMethodSecurity(prePostEnabled = true) 注释),我正在注入一个自定义 MethodSecurityExpressionHandler bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
      DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler() {

        @Override
        public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {

          StandardEvaluationContext evaluationContext = super.createEvaluationContextInternal(auth, mi);

          //Register custom package paths, since StandardTypeLocator is only aware of"java.lang"
          //So there will be no need to provide a fully qualified path
          ((StandardTypeLocator) evaluationContext.getTypeLocator()).registerImport("desired.path");

          return evaluationContext;
        }
      };

      expressionHandler.setPermissionEvaluator(new ExpressionAccessPermissionEvaluator()); //register some custom PermissionEvaluator if any
      return expressionHandler;
    }
  }

谢谢


我不确定这是否能解决您的问题,但 ThymeleafViewResolver 类具有 addStaticVariable 方法,可在处理视图之前将变量添加到上下文中。

我做了一个小测试:

1
2
3
4
5
6
7
@Autowired
ThymeleafViewResolver thymeleafViewResolver;

@PostConstruct
public void postConstruct() {
    thymeleafViewResolver.addStaticVariable("myUtil", new StringUtils());
}

使用 StringUtils 如下:

1
2
3
4
5
public class StringUtils {
    public static String print() {
        return"Printed";
    }
}

还有观点:

1
2
Test
Test

两者都工作得很好。如果您的方法不是静态的,后者也可以工作。

希望对你有帮助。


我设法通过package EngineContextFactory 类来做到这一点,所以我在我的 Thymeleaf 配置类中如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@Bean
public SpringTemplateEngine springTemplateEngine(SpringResourceTemplateResolver templateResolver,
                                                                 IDialect springSecurityDialect)
{
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    templateEngine.setEngineContextFactory(engineContextFactory());
    templateEngine.addDialect(springSecurityDialect);
    return templateEngine;
}

private IEngineContextFactory engineContextFactory()
    {
        return new EngineContextFactoryWrapper()
            // packages to register
            .registerImport("java.util")
            .registerImport("java.math")
            .registerImport("com.mainsys.fhome.gui.util");
    }

public static class EngineContextFactoryWrapper
    implements IEngineContextFactory
{
    private final IEngineContextFactory delegate;
    private final List<String> typeLocatorPrefixes;

    public EngineContextFactoryWrapper()
    {
        super();
        delegate = new StandardEngineContextFactory();
        typeLocatorPrefixes = new ArrayList<String>();
    }

    @Override
    public IEngineContext createEngineContext(IEngineConfiguration configuration,
                                                            TemplateData templateData,
                                                            Map<String, Object> templateResolutionAttributes,
                                                            IContext context)
    {
        IEngineContext engineCtx = delegate.createEngineContext(configuration, templateData, templateResolutionAttributes, context);
        EvaluationContext evaluationContext;
        if (engineCtx.containsVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME))
        {
            evaluationContext = (EvaluationContext) engineCtx.getVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME);
        }
        else
        {
            evaluationContext = new ThymeleafEvaluationContextWrapper(new StandardEvaluationContext());
        }
        for (String prefix : typeLocatorPrefixes)
        {
            ((StandardTypeLocator) evaluationContext.getTypeLocator()).registerImport(prefix);
        }
        return engineCtx;
    }

    public EngineContextFactoryWrapper registerImport(String prefix)
    {
        typeLocatorPrefixes.add(prefix);
        return this;
    }
}