Spring boot 在嵌入式Tomcat中使用JSP标记Libs进行Spring引导

Spring boot 在嵌入式Tomcat中使用JSP标记Libs进行Spring引导,spring-boot,freemarker,jsp-tags,embedded-tomcat-8,Spring Boot,Freemarker,Jsp Tags,Embedded Tomcat 8,我目前正在迁移一个SpringMVCwebapp(通过SpringBoot将xml配置迁移到java配置,将tomcat迁移到嵌入式tomcat) webapp使用freemarker作为模板引擎和JSP标记库。现在,当我调用freemarker页面时,会出现以下错误: freemarker.ext.jsp.TaglibFactory$TaglibGettingException: No TLD was found for the "http://www.springframework.org

我目前正在迁移一个SpringMVCwebapp(通过SpringBoot将xml配置迁移到java配置,将tomcat迁移到嵌入式tomcat)

webapp使用freemarker作为模板引擎和JSP标记库。现在,当我调用freemarker页面时,会出现以下错误:

freemarker.ext.jsp.TaglibFactory$TaglibGettingException: 
No TLD was found for the "http://www.springframework.org/tags/form" JSP taglib URI. (TLD-s are searched according the JSP 2.2 specification. In development- and embedded-servlet-container setups you may also need the "MetaInfTldSources" and "ClasspathTlds" freemarker.ext.servlet.FreemarkerServlet init-params or the similar system properites.)
freemarker-header.ftl以以下代码段开头:

<#assign form=JspTaglibs["http://www.springframework.org/tags/form"]>
<#assign core=JspTaglibs["http://java.sun.com/jstl/core"]>
<#assign spring=JspTaglibs["http://www.springframework.org/tags"]>
<#assign osc=JspTaglibs["/WEB-INF/osc.tld"]>

我没有找到MetaInfTldSources和ClassPathTLD的任何可用搜索结果。以前有人解决过这个问题吗

韩元
Habib

Spring Boot不支持使用带有现成Freemarker的JSP标记库。有一个你可能感兴趣的问题。它包含一个指向可能的解决方案的链接,在该解决方案中,您可以配置
freemarkerconfiguer
的标记库工厂,并从类路径加载一些额外的TLD:

freeMarkerConfigurer.getTaglibFactory().setClasspathTlds(…);

这真的应该是内置的

首先,在
应用程序上禁用内置的
freemakerautoconfiguration

@SpringBootApplication
@EnableAutoConfiguration(exclude = {FreeMarkerAutoConfiguration.class})
public class Application extends WebMvcConfigurerAdapter {
    ...
]
然后添加此自定义配置:

(改编自;向
TaglibFactory
添加了
ObjectWrapper
,并删除了
addResourceHandlers()
覆盖)

然后,您可以在模板中加载:

<#assign s=JspTaglibs["/META-INF/spring.tld"] />

<a href="${s.mvcUrl("IC#index").build()}">Home</a>

