在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串

Posted

技术标签:

【中文标题】在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串【英文标题】:Process HTML file using Thymeleaf in Web based Scopes of Spring and store the processed template as String 【发布时间】:2018-09-29 14:12:32 【问题描述】:

我正在尝试使用 thymeleaf 呈现 html 文件,并将生成的 HTML 内容保存在 web-based scopes of Spring 的 String 变量中,以便以后可以使用它来发送电子邮件或将内容转换为 pdf。我已经完成了this 网站中给出的实现,但它给了我我无法弄清楚的错误。我正在使用带有 thymeleaf 3.0.9 n thymeleaf dialect 2.2.2 的 spring boot 1.5.12.RELEASE。下面是我的实现。请帮忙。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

@Controller
public class jataController 

    @GetMapping(value = "/manual-thym")
    @ResponseBody
    public void justSample() 
        Context context = new Context();
        String filename = "templates/view/failure";
        String html = renderHtml(filename, context);
        System.out.println("template\n" + html);
    

    private String renderHtml(String filename, Context context) 

        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();

        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCacheable(false);
        templateResolver.setOrder(1);
        templateResolver.setCharacterEncoding("UTF-8");

        TemplateEngine templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);

        String html = templateEngine.process(filename, context);

        return html;
    

还有 StackTrace:

2018-04-19 12:26:43.742 ERROR 6375 --- [nio-9000-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: ognl/PropertyAccessor] with root cause

java.lang.ClassNotFoundException: ognl.PropertyAccessor
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_162]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_162]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338) ~[na:1.8.0_162]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_162]
    at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_162]
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[na:1.8.0_162]
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[na:1.8.0_162]
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) ~[na:1.8.0_162]
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73) ~[na:1.8.0_162]
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368) ~[na:1.8.0_162]
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362) ~[na:1.8.0_162]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_162]
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361) ~[na:1.8.0_162]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_162]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338) ~[na:1.8.0_162]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_162]
    at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.<init>(OGNLVariableExpressionEvaluator.java:83) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.standard.StandardDialect.getVariableExpressionEvaluator(StandardDialect.java:179) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.standard.StandardDialect.getExecutionAttributes(StandardDialect.java:393) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.DialectSetConfiguration.build(DialectSetConfiguration.java:263) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.EngineConfiguration.<init>(EngineConfiguration.java:123) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.TemplateEngine.initialize(TemplateEngine.java:336) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1079) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1059) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1048) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
    at com.siqes.flight.controller.jataController.renderHtml(jataController.java:37) ~[classes/:na]
    at com.siqes.flight.controller.jataController.justSample(jataController.java:19) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_162]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_162]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_162]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_162]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) ~[spring-boot-1.5.12.RELEASE.jar:1.5.12.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:111) ~[spring-boot-actuator-1.5.12.RELEASE.jar:1.5.12.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) ~[spring-boot-actuator-1.5.12.RELEASE.jar:1.5.12.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.16.RELEASE.jar:4.3.16.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:613) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_162]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_162]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.29.jar:8.5.29]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_162]

【问题讨论】:

【参考方案1】:

如果TemplateEnginesingleton bean 中自动装配,那么下面提到的代码就完美了。

@Controller
public class jataController 

    @Autowired
    private TemplateEngine templateEngine;

    @GetMapping(value = "/manual-thym")
    @ResponseBody
    public void justSample() 
        Context context = new Context();
        String filename = "templates/view/generated-ticket.html";
        String html = renderHtml(filename, context);
        System.out.println("template\n" + html);
    

    private String renderHtml(String filename, Context context) 

        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCacheable(false);
        templateResolver.setOrder(1);
        templateResolver.setCharacterEncoding("UTF-8");

        templateEngine.setTemplateResolver(templateResolver);

        String html = templateEngine.process(filename, context);

        return html;
    

但是如果 TemplateEngine 自动装配在 request 范围 bean 类型上,它会给出异常,thymeleaf 会造成内存泄漏。所以最后经过大量的尝试和尝试,我得到了一个可行的解决方案,感谢@Paizo。它可能包含一些不好的做法,但这就是它的工作原理:

@Controller
@Configuration
@EnableWebMvc
@ApplicationScope
public class MyThymeleafConfig 

    @GetMapping("/view-template")
    @ResponseBody
    public void viewTemplates() 

        Context context = new Context();
        context.setVariable("mydata", "this is it");

        String html = templateEngine().process("templates/view-to-process.html", context);
        System.out.println(html);
    


    /*

    configuration for thymeleaf and template processing

    */

    @Bean
    public SpringTemplateEngine templateEngine() 
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(thymeleafTemplateResolver());
        return templateEngine;
    

    @Bean
    public SpringResourceTemplateResolver thymeleafTemplateResolver() 
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setPrefix("classpath:");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheable(false);
        templateResolver.setTemplateMode(TemplateMode.HTML);
        return templateResolver;
    

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() 
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        return viewResolver;
    

为了提供静态资源,我们需要定义另一个bean,如下:

@Configuration
@EnableWebMvc
public class StaticResourceConfig implements WebMvcConfigurer 

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) 
        registry
                .addResourceHandler("/**")
                .addResourceLocations("/static/", "classpath:static/");
    

在任何论坛上都找不到此解决方案。但是,我会要求任何Spring 开发人员为上述代码提供更好的实现

【讨论】:

因为它使用标准配置的默认@Bean,在你创建一个没有配置的新实例之前。另外,您为什么要手动转换模板并将其返回到控制器中? 我想用 thymeleaf 处理 html 页面,然后将其存储在字符串变量中,以便我可以将其用于 html 到 pdf 的转换。 这意味着自动装配采用spring默认bean的默认配置。 为什么不将内容保存在 Redis 中? 我应该在redis中坚持什么【参考方案2】:

见下方更新

当您配置 Thymeleaf 时,您应该定义模板引擎和模板解析器,否则当您使用自动装配默认值时。如果您每次都创建一个实例,这不是一个好习惯。这里是一个示例配置:

@Configuration
@EnableWebMvc
public class ThymeleafConfiguration 

    @Bean
    public SpringTemplateEngine templateEngine() 
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(thymeleafTemplateResolver());
        return templateEngine;
    

    @Bean
    public SpringResourceTemplateResolver thymeleafTemplateResolver() 
        SpringResourceTemplateResolver templateResolver 
          = new SpringResourceTemplateResolver();
        templateResolver.setPrefix("/WEB-INF/views/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode("HTML5");
        return templateResolver;
    

然后,如果您想以编程方式进行试验,您可以自动装配它们,但使用 thymeleaf 提供 html 页面的通常流程也是定义视图解析器:

@Bean
public ThymeleafViewResolver thymeleafViewResolver() 
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setTemplateEngine(templateEngine());
    return viewResolver;

一切就绪后,您可以将控制器编写为:

@Controller
public class MyController 

    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public String test() 
        return "yourTemplateName";
    

可以使用模型属性将参数传递给模板。

2018 年 7 月 31 日更新

不幸的是,我没有时间完成工作概念验证,但是我认为下面的代码足以显​​示流程。如果您运行它并调用localhost:8080/test,您应该能够在控制台中看到输出html。 pdf 生成可以作为视图解析器添加和/或以编程方式调用,在此示例中使用 xhtmlrenderer;我没有时间完成它,所以我把它注释掉了,但你可以明白这一点:一个服务通过自动装配模板引擎来提供 html 和 pdf 生成。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.paizo</groupId>
    <artifactId>html2pdf</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>html2pdf</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf</artifactId>
            <version>9.1.14</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

ThymeleafConfiguration.java

@Configuration
public class ThymeleafConfiguration 

    @Bean
    public SpringTemplateEngine templateEngine() 
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(thymeleafTemplateResolver());
        return templateEngine;
    

    @Bean
    public ClassLoaderTemplateResolver thymeleafTemplateResolver() 
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setPrefix("templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode("HTML5");
        return templateResolver;
    

HelloWorldController.java

@Controller
public class HelloWorldController 

    @Autowired
    private Html2PdfService pdfService;

    @GetMapping(path = "/test")
    public String hello() 
        Map parameters = new HashMap();
        parameters.put("name", "Borat");
        System.out.println(pdfService.template2Html("test", parameters));
        return "test";
    

//    @ResponseBody
//    @GetMapping
//    public ResponseEntity helloPdf() 
//        Map parameters = new HashMap();
//        parameters.put("name", "Borat");
//        pdfService.template2Pdf("test", parameters);
//        String filePath = "PATH_HERE";
//        InputStream inputStream = new FileInputStream(new File(filePath));
//        InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentLength();
//        return new ResponseEntity(inputStreamResource, headers, HttpStatus.OK);
//    

Html2PdfService.java

@Service
public class Html2PdfService 

    @Autowired
    private TemplateEngine templateEngine;

    public OutputStream template2Pdf(String templateName, Map parameters) 
//        OutputStream outputStream = new BufferedOutputStream();
//        IOUtils.copy()
//
//        Context ctx = new Context();
//        String processedHtml = templateEngine.process(templateName, ctx);
//        ITextRenderer renderer = new ITextRenderer();
//        renderer.setDocumentFromString(processedHtml);
//        renderer.layout();
//        renderer.createPDF(os, false);
//        renderer.finishPDF();
        return null;
    

    public String template2Html(String templateName, Map parameters) 
        Context ctx = new Context();
        ctx.setVariable("name", "pippo");
        String processedHtml = templateEngine.process(templateName, ctx);
        return processedHtml;
    

Html2pdfApplication.java

@SpringBootApplication
public class Html2pdfApplication 

    public static void main(String[] args) 
        SpringApplication.run(Html2pdfApplication.class, args);
    

作为旁注,如果您计划动态生成 pdf 并将其作为控制器中的响应,我建议使用流而不是字节数组或临时文件。

【讨论】:

您好@Paizo,您想查看我的答案,以便我可以给您赏金 嗨@SAM我刚刚用一个工作示例更新了答案,对于html部分来说更干净,而不是在你的代码上点对点;我不确定您提到的泄漏,它可能与控制器类中定义的所有注释的混合有关 是的,我使用 ByteArrayInputStream 避免临时文件。 感谢您抽出宝贵的时间。我能够处理模板并使用 itext 生成 pdf。我相信飞碟也使用itext。但问题在于 bean 的范围。如果我切换到请求、会话、globalSession 等基于 Web 的范围,我会遇到异常。但是使用 thymeleaf 配置描述的 beans 进行模板处理在应用程序范围内没有任何问题。当用配置和控制器注释一个类时,我觉得这是一种不好的做法。关于如何重构我的代码的任何解决方案。 是的,看我的:D 提供@Service 的功能(默认为单例),不要手动创建ClassLoaderTemplateResolver 的实例,只需在@Configuration 类中定义bean 并自动装配它们在服务中,无论请求如何,都应该为您提供 1 个 bean 实例。不要混合多个类注解,@EnableWebMvc 不需要。该服务为您提供了一个抽象层,使您可以轻松更改实现。如果你可以直接从 itext->service->controller 传递 pdf 流,性能和内存会更好【参考方案3】:

我正在使用类似的 Springboot 和 Thymeleaf 版本,这样的东西在一些项目中对我有用:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

@Component
public class EmailProcessor 

    private TemplateEngine htmlTemplateEngine;

    @Autowired
    public EmailProcessor(TemplateEngine templateEngine) 
        this.htmlTemplateEngine = templateEngine;
    


    public String process(User user) 

        final Context ctx = new Context();

        if (user != null) 
            ctx.setVariable("user", user);
        

        return htmlTemplateEngine.process("emails/template", ctx);
    

电子邮件模板只是一个常规的 Thymeleaf 模板,位于:

资源/模板/电子邮件/template.html

【讨论】:

当 bean 类型为“单例”时,这也对我有用。但是当 bean 类型是“请求”时,它会给你错误。试试看。【参考方案4】:

使用 Maven,将 ognl 依赖项添加到您的 pom.xml 文件中,例如(在MVNRepository 中找到最新的依赖项/版本):

<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>3.1.12</version>
</dependency>

【讨论】:

【参考方案5】:

就我而言,我的 html 模板不在正确的目录中。将其更改为“src/main/resources/abc.html”,这修复了我的错误:

java.lang.ClassNotFoundException: ognl.PropertyAccessor

【讨论】:

以上是关于在基于 Web 的 Spring 范围中使用 Thymeleaf 处理 HTML 文件并将处理后的模板存储为字符串的主要内容,如果未能解决你的问题,请参考以下文章

spring只适用于web项目么

Spring基于范围的默认配置文件选择

Spring oAuth2 中基于用户的权限/范围

springboot thymeleaf引入css和js必须添加th吗

在基于 Spring 的 Web 应用程序中处理会话过期事件

Web 应用程序上下文层次结构中的 Spring bean 范围