在呈现模板时,freemarker调用TaglibFactory,它通过四种方式搜索TLD:

  • addTldLocationsFromClasspathTlds
  • addTldLocationsFromWebXml
  • addtldLocationsfromebinftlds
  • addTldLocationsFromMetaInfTlds
  • 所有这些方法都在freemarker jar的TablibFactory类中。最后,扫描WEB-INF/lib中的每个jar,搜索/META-INF/***.tld。如果启用freemarker的调试模式,则可以看到此日志记录

    看看您的项目是如何部署的。在我的例子中,使用eclipse、wtp、tomcat和maven,maven依赖项在eclipse/Deployment assembly中配置为maven依赖项,当然:),因此这些lib不在WEB-INF/lib中,因此,
    addtllocationsfrommetainftld
    找不到


    一种解决方法是强制部署将所有maven依赖项复制到WEB-INF/lib。我在eclipse的“服务器”视图中打开了服务器配置,在“服务器选项”下取消选中所有复选框,但“默认情况下自动重新加载模块”。如果您知道如何操作,这实际上是一项简单的任务。您所需要的一切都已经嵌入到FreeMarker中,例如它是
    TaglibFactory.ClasspathMetaInfTldSource
    class。我花了几个小时调查这个问题,所以我想分享一个解决方案

    我将其实现为
    BeanPostProcessor
    ,因为在
    FreeMarkerConfigure
    bean初始化之前,现在无法设置
    TaglibFactory

    import freemarker.ext.jsp.TaglibFactory;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
    
    import java.util.Arrays;
    import java.util.regex.Pattern;
    
    /**
     * A {@link BeanPostProcessor} that enhances {@link FreeMarkerConfigurer} bean, adding
     * {@link freemarker.ext.jsp.TaglibFactory.ClasspathMetaInfTldSource} to {@code metaInfTldSources}
     * of {@link TaglibFactory}, containing in corresponding {@link FreeMarkerConfigurer} bean.
     *
     * <p>
     * This allows JSP Taglibs ({@code *.tld} files) to be found in classpath ({@code /META-INF/*.tld}) in opposition
     * to default FreeMarker behaviour, where it searches them only in ServletContext, which doesn't work
     * when we run in embedded servlet container like {@code tomcat-embed}.
     *
     * @author Ruslan Stelmachenko
     * @since 20.02.2019
     */
    @Component
    public class JspTagLibsFreeMarkerConfigurerBeanPostProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof FreeMarkerConfigurer) {
                FreeMarkerConfigurer freeMarkerConfigurer = (FreeMarkerConfigurer) bean;
                TaglibFactory taglibFactory = freeMarkerConfigurer.getTaglibFactory();
    
                TaglibFactory.ClasspathMetaInfTldSource classpathMetaInfTldSource =
                        new TaglibFactory.ClasspathMetaInfTldSource(Pattern.compile(".*"));
    
                taglibFactory.setMetaInfTldSources(Arrays.asList(classpathMetaInfTldSource));
    //            taglibFactory.setClasspathTlds(Arrays.asList("/META-INF/tld/common.tld"));
            }
            return bean;
        }
    }
    
    要使自定义标记(“”)起作用,它必须是
    src/main/resources/META-INF/some.tld中的
    *.tld
    文件,并且必须包含
    xml标记,如
    http://my-custom-tag-library/tags
    。它将由FreeMarker找到

    我希望它能帮助人们节省几个小时来找到这个问题的“正确”解决方案


    使用spring boot v2.0.5进行测试。发布版

    这些解决方案对我都不起作用,但在分析中的解决方法后,我找到了一个确实有效的解决方案:

    1-在pom.xml中添加以下内容

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
        </dependency>
    
    3-现在您可以加载ftl文件,例如安全LIB

    <#assign spring = JspTaglibs["http://www.springframework.org/security/tags"]>
    
    
    

    我希望这对其他人有用

    嗨,我试过了,但没用。。。当前的解决方法是将TLD从JAR放入webapp/META-INF文件夹。但这仅在使用springboot:run命令启动应用程序时有效。通过IntelliJ中的标准主应用程序类运行应用程序会导致相同的结果,即应用程序找不到tld文件…:-(回答得很好,完全解决了这个问题。我个人正试图将spring安全支持添加到我的freemarker文件中,在添加这个CustomFreeMarker配置之后,我所要做的就是完成这项工作。
    <#assign common = JspTaglibs["http://my-custom-tag-library/tags"]>
    <#assign security = JspTaglibs["http://www.springframework.org/security/tags"]>
    <#assign form = JspTaglibs["http://www.springframework.org/tags/form"]>
    <#assign spring = JspTaglibs["http://www.springframework.org/tags"]>
    
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
        </dependency>
    
    import org.apache.commons.lang3.ArrayUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
    
    import javax.annotation.PostConstruct;
    import java.util.Arrays;
    import java.util.List;
    
    public class ClassPathTldsLoader  {
    
        private static final String SECURITY_TLD = "/META-INF/security.tld";
    
        final private List<String> classPathTlds;
    
        public ClassPathTldsLoader(String... classPathTlds) {
            super();
            if(ArrayUtils.isEmpty(classPathTlds)){
                this.classPathTlds = Arrays.asList(SECURITY_TLD);
            }else{
                this.classPathTlds = Arrays.asList(classPathTlds);
            }
        }
    
        @Autowired
        private FreeMarkerConfigurer freeMarkerConfigurer;
    
        @PostConstruct
        public void loadClassPathTlds() {
            freeMarkerConfigurer.getTaglibFactory().setClasspathTlds(classPathTlds);
        }
    
    
    }
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FreemarkerTaglibsConfig {
    
    
        @Bean
        @ConditionalOnMissingBean(ClassPathTldsLoader.class)
        public ClassPathTldsLoader classPathTldsLoader(){
            return new ClassPathTldsLoader();
        }
    
    }
    
    <#assign spring = JspTaglibs["http://www.springframework.org/security/tags"]